← Back to blog

January 10, 2026

DevOps Runbook (Phase A): Dockerize + Postgres + Zero‑Downtime Rolling Updates

Long-form Markdown test article (local file storage).

DevOps Runbook (Phase A): Dockerize + Postgres + Zero‑Downtime Rolling Updates

This doc captures what we did to:

  • Dockerize the NestJS API
  • Run API + Postgres locally with Docker Compose
  • Push the image to GHCR
  • Deploy to a Kubernetes homelab cluster with Postgres
  • Stabilize health probes and rollouts for zero‑downtime RollingUpdate
  • Install NGINX Ingress (homelab) and validate no downtime during rollouts

It also documents the bugs we hit and how we fixed them.


0) Pre-reqs

  • Docker (Docker Desktop or docker engine) available on the machine that builds images
  • kubectl configured to talk to your cluster
  • Optional but recommended: helm (for Postgres chart)
  • A working CNI and CoreDNS in the cluster

1) Dockerize the API (multi-stage build)

What we added

  • Dockerfile: multi-stage build that produces a small runtime image
  • .dockerignore: already existed and was good

Notes / decisions

  • We used Debian slim base images instead of Alpine because the app depends on bcrypt (native addon). Alpine often introduces build/runtime pain for native modules unless you install extra build deps.
  • The container listens on PORT (your src/main.ts already supports PORT, typically 8080 for platforms like Cloud Run).

Build locally

docker build -t wakari-api:local .

2) Local dev with Docker Compose (API + Postgres as separate containers)

What we changed

  • Extended docker-compose.yml to include:
    • api service built from the Dockerfile
    • existing postgres service
    • wiring via env vars: WAKARI_DB_HOST=postgres, etc.
    • Postgres healthcheck + depends_on so API waits until DB is healthy

Run

docker compose up --build

Smoke test

  • /health should return 200
curl -sS -i http://localhost:8080/health

Summary

ConceptUserServiceAccount
Create identityopenssl → CSR → kubectl certificate approvekubectl create sa
AuthenticateClient certificate (TLS)Projected JWT token
Identify in RBACkind: User, name: alicekind: ServiceAccount, name: deploy-bot, namespace: app
Impersonate for testing--as=alice--as=system:serviceaccount:app:deploy-bot
Group supportO= field in cert → kind: Groupsystem:serviceaccounts:<ns> (automatic)
Best forHuman access, external CIPod-to-API access, operators, controllers