Skip to main content

ADR-013: GitHub Container Registry (GHCR) as Container Registry

Status

Accepted (2026-03-06 — shipped in sv0-platform PR #35)


Context

The CI/CD pipeline (automated dev deploys, manual prod deploys) needs a place to store built Docker images so that the deploy SSH step can pull them onto the target servers without re-building from source. The build runs in GitHub Actions; the deploy step runs on two Hetzner VPS hosts via SSH.

We need a registry that:

  • Is accessible from both GitHub Actions (push) and Hetzner VPS hosts (pull)
  • Requires no additional account setup beyond what we already have
  • Supports private images (security)
  • Can tag images by commit SHA for immutable, rollback-safe deploys

Options Considered

OptionNotes
GHCR (GitHub Container Registry)Built into GitHub. GITHUB_TOKEN authenticates in CI automatically. Org-scoped, private by default. No additional cost within org plan limits.
Docker HubPublic registry. Free tier has pull rate limits (100 pulls/6h for anonymous). Private repos require a paid plan and a separate account/token to manage.
Amazon ECRRequires an AWS account and IAM credentials. Adds AWS dependency to an otherwise AWS-free stack. 500 MB/month free, then per-GB cost.
Self-hosted (registry container)Eliminates external dependency but adds operational overhead: storage, authentication, availability, garbage collection.

Decision

Use GitHub Container Registry (GHCR) at ghcr.io/securityv0/sv0-platform/.

All Docker images are published to GHCR on every:

  • Push to main (triggers build + dev deploy)
  • Pull request push (builds PR-tagged images for preview instances)

Image Naming and Tagging

ghcr.io/securityv0/sv0-platform/api:<tag>
ghcr.io/securityv0/sv0-platform/ui:<tag>
TagWhen createdUse
sha-<commit>Every push to mainImmutable reference for prod deploys
latestEvery push to mainConvenience alias — do not use for prod
pr-<number>Every PR pushPR preview instances on dev

Production deploys always specify a sha-<commit> tag for immutability and rollback safety.


Authentication

ContextAuth method
GitHub Actions (push)GITHUB_TOKEN — automatic, no PAT needed
GitHub Actions (pull in deploy workflow)GITHUB_TOKEN — automatic
Hetzner VPS (pull via SSH deploy)GHCR_TOKEN secret (fine-grained PAT, read:packages scope) passed to deploy scripts
Manual developer pullPAT with read:packages scope: echo $PAT | docker login ghcr.io -u <user> --password-stdin

Consequences

Positive:

  • Zero additional infrastructure to manage
  • Authentication is handled by GITHUB_TOKEN in CI — no PAT rotation for the build step
  • Images are scoped to the SecurityV0 GitHub org — access control inherits from GitHub org membership
  • Image metadata and layer caching visible in GitHub Packages UI

Negative / Trade-offs:

  • Pull on VPS requires a dedicated PAT (GHCR_TOKEN) stored as a GitHub Actions secret — one token to manage per environment
  • If we ever leave GitHub, image migration is required
  • GitHub org plan limits apply (currently generous for our scale)

  • Infrastructure Index — deployment overview
  • Platform docs/deploy/deployment.md — GHCR pull troubleshooting (401/403 errors)