API with sidecar
Build a production-grade deployment from scratch:
- An API container listening on 8080
- An nginx sidecar on 80 handling TLS termination at the service boundary and serving static assets
- Database credentials loaded from a sealed secret
- Ingress with automatic certificate via cert-manager
- Horizontal pod autoscaler
The full file
Section titled “The full file”deployment "api" { namespace = "production" image_pull_secrets = ["regcred"] service_account = "api"
container "api" { image = image("api") image_pull_policy = "IfNotPresent"
port "8080" "http" { health = "/health" ready = "/ready" }
env { APP_ENV = var.environment DATABASE_URL = secret("db-credentials", "DATABASE_URL") JWT_SECRET = secret("jwt-keys", "secret") LOG_LEVEL = var.environment == "production" ? "warn" : "debug" }
env_from { config_map = "api-config" }
resources { cpu = "300m..1000m" memory = "512Mi..1Gi" }
volume "assets" { mount_path = "/app/public" empty_dir = true }
security_context { run_as_user = 1000 run_as_non_root = true read_only_root = true } }
container "nginx" { image = image("nginx")
port "80" "http" { tcp_health = true }
resources { cpu = "50m..200m" memory = "32Mi..128Mi" }
volume "nginx-conf" { mount_path = "/etc/nginx/conf.d/default.conf" sub_path = "default.conf" config_map = "nginx-config" }
volume "assets" { mount_path = "/var/www/html" empty_dir = true read_only = true } }
init "warmup" { image = image("api") command = ["/bin/sh", "-c", "php bin/console cache:warmup && cp -R public/* /shared/"]
volume "assets" { mount_path = "/shared" empty_dir = true } }
service { port "80" "http" {} }
ingress { host = "api.example.com" tls = true issuer = "letsencrypt-production"
annotations = { "nginx.ingress.kubernetes.io" = { "proxy-body-size" = "50m" "proxy-read-timeout" = "120" } } }
autoscale { min = 2 max = 10 cpu = 70 memory = 80 }}What this generates
Section titled “What this generates”One kdef render --dir k8s/ produces, in order:
- Deployment
apiwith init container + two sidecars - Service
apion port 80 → containerPort 80 (the nginx sidecar) - Ingress
api.example.comwith TLS and cert-manager annotations - Certificate (via cert-manager) for the host
- HorizontalPodAutoscaler targeting 70% CPU / 80% memory, 2–10 replicas
Five resources. One block.
The supporting files
Section titled “The supporting files”variable "environment" { type = "enum[staging, production]" default = "staging"}
ingress_defaults { issuer = "letsencrypt-production" annotations = { "nginx.ingress.kubernetes.io" = { "force-ssl-redirect" = "true" } }}images { api = "registry.example.com/my-app/api:1.4.2" nginx = "registry.example.com/nginx:stable-alpine"}configmap "api-config" { namespace = "production" data = { APP_NAME = "my-api" APP_URL = "https://api.example.com" }}
configmap "nginx-config" { namespace = "production" data = { "default.conf" = file("configs/nginx.conf") }}# secrets.kdef — committed safely; values are kubeseal-encryptedsealedsecret "db-credentials" { namespace = "production" data = { DATABASE_URL = "AgBy3i4OJSWK+PiTySYZZA9rO43..." }}
sealedsecret "jwt-keys" { namespace = "production" data = { secret = "AgCE9F2h7GKJF8mL3nP5rS7tV9xB..." }}To produce those encrypted blobs:
kdef seal --secret db-credentials --key DATABASE_URL \ --value "postgres://api:hunter2@db:5432/api"See the sealed secrets guide for the full flow.
Deploy it
Section titled “Deploy it”kdef validate --dir k8s/kdef diff --dir k8s/kdef apply --dir k8s/