Skip to content

0005 — GitHub Actions → GHCR → pull-based deploy

Status: accepted (2026-07-23)

Context

The repo's original scripts/ deployed by password-authenticated rsync-and-restart (removed in 1120727); nothing was reproducible and the VPS is too small to build images. Karat's pipeline (build in CI, pull on the server) has been running reliably and is the obvious template.

Decision

Per-component workflows (.github/workflows/deploy-*.yml) build images with Buildx (GHA layer cache), push latest + sha-<short> to ghcr.io/aimorgcode/*, copy the deploy/ configs to /opt/aim-store, then SSH in and run docker compose pull + up -d --no-build for just the affected services — serialized with flock /tmp/aim-store-deploy.lock, fail-fast, and verified by curl --resolve <domain>:443:200.141.2.84 health checks. Backend deploys additionally take pg_dump and mongodump backups into /opt/aim-store/backups (last 14 of each) and run deploy/apply-migrations.sh before restarting.

Consequences

  • The server never builds; deploys are minutes and reproducible; every deploy has a restorable pre-migration backup of both databases.
  • Rollback = pin API_IMAGE_TAG / WEBSITE_IMAGE_TAG / ADMIN_PORTAL_IMAGE_TAG / DOCS_IMAGE_TAG to a previous sha- tag and re-run up -d --no-build.
  • Requires two small-blast-radius credentials: an SSH deploy key (VPS_SSH_KEY + VPS_HOST/VPS_USER) and a read:packages pull token (GHCR_PULL_USER/GHCR_PULL_TOKEN).