Skip to main content
Sentinel runs as a Kubernetes Deployment managed by Krane. For details on how Krane manages sentinel state, see the Krane documentation.

When sentinels are created

Sentinels are created as part of the deployment workflow in the control plane worker (svc/ctrl/worker/deploy/deploy_handler.go). During the deploying phase, the ensureSentinels step checks whether each target region already has a sentinel for the environment. If a region has no sentinel, the workflow inserts one into the database. Once the sentinel record exists in the database, Krane picks it up via its WatchSentinels stream and applies the corresponding Kubernetes resources.

Where sentinels run

All sentinel pods run in a dedicated sentinel Kubernetes namespace, separate from customer workloads and other Unkey services. This namespace contains:
  • Sentinel Deployments (one per environment per region)
  • Services for routing traffic to sentinel pods
  • Gossip headless Services and CiliumNetworkPolicies for cache invalidation
  • Secrets for database, ClickHouse, and Redis credentials
Sentinel pods are scheduled onto dedicated sentinel node class nodes using a toleration for the node-class=sentinel:NoSchedule taint. This keeps sentinel workloads isolated from customer instance pods at the node level, preventing resource contention between the proxy layer and the workloads it routes to.

Kubernetes resources

Each sentinel consists of five resources, all created via server-side apply:
ResourceScopePurpose
DeploymentPer sentinelSentinel pods with rolling update strategy
ClusterIP ServicePer sentinelRoutes traffic to sentinel pods on port 8040
PodDisruptionBudgetPer sentinelKeeps at least one pod available during disruptions
Headless ServicePer environmentGossip peer discovery (resolves to pod IPs on port 7946)
CiliumNetworkPolicyPer environmentAllows gossip traffic between sentinel pods
The environment-scoped resources (headless Service, CiliumNetworkPolicy) are shared across all sentinels in an environment and are not owned by any single Deployment.

Deployment spec

SettingValue
StrategyRollingUpdate
Max Unavailable0
Max Surge1
Min Ready Seconds30
Topology SpreadmaxSkew=1 across availability zones, ScheduleAnyway
Ports8040 (HTTP), 7946 (gossip TCP+UDP)

Probes

ProbeValue
LivenessGET /_unkey/internal/health on port 8040
ReadinessGET /_unkey/internal/health on port 8040
Two consecutive failures remove the pod from the Service endpoints, stopping traffic from reaching it.
Both probes hit the same trivial endpoint that returns 200 unconditionally without checking dependencies. A sentinel with a dead database connection or unavailable Redis reports as healthy. This needs to be migrated to proper liveness and readiness checks like our other services: liveness must verify the process is alive, readiness must verify sentinel can actually serve traffic (database reachable, middleware engine initialized). Tracked in #5367.

Labels

All sentinel resources carry these labels:
LabelValue
app.kubernetes.io/managed-bykrane
app.kubernetes.io/componentsentinel
unkey.com/workspace.idWorkspace ID
unkey.com/project.idProject ID
unkey.com/app.idApp ID
unkey.com/environment.idEnvironment ID
unkey.com/sentinel.idSentinel ID

Cache prewarming

On startup, the router service loads all deployments with status READY in the environment and prefetches their instances. This avoids cold-start latency spikes on the first requests after a pod restart.