Kubernetes
Plain manifests for the whole stack, one namespace (ovc-infra) and
ReadWriteOnce PVCs for persistent data.
Ready to deploy?
quick-install/kubernetesis a hardened, ready-to-apply version of everything below - Keycloak’s realm auto-imported, the session secret generated, NodePorts pinned and an Ingress included. This page explains the why behind that design; the linked README has the actual manifests, ports andapply.sh.
Container images
ovc-frontend, ovc-backend and ovc-webrdp are built by GitHub Actions and
published to GHCR:
| Component | Image |
|---|---|
ovc-frontend |
ghcr.io/claudio-azevedo/ovc-frontend:latest |
ovc-backend |
ghcr.io/claudio-azevedo/ovc-backend:latest |
ovc-webrdp |
ghcr.io/claudio-azevedo/ovc-webrdp: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.
Backing services
PostgreSQL, RabbitMQ and Valkey, each with its own PVC:
PGDATAis set one level below the mount point so initdb never trips over a non-empty directory (lost+found, etc).RABBITMQ_NODENAMEpins the Erlang node name so a pod restart keeps the same Mnesia database.guacdisn’t a backing service of its own - it runs as a sidecar in theovc-webrdpPod, see ovc-webrdp 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.
OIDC provider
ovc-backend and ovc-frontend default to real OIDC login against a
provider - any works (Keycloak, Auth0, Okta, Entra ID). Roles are read from a
configurable claim (resource_access.<client_id>.roles by default) - see
Roles below.
No IdP, lab/demo deployment: set OVC_AUTH_MODE: stub in both
ovc-backend’s and ovc-frontend’s config instead.
Roles
The app recognizes exactly one role by name:
| Role | Grants |
|---|---|
ADMINISTRATOR |
Full access to every cluster, host, folder and VM - no further setup needed. Configurable via OVC_ADMIN_ROLE on the backend. |
| (any other role) | Login only. Visibility is then controlled per-user by scope grants (global/cluster/host/folder) recorded on the backend - none yet means the user sees nothing until one is added. |
| (no role at all) | Redirected to /access-denied right after login. |
For a first deployment, assign ADMINISTRATOR to at least one user so
someone can log in and add hosts.
ovc-webrdp
ovc-guacd runs as a sidecar in the same Pod as ovc-webrdp, matching
ovc-webrdp’s own
deployment-app.yaml -
it reaches guacd over localhost (same network namespace), and nothing
outside the Pod needs to, so there’s no separate guacd Service.
ovc-backend and ovc-frontend
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, WEBRDP_ORIGIN, OIDC_* and BETTER_AUTH_* are read
from the environment on every request. WEBRDP_ORIGIN is the base URL of
ovc-webrdp (including its context path) - ovc-frontend’s own server
proxies the Console tab’s Guacamole tunnel to it, server-to-server, so the
browser never talks to ovc-webrdp directly.
Deploying
See quick-install/kubernetes
for the actual manifests, NodePorts, optional Ingress and apply.sh /
delete.sh.
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.