This example deploys a CoderControlPlane backed by a PostgreSQL cluster managed by CloudNativePG.
It creates:
- a
codernamespace - a CloudNativePG
Clusternamedcoder-db - a
CoderControlPlanenamedcoderwired to the CloudNativePG app Secret (coder-db-app)
- A Kubernetes cluster
kubectlconfigured for that clusterhelm
helm repo add cnpg https://cloudnative-pg.github.io/charts
helm repo update
helm upgrade --install cnpg cnpg/cloudnative-pg \
--namespace cnpg-system \
--create-namespaceFollow Deploy the controller (in-cluster), or run:
kubectl create namespace coder-system
kubectl apply -f config/crd/bases/
kubectl apply -f config/rbac/
kubectl apply -f deploy/deployment.yaml
kubectl rollout status deployment/coder-k8s -n coder-systemdeploy/deployment.yaml defaults to --app=all, which runs the controller, aggregated API server, and MCP server in a single pod. For split deployments, you can set --app=controller, --app=aggregated-apiserver, or --app=mcp-http in the Deployment args.
kubectl apply -f examples/cloudnativepg/The namespace manifest is prefixed (00-namespace.yaml) so kubectl apply -f creates coder before namespaced resources.
These manifests also include Argo CD sync-wave annotations so the same directory works with examples/argocd/: namespace (wave 0) -> CloudNativePG Cluster (wave 1) -> CoderControlPlane (wave 2).
Wait for PostgreSQL and verify the generated Secret:
kubectl -n coder wait --for=condition=Ready cluster/coder-db --timeout=10m
kubectl -n coder get secret coder-db-appWait for the CoderControlPlane deployment:
kubectl -n coder rollout status deployment/coder --timeout=10m
kubectl -n coder get codercontrolplane coderIn one terminal:
kubectl -n coder port-forward svc/coder 3000:80Then open:
Use the setup flow to create the first admin user.
This example sets:
CODER_ACCESS_URL=http://localhost:3000
That value is convenient for UI smoke tests through kubectl port-forward, but it is not suitable for end-to-end workspace connectivity because in-cluster components cannot reach your local localhost.
For a full setup, expose Coder with an ingress/load balancer and set CODER_ACCESS_URL to a real external URL (for example, https://coder.example.com).