Deploy the controller¶
Run coder-k8s as an operator only (--app=controller). The operator reconciles CoderControlPlane, CoderProvisioner, and CoderWorkspaceProxy resources.
Run the commands from a clone of this repository.
1. Install CRDs and RBAC¶
kubectl create namespace coder-system
kubectl apply -f config/crd/bases/ -f config/rbac/
2. Deploy in controller-only mode¶
The default of deploy/deployment.yaml is --app=all. Change it to the controller:
kubectl apply -f deploy/deployment.yaml
kubectl -n coder-system patch deployment/coder-k8s --type=json \
-p='[{"op":"add","path":"/spec/template/spec/containers/0/args","value":["--app=controller"]}]'
Pin the image
The manifest uses ghcr.io/coder/coder-k8s:latest. To pin a version, change the tag before you apply the manifest.
3. Verify¶
kubectl rollout status deployment/coder-k8s -n coder-system
kubectl logs -n coder-system deploy/coder-k8s
Optional smoke test:
kubectl create namespace coder
kubectl apply -f config/samples/coder_v1alpha1_codercontrolplane.yaml
kubectl get codercontrolplanes -A
Run all components instead¶
To keep --app=all (the operator and the aggregated API server), do not do the kubectl patch step. Then register the aggregated API:
kubectl apply -f deploy/apiserver-service.yaml -f deploy/apiserver-apiservice.yaml
Connect an external PostgreSQL database¶
Coder requires a PostgreSQL connection URL.
- Put the URL in a Secret in the same namespace as the
CoderControlPlane. - Set
spec.database.connectionSecretRefto the name and key of that Secret:
apiVersion: coder.com/v1alpha1
kind: CoderControlPlane
metadata:
name: coder
namespace: coder
spec:
database:
connectionSecretRef:
name: coder-db-app
key: uri
The value must be a postgres:// or postgresql:// URL. The controller then does two things:
- It sets
CODER_PG_CONNECTION_URLin the Coder container withvalueFrom.secretKeyRef. The Deployment holds a reference to the Secret, not a copy of the URL. - It reads the URL from the Secret to create the
coder-k8s-operatoruser and API token (operator access bootstrap).
Rules:
- You must set both
nameandkey. - Do not also set
CODER_PG_CONNECTION_URLinspec.extraEnv. The API server rejects that combination. - Do not set this variable in
spec.envFrom. The controller does not examinespec.envFromfor this variable.
If you do not set spec.database, the controller keeps the earlier behavior. You set CODER_PG_CONNECTION_URL in spec.extraEnv, as a literal value or with valueFrom.secretKeyRef.
Check the DatabaseSecretResolved condition¶
While spec.database has a value, the controller reports the DatabaseSecretResolved condition. The controller does a new check of the condition when the Secret is created, updated, or deleted. When you remove spec.database, the controller removes the condition.
| Status | Reason | Meaning |
|---|---|---|
True |
Resolved |
The Secret key contains a postgres:// or postgresql:// URL. |
False |
SecretNotFound |
The Secret does not exist in the namespace. |
False |
KeyNotFound |
The Secret exists but does not contain the key. |
False |
EmptyValue |
The value is empty or contains only whitespace. |
False |
InvalidURL |
The value is not a valid postgres:// or postgresql:// URL. The scheme must be lowercase. For example, the controller rejects Postgres://. |
False |
ConflictingConfiguration |
spec.extraEnv also sets CODER_PG_CONNECTION_URL. The controller does not change the Deployment. |
Resolved does not mean that the database is healthy or that the network can reach it. The controller does not connect to PostgreSQL to set this condition. The condition messages contain the names of the Secret and the key, but never the URL or credentials.
kubectl -n coder get codercontrolplane coder \
-o jsonpath='{range .status.conditions[?(@.type=="DatabaseSecretResolved")]}{.status} {.reason}: {.message}{"\n"}{end}'
You can create the CoderControlPlane before its Secret. Until the Secret exists:
- The condition reason is
SecretNotFound. - The Coder pod cannot start.
- The operator access bootstrap waits.
After you create the Secret, the controller reconciles again and Kubernetes starts the pod.
Rotate database credentials¶
When a Secret changes, Kubernetes does not update the environment variables in running pods. After you change the connection URL in the Secret (for example, to set a new password), do these steps:
- Make sure that the condition reason is
Resolved. -
Restart the Coder Deployment, so that the new pods read the new value:
kubectl -n coder rollout restart deployment/coder kubectl -n coder rollout status deployment/coder
The controller does not restart pods automatically. The operator access bootstrap reads the Secret again on each reconcile. Thus it uses the new URL without a restart.