Skip to content

Shared clusters with team namespaces

Many platform teams run one big cluster (often AKS, EKS, or GKE) where each team gets its own namespaces, isolated with network policies. Shoehorn handles this with one agent and zero per-workload config.

The agent assigns every workload to a team using the first match:

  1. A shoehorn.dev/owner or shoehorn.dev/team label on the workload, then the same keys as annotations (labels win over annotations)
  2. A shoehorn.dev/team label on the workload’s namespace
  3. Falls back to unassigned

shoehorn.dev/team and shoehorn.dev/owner are the recommended keys. Legacy alias keys (owner, app.kubernetes.io/owner, and team on namespaces) also work; see the annotations reference for the full list.

So the whole setup is one label per namespace:

Terminal window
kubectl label namespace payments shoehorn.dev/team=payments
kubectl label namespace checkout shoehorn.dev/team=checkout

Every Deployment, StatefulSet, DaemonSet, and CronJob in those namespaces now shows up in the catalog owned by the right team. Individual workloads can still override with their own shoehorn.dev/team label.

The agent only needs two connections: the Kubernetes API server and your Shoehorn API endpoint. It never connects to kubelets, pods, or node ports, so deny-by-default policies between team namespaces don’t affect it. Allow egress from the agent’s namespace to the API server and to your Shoehorn URL and you’re done.

Helm release detection lets the agent read the contents of every secret in the namespaces it watches, not just Helm’s own. Leave it off unless you need Helm release discovery.

One agent per cluster is the default and covers the shared-cluster case: teams are separated in the catalog by ownership, not by agent. If you need hard separation (different Shoehorn tenants on one physical cluster), run one agent per scope with agent.kubernetes.scopeMode: namespaces. Each agent registers as its own cluster and uses one license slot. See the agent guide for scoped mode.

The optional network observer (eBPF) watches pods across the whole cluster, so it doesn’t work with per-tenant scoped agents on a shared cluster. Leave it off in that setup.

For clusters past roughly 200 nodes, start from the large-cluster values preset so the agent’s buffers match your event volume. See Tuning for large clusters.