Deploy on Kubernetes
Kubernetes manifests, Kustomize and the inline edge sidecar
Deploy on Kubernetes
The manifests live in
deploy/k8s/, all in
the dedicated sentry namespace:
| Manifest | Contents |
|---|---|
namespace.yaml | sentry namespace (Pod Security baseline) |
configmap.yaml | non-secret sentry.toml (packs + nginx source) |
secret.yaml | sentry-secrets (cf-token, cf-zone, llm-key, pg-password) |
postgres.yaml | Postgres 16 StatefulSet + 10Gi PVC + Service |
sentry.yaml | daemon Deployment + Service 8080 |
pdb.yaml | PodDisruptionBudget (minAvailable: 1) |
kustomization.yaml | applies all of the above at once |
edge-sidecar.yaml | separate example: inline edge as a sidecar to your app |
Apply the manifests
kubectl apply -k deploy/k8s/ # via Kustomize (recommended)
kubectl apply -f deploy/k8s/ # or file by fileVerify the deploy
The namespace is sentry — pass -n on every command:
kubectl get pods -n sentry
kubectl logs -f deployment/sentry -n sentry
kubectl describe pod -n sentry -l app=sentryThe Deployment has a sentry config validate liveness probe — a pod with an
invalid config restarts with the error visible in kubectl logs.
Secrets
Secrets (Cloudflare tokens, LLM key, Postgres password) live in
secret.yaml
(sentry-secrets) — fill in real values before applying:
kubectl create secret generic sentry-secrets -n sentry \
--from-literal=cf-token=$SENTRY_CF_TOKEN \
--from-literal=cf-zone=$SENTRY_CF_ZONE \
--from-literal=llm-key=$SENTRY_LLM_KEY \
--from-literal=pg-password=$SENTRY_PG_PASSWORDThe Deployment consumes them via valueFrom: secretKeyRef (cf-token,
cf-zone, llm-key); Postgres consumes pg-password.
Postgres
postgres.yaml
runs a postgres:16-alpine StatefulSet with a 10Gi PVC and the
postgres.sentry.svc.cluster.local:5432 Service. For production, a managed
Postgres (Cloud SQL, RDS, …) works too: point
SENTRY_STORAGE__POSTGRES__URL at it and drop the StatefulSet from the
kustomization.
Inline edge as a sidecar
edge-sidecar.yaml
shows the inline topology (sentry-edge):
the sentry-edge container runs with
[deployment] mode = "inline", owns the Service's public port
([edge] listen = "0.0.0.0:80") and forwards to the application on
127.0.0.1:8080 — the app port is never exposed. Same binary as the daemon,
different command/config:
kubectl apply -f deploy/k8s/edge-sidecar.yamlFor TLS 443 on the sidecar, use an image built with the edge-tls feature
and set [edge] tls_cert/tls_key/tls_listen (JA3/JA4 telemetry included).
Horizontal scaling
Shared state lives in Postgres: rules and blocks sync across pods via
LISTEN/NOTIFY (sentry_rules_changed, sentry_blocks_changed) and
duplicate events from sibling nodes are deduped by payload_hash inside the
10s window (F4.7).
Documented limitations: behavioral trackers (scan/behavior/offender) and the
in-memory rate limiter are per-node — for shared counters use the Redis
backend ([rate_limit] backend = "redis" + the rate-redis feature).
kubectl scale deployment/sentry -n sentry --replicas=3On multi-node setups, give each pod a distinct instance_id
([deployment] instance_id) for the sentry_instance_info{instance} gauge.
Update the configuration
Edit the ConfigMap and restart the deployment (DB-managed rules hot-reload via NOTIFY, no restart needed):
kubectl edit configmap sentry-config -n sentry
kubectl rollout restart deployment/sentry -n sentry