> For the complete documentation index, see [llms.txt](https://docs.visdom.virtuslab.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.visdom.virtuslab.com/visdom-orchestrator/user-guide/deploying-and-configuring-the-controller.md).

# Deploying and configuring the controller

The controller is a single Rust binary over Postgres. Everything else (Kubernetes, an identity provider, a registry) is optional depending on how much of the platform you need running, which is why local development is organised in **tiers**: from no agents at all, through running the runtime as a host process, up to a full local cluster with its own registry and identity provider.

## Requirements

* Rust 1.85+, and Docker for the TestContainers-based suite
* **Postgres 17+**: not negotiable; the schema uses `make_interval(secs => …)`
* Node 20+ and pnpm 9+ for the frontend

System endpoints are on the controller itself: `/_/health` for liveness, `/_/ready` for a database ping, `/_/metrics` for Prometheus, and `/_/openapi/ui` for a Swagger UI over the whole API.

## Configuration

All runtime configuration is environment variables prefixed `VP_`. The ones that decide how a deployment behaves:

| Variable                              | Purpose                                                                                    | Required |
| ------------------------------------- | ------------------------------------------------------------------------------------------ | -------- |
| `VP_DATABASE_URL`                     | Postgres DSN.                                                                              | Yes      |
| `VP_LISTEN_ADDR`                      | Bind address. Defaults to `0.0.0.0:8080`.                                                  | No       |
| `VP_BOOTSTRAP_ADMINS`                 | Comma-separated emails granted Admin at workspace scope on first sign-in.                  | No       |
| `VP_RUN_MIGRATIONS_ON_BOOT`           | Run migrations at startup. Local development only.                                         | No       |
| `VP_SECRETS_MASTER_KEY_V<N>`          | Base64 32-byte master keys for the secret store key ring.                                  | No       |
| `VP_K8S_IN_CLUSTER` / `VP_KUBECONFIG` | Enables the Kubernetes orchestrator. Without one of these, no pods are scheduled.          | No       |
| `VP_CALLBACK_URL`                     | URL that run pods post events to. Defaults to the controller's in-cluster DNS name.        | No       |
| `VP_POD_MTLS_SECRET`                  | Kubernetes secret holding the pod-side client TLS material. Defaults to `visdom-pod-mtls`. | No       |
| `VP_POD_TOKEN_SIGNING_KEY_HEX`        | Key the controller signs per-run pod tokens with.                                          | No       |
| `VP_OTEL_ENDPOINT`                    | OTLP collector. Without it, traces go to stdout as JSON.                                   | No       |

## Identity

**OIDC is supported natively.** Sign-in is a normal authorization-code flow against a configured provider, and the API uses session cookies rather than bearer tokens, which matters when scripting against it, because a script either signs in through the dev-auth route or reuses a session cookie copied from the browser.

**SAML is not implemented directly.** SAML deployments run a SAML-to-OIDC bridge (Keycloak, Dex or SimpleSAMLphp) and the platform sees OIDC. The `/api/v1/auth/saml/*` routes deliberately return 501 with a pointer to that runbook. If your environment mandates SAML, the answer is yes through a bridge you already run, not natively.

## Deployment paths

* **Helm**: a chart for the platform ships in the repository. This is the intended install for a client cluster.
* **GCP with Terraform**: GKE Autopilot, Cloud SQL, KMS, GCS and Secret Manager, wired up.
* **Local cluster**: kind or k3d with a local registry, covered by smoke runbooks. Enough to exercise real pods without a cloud account.

There is also an admin CLI (`vp-admin`) for operator tasks such as generating master keys, and a disaster-recovery runbook covering the database and key material.

{% hint style="danger" %}
**The local registry is the most common way a local cluster breaks.** Creating the cluster wires three separate things: the registry container, a network alias, and a containerd config telling the kubelet to use plain HTTP. Stopping and restarting the cluster, or recreating the registry on its own, breaks some of them, and the symptom is `Init:ErrImagePull` on every run, which looks like a platform fault rather than a networking one. The getting-started runbook has the three commands that repair it without recreating the cluster.
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.visdom.virtuslab.com/visdom-orchestrator/user-guide/deploying-and-configuring-the-controller.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
