feat: name workspace Deployment for Prometheus workload attribution - #861
Merged
Conversation
ppat
force-pushed
the
feat/workload-rename
branch
from
August 17, 2026 02:00
d6d7cd8 to
d3a2ea3
Compare
Name the workspace Deployment coder-workspace-<owner>-<workspace-name> instead of coder-<workspace-uuid>. The cluster's Prometheus already resolves pod -> ReplicaSet -> Deployment via a kube_pod_owner recording rule and exposes it as a workload label, so this is what workspace CPU/memory/PSI/OOM metrics get attributed to in Grafana/PromQL, at zero added series and no join. Owner is included because workspace names are unique per-owner, not cluster-wide. The coder-workspace- prefix (already used as app.kubernetes.io/part-of) distinguishes workspaces from both the coder control-plane Deployment and other coder-prefixed infra sharing the namespace (verified live: a coder-db-<date> CloudNativePG cluster exists today, so a bare coder- prefix is not sufficient). Reuses the existing hostname sanitization idiom instead of a new one, and documents the rationale in DESIGN.md/CLAUDE.md since the name is now load-bearing for observability, not just a Kubernetes identifier.
ppat
force-pushed
the
feat/workload-rename
branch
from
August 17, 2026 02:14
d3a2ea3 to
bcbb61b
Compare
Contributor
|
🎉 This PR is included in version 2.25.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this changes
Names the workspace
kubernetes_deployment_v1(deployment.tf)coder-workspace-<owner>-<workspace-name>instead ofcoder-<workspace-uuid>.local.workload_nameand its sanitization helpers live inmain.tf;deployment.tf's pre-existing hostname sanitization now reuses the same local instead of duplicating the regex.Why
cAdvisor's
container_*series carry no Kubernetes pod labels — they come from the cgroup filesystem, with no API-server connection, so they structurally can't. The cluster's Prometheus already has anamespace_workload_pod:kube_pod_owner:relabelrecording rule that resolves pod → ReplicaSet → Deployment and exposes aworkloadlabel — verified live, this already givesworkload="coder"for the control-plane pod andworkload="coder-<uuid>"for a workspace pod. That machinery already works; it only produces a UUID because that's what the Deployment is named. Renaming the Deployment makesworkloadhuman-readable at zero added Prometheus series and no PromQL join. An alternative (kube-state-metricsmetricLabelsAllowlist+group_left) was evaluated separately and rejected as more expensive/fragile for this purpose.Naming scheme and rationale
coder-workspace-<owner>-<workspace-name>, e.g. a workspace nameddevowned by a useralicebecomescoder-workspace-alice-dev.terraform apply(Deployment names must be unique within thecodernamespace). The owner mentioned possibly giving separate users to different AI agents later, each creating workspaces dynamically — cheap to fold in now, expensive to retrofit as a second migration later.coder-workspace-, not justcoder-. A barecoder-prefix isn't sufficient to unambiguously mean "workspace" in this namespace: verified live, thecodernamespace today also contains acoder-db-<date>-named CloudNativePG cluster (unrelated to workspaces) alongside thecodercontrol-plane Deployment itself.coder-workspace-matches theapp.kubernetes.io/part-of: coder-workspacevalue the template'scommon_labelsalready use, so it's an existing convention, not a new one — and it can't collide with the control-plane Deployment (exact-matchworkload="coder") or with othercoder-*infra sharing the namespace.lower(replace(x, "/[^a-zA-Z0-9]/", "-")), previously only used for the podhostname) rather than inventing a new one. In practice Coder's own server-side name validation (NameValidincoder/coder'scodersdk) already restricts workspace and owner names to^[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*$, ≤ 32 chars — solower()is the only transformation strictly required; thereplace()is defense-in-depth consistent with the pre-existing pattern.What references the Deployment name — checked across the whole template
deployment.tf's ownmetadata.nameandhostnamefield. Nothing else in the template (agent config,coder_env,coder_agentmetadata scripts, the ConfigMap, the release workflow) reads or constructs the Deployment name.kubernetes_config_map_v1.workspace_scriptsresource (configmap.tf) is namedinit-scripts-<workspace-uuid>— unrelated to this change (not in the pod→ReplicaSet→Deployment ownership chain Prometheus uses) and left as-is, out of scope.CODER_AGENT_TOKENbearer token (coder_agent.main.token,deployment.tf), not by any Kubernetes object name — this template has nocoder_app/exec-based access path that would depend on a parseable pod/Deployment name.Length limits — verified against Kubernetes source, not assumed
metadata.nameare validated asIsDNS1123Subdomain(253 chars) ink8s.io/apimachinery. At Coder's own per-field max (32-char owner + 32-char workspace name), the Deployment name is 81 chars and the ReplicaSet name (+ "-" + <10-char pod-template-hash>) is 92 — both comfortably under 253.GenerateName, which truncates its input to63 - 5 = 58chars before appending a random suffix (k8s.io/apiserver/pkg/storage/names). At the 81-char worst case, the Pod's own object name gets silently truncated and randomized past char 58. This is cosmetic only: theworkloadlabel is read from the Deployment object via the ownership chain, not parsed from the Pod's own name, so attribution is unaffected regardless. Realistic names in this homelab (short owner/workspace names) don't come close to the threshold anyway.Why the UUID was chosen, and why it no longer applies
No commit message or PR description spells this out directly (the UUID naming was present from the initial Docker→Kubernetes migration commit,
5c4c3a3/PR #361, with an empty PR body and no discussion), but the repo owner supplied the rationale from memory, and it's corroborated by what the history does show:templates/docker/homelab-workspace/volumes.tf, removed by the same migration commit): it defined a template-manageddocker_volumeresource namedcoder-${data.coder_workspace.me.id}-${each.key}— keyed on the immutable workspace ID, not the mutable workspace name — withlifecycle { ignore_changes = all }on top, i.e. exactly the "don't let a mutable input churn volume identity" mechanism described.PersistentVolumeClaimreferenced by a fixed, external name (claim_name = "coder-workspace-home") — not a resource this template creates or names itself. Checked the entire git history of this repo across all branches: nokubernetes_persistent_volume_claimresource has ever existed in it, in this file or any other. Checked live:coder-workspace-homein thecodernamespace is Flux-managed (kustomize.toolkit.fluxcd.iolabels/annotations), bound and provisioned entirely outside this template. So the PVC has been externally defined for the entire life of the Kubernetes template — since the same commit (5c4c3a3) that introduced the UUID-based Deployment name. The constraint that justified the UUID (Deployment name ↔ volume identity) had therefore already stopped applying by the time that commit landed; the UUID naming looks to have been carried forward from the Docker template's convention rather than re-derived against the new (already-external) storage model.kubernetes_persistent_volume_claimresource managed here again), this naming scheme — or any scheme deriving a resource identity from the mutable workspace name — would need revisiting for the same reason the original UUID choice existed.Rename behavior — unchanged in kind, not newly introduced
deployment.tfalready coupled to the workspace name before this change: thehomevolume'ssub_pathisdata.coder_workspace.me.name, so renaming a workspace already relocates (makes inaccessible from the new path) its home directory today. Making the Deployment name also track the workspace name doesn't add a new category of rename hazard — it's already priced into the existing design. A rename now additionally recreates the Deployment under the newcoder-workspace-<owner>-<new-name>name, which is consistent with the pod restarting anyway on anyapply.Live verification
workload="coder"for the control-plane pod,workload="coder-<uuid>"for workspace pods — thekube_pod_ownerrecording rule already resolves the Deployment name intoworkloadwith no gaps.codernamespace contains acoder-db-<date>-named non-workspace resource today, which is why a barecoder-prefix was rejected in favor ofcoder-workspace-.releaseworkflow,publish-templatejob) passed: it pushes this exact template against the real Coder deployment and Kubernetes API (redirected to a disposable test template perTESTING.md), confirming the new locals/resource are accepted by the live provider/schema — not justterraform validate.workload="coder-workspace-<owner>-<name>".kubernetes_deployment_v1.deployment'scountis gated onstart_count, and a template push (what dry-run does) doesn't start a workspace, so the Deployment resource itself is never actually applied during CI — only planned/schema-validated. Confirming the liveworkloadvalue requires eitherterraform apply(out of scope per instructions) or starting a real workspace from the merged template. What is verified: the naming expression by hand for realistic and worst-case (32/32-char) inputs, and the pre-change baseline (workload="coder-<uuid>"today, exact mechanism confirmed live).Existing workspaces
Confirmed acceptable (per repo owner): applying this recreates existing workspace Deployments (pods restart anyway on
apply; the PVC/home-volume data is unaffected since it's addressed by workspace name viasub_path, unchanged by this PR).Interaction with PR #859
PR #859 (
feat/memory-watchdog) is open and touchesdeployment.tf/configmap.tf/coder-agent.tf/etc., but itsdeployment.tfhunk only adds a newvolume_mountblock (for/memory-watchdog.sh) inside theworkspacecontainer — nowhere near themetadata.name(line 5) orhostname(previously line 156) lines this PR touches. No line-level overlap; expected to merge cleanly in either order. Not otherwise coordinated with that PR.Docs updated
CLAUDE.md's "Implementation gotchas" andDESIGN.md's "Design tensions and decisions" both now explain that the Deployment name is a Prometheus identity, not just a Kubernetes identifier — so a future edit doesn't silently regress attribution back to an opaque name.Related cleanup in a different repo (not part of this PR)
homelab-ops-kubernetes-apps'sapps/subsystems/coder/prometheusrule-coder.yamlhas acoder:coderd_podrecording rule that hand-rolls the same pod→ReplicaSet→Deployment joinnamespace_workload_pod:kube_pod_owner:relabelalready does, and could collapse to aworkload="coder"match. Checked all 15 open PRs in that repo — none touch that file, so this is not currently in flight elsewhere. Left undone here since it's a different repository and needs its own PR.Testing
terraform fmt -check,terraform validate,tflint --config=../../../.tflint.hclin the template dir: all pass.pre-commit run --all-files: passes except one pre-existing, unrelated failure already present onmain(hadolintSC3037/DL3066on the Dockerfile) — not touched by this change.test_mode/a liveterraform apply— see "Live verification" above for what is and isn't proven pre-merge.Branch base
#862 (the
end-of-file-fixerfix this branch previously depended on) has landed onmain. This branch is now rebased directly onto currentmainand carries none of that change itself.