Skip to content

Kubernetes

Plain manifests for the whole stack, one namespace (ovc-infra) and ReadWriteOnce PVCs for persistent data.

ovc-frontend and ovc-backend are built by GitHub Actions and published to GHCR (guacd is the upstream guacamole/guacd:1.6.0):

Component Image
ovc-frontend ghcr.io/openvcenter/ovc-frontend:latest
ovc-backend ghcr.io/openvcenter/ovc-backend:latest

ovc-backend’s worker Deployment reuses the same ovc-backend image with command: python -m app.worker instead of uvicorn. ovc-agent-hyperv isn’t distributed as a container - it’s a Windows service binary uploaded through the frontend’s Agent Management dialog (stored by the backend) and installed directly on each Hyper-V host.

PostgreSQL, RabbitMQ and Valkey, each with its own PVC:

  • PGDATA is set one level below the mount point so initdb never trips over a non-empty directory (lost+found, etc).
  • RABBITMQ_NODENAME pins the Erlang node name so a pod restart keeps the same Mnesia database.
  • guacd (no PVC) is its own Deployment behind a ClusterIP Service, see ovc-backend and ovc-frontend below.

Per the project convention, container env vars - passwords included - live in ConfigMaps. For a production deployment, move the passwords into Secrets and switch envFrom.configMapRef to secretRef.

ovc-backend and ovc-frontend default to real OIDC login against a provider - any works (Keycloak, Auth0, Okta, Entra ID). The user’s groups are read from a configurable claim (resource_access.<client_id>.roles by default) - see Access control below.

The provider has two addresses, and each service needs to know both:

Setting ovc-frontend ovc-backend
Issuer, as the browser reaches it (it ends up in the token’s iss) OIDC_ISSUER OVC_OIDC_ISSUER
The same provider, as the server reaches it OIDC_INTERNAL_URL (issuer URL) OVC_OIDC_JWKS_URL (JWKS URL)

In containers they differ: the browser uses e.g. http://keycloak.example.com, while the pods use the Service name. ovc-frontend fetches only the discovery document from OIDC_INTERNAL_URL, so the provider must answer with a browser-facing authorization_endpoint and server-reachable token, userinfo and JWKS endpoints. With Keycloak, set KC_HOSTNAME to its public URL and KC_HOSTNAME_BACKCHANNEL_DYNAMIC=true. Without OIDC_INTERNAL_URL the frontend uses OIDC_ISSUER for everything, which is right when the server can reach the public URL too.

If the provider is unreachable when ovc-frontend starts, the login screen says so and the frontend retries discovery every few seconds - no restart needed once the provider is up.

No IdP, lab/demo deployment: set OVC_AUTH_MODE: stub in both ovc-backend’s and ovc-frontend’s config instead.

Access is granted to groups, never to individual users. The backend reads the user’s groups from one token claim, OVC_OIDC_GROUPS_CLAIM (a dot path; default resource_access.${client_id}.roles, i.e. Keycloak client roles). Set the frontend’s OIDC_GROUPS_CLAIM to the same path. For another provider, point both at its groups claim, e.g. groups or roles for Entra ID.

Group Access
ADMINISTRATOR Every privilege on every object, with no setup needed. Configurable via OVC_ADMIN_GROUP on the backend.
(any other group) Logs in. Sees and does only what the permissions granted to its groups allow; none means an empty tree.
(no group at all) Redirected to /access-denied right after login.

Permissions are managed in the UI: Administration ▸ Roles defines roles (named sets of privileges such as power, console or snapshots), and each cluster, host, folder and VM has a Permissions tab that grants a role to a group on that object. A permission propagates to the objects below it unless told not to; the built-in No access role blocks a branch.

For a first deployment, put at least one user in ADMINISTRATOR so someone can log in, add hosts and grant permissions to the other groups.

The API and the worker are two Deployments sharing one image and ConfigMap. OVC_AGENT_STORAGE=local needs its own PVC (mounted only on the API pod) so uploaded ovc-agent binaries survive a redeploy.

All of ovc-frontend’s config is runtime (changeable without a rebuild): API_URL, GUACD_URL, OIDC_* and BETTER_AUTH_* are read from the environment at runtime.

ovc-guacd is a plain Deployment behind the ovc-guacd-service ClusterIP Service - the same shape as the Docker Compose setup. The frontend’s own server serves the consoles’ Guacamole tunnel (/webrdp/tunnel) and talks to guacd over the cluster-internal DNS (GUACD_URL: ovc-guacd-service:4822); the browser never connects to guacd, so it needs no NodePort. The console tunnels live in the frontend’s memory, so keep one frontend replica or use sticky sessions.

See quick-install/kubernetes for the actual manifests, NodePorts, optional Ingress and apply.sh / delete.sh. apply.sh asks for a base domain once (default openvcenter.local; the hosts become frontend.<domain>, keycloak.<domain>, rabbitmq-management.<domain> and, for the agents, rabbitmq.<domain>), applies the manifests with it, and finishes with a sign-in check from inside the frontend pod.

Once ovc-frontend is reachable, open it to add a host and install its agent - see Running in dev mode › Agent for the Add Host / Setup Agent steps.