Skip to content

ADR 0020: Registry-Based CD (Build on CI, Pull on Server)

ADR 0020: Registry-Based CD (Build on CI, Pull on Server)

Section titled “ADR 0020: Registry-Based CD (Build on CI, Pull on Server)”
  • Status: Accepted
  • Date: 2026-04-14
  • Deciders: Ideony team

The production server is a Hetzner CAX11 (2 vCPU ARM64, 4 GB RAM). Running docker build on the server during deployment caused out-of-memory kills: the TypeScript compiler + SWC + Prisma generate combined exhaust available RAM, crashing the build mid-step. A different build strategy was needed.

Build the Docker image in GitHub Actions CI (on ubuntu-latest with 7 GB RAM, cross-compiling for linux/arm64 via QEMU). Push the tagged image to GitHub Container Registry (ghcr.io/acidrums7/ideony-api:<sha>). Deploy step SSH-es into the server and runs docker pull <image>:<sha> && docker stop ideony-api && docker run .... The server never runs docker build.

  • CI runners (7 GB RAM) handle the full TypeScript + SWC + Prisma build without OOMs.
  • Pull-based deploy: server downloads a pre-built image; startup is fast and deterministic.
  • GHCR image tagged with git SHA enables precise rollback (docker run ghcr.io/.../ideony-api:<previous-sha>).
  • Trivy vulnerability scan runs against the pushed image in CI before deploy is triggered.
  • Blue-green swap + health check in deploy script; auto-rollback on failed health check.
  • Cross-compilation (linux/arm64 on x86_64) via QEMU is 4–8× slower than native build; CI push takes ~8–12 min. Acceptable for MVP 0.
  • GHCR requires GITHUB_TOKEN / PAT with packages:write in CI and packages:read on the server (Docker login).
  • Old image tags accumulate in GHCR; a periodic retention policy (keep last 10 tags) must be configured.
  • Build on server — rejected: CAX11 OOMs during TypeScript + SWC + Prisma generate; build killed mid-step.
  • Dokploy native build (Nixpacks) — considered: Dokploy’s Nixpacks builder also runs on CAX11; same OOM problem.
  • Fly.io remote builder — rejected: would require switching hosting provider; out of scope.
  • Smaller base image / incremental build — considered: multi-stage Dockerfile already minimises final image; the compile stage itself (not the final image) is the OOM source; compile must happen on CI.