dev_arc_aws/deploy
Samuel James bddf891c0a
All checks were successful
Build & Push Images / build (push) Successful in 38s
CI / validate (push) Successful in 1m11s
Build & Push Images / deploy (push) Successful in 23s
Auto-deploy to racknerd2 after a successful build
Add a `deploy` job to build.yml that needs `build`, so every push to main
builds + pushes the images and then deploys them to racknerd2 over the
mesh, pinned to the built commit's SHA, with an /api/health gate. Fully
hands-off.

The standalone deploy.yml stays as a manual workflow_dispatch for
deploying/rolling back to an arbitrary tag without rebuilding.

deploy/README.md updated to document the auto-deploy flow.

Co-authored-by: Samuel James <ssamjame@amazon.com>
Co-authored-by: Kiro <noreply@kiro.dev>
2026-06-25 11:34:27 -04:00
..
.env.example Add Forgejo Actions build + deploy pipeline (registry -> racknerd2) 2026-06-25 10:04:59 -04:00
docker-compose.yml Point registry at registry.snsnetlabs.com; record even=dev versioning 2026-06-25 10:55:15 -04:00
README.md Auto-deploy to racknerd2 after a successful build 2026-06-25 11:34:27 -04:00

ArchNest — Build & Deploy (Forgejo Actions → registry → racknerd2)

This pipeline builds the Docker images in Forgejo Actions, pushes them to the Forgejo container registry, and deploys them to racknerd2 (validation host) over the NetBird mesh. racknerd2 only pulls and runs — it never builds (1.9 GiB RAM).

push to main ─► [build.yml]
                  job: build   ─► build + push images ─► registry.snsnetlabs.com/sam/{archnest,archnest-backend}
                  job: deploy  ─► (needs build) ssh racknerd2 ─► compose pull + up -d (this build's SHA) ─► /api/health
                                                                      │
        manual dispatch (any tag / rollback) ─► [deploy.yml] ssh racknerd2 ─► compose pull && up -d

Every push to main auto-builds and auto-deploys to racknerd2. deploy.yml stays as a manual workflow_dispatch for deploying/rolling back to an arbitrary tag without rebuilding.

Images

Image From Tags
registry.snsnetlabs.com/sam/archnest root Dockerfile (React build → nginx) latest, <commit-sha>
registry.snsnetlabs.com/sam/archnest-backend backend/Dockerfile (Fastify) latest, <commit-sha>

registry.snsnetlabs.com is the unproxied (DNS-only) registry host, so large layers bypass Cloudflare's ~100 MB request-body cap. Pushed images appear at https://forgejo.snsnetlabs.com/sam/-/packages (web UI, Cloudflare Access SSO).

One-time setup

1. Forgejo Actions secrets (repo or org settings → Actions → Secrets)

  • FORGEJO_REGISTRY_TOKEN — Forgejo personal access token for sam with package scope (NOT the account password). Used by build.yml to log in and push.
  • RACKNERD2_SSH_KEY — private SSH key authorized for root@racknerd2 (mesh IP 100.96.217.250). Used by deploy.yml.

2. Runner (forgejo-runner host) — allow Docker builds

The runner runs jobs inside containers and by default has no Docker access. Enable socket auto-mounting so the build job can build images. Create /opt/config.yaml (or edit the existing runner config) with at least:

container:
  docker_host: "automount"   # mounts /var/run/docker.sock into job containers

Generate a full example with forgejo-runner generate-config > /opt/config.yaml, set docker_host: "automount", point the service at it (ExecStart=/usr/local/bin/forgejo-runner daemon -c /opt/config.yaml), then systemctl daemon-reload && systemctl restart forgejo-runner.

3. racknerd2 — prepare the deploy host

Docker Engine + compose plugin are already installed. Then:

mkdir -p /opt/archnest
# copy deploy/docker-compose.yml from this repo to /opt/archnest/docker-compose.yml
# create /opt/archnest/.env from deploy/.env.example and fill in the secrets:
#   ARCHNEST_JWT_SECRET   = openssl rand -hex 32
#   ARCHNEST_SECRET_KEY   = openssl rand -hex 32
#   ARCHNEST_GUAC_CRYPT_KEY = openssl rand -base64 24 | cut -c1-32
docker login registry.snsnetlabs.com   # user: sam, password: the package token

Ports are bound to the mesh IP only (100.96.217.250) — Docker bypasses ufw, so this is what keeps the app off the public interface. Validate at http://100.96.217.250:8080.

Running it

  • Automatic: push to mainbuild.yml builds + pushes both images, then its deploy job (needs build) pulls this commit's SHA onto racknerd2, restarts the stack, and health-checks /api/health. Fully hands-off.
  • Manual build: run Build & Push Images from the Actions tab (also triggers the auto-deploy job).
  • Manual deploy / rollback: run Deploy to racknerd2, entering any tag (latest or a specific commit SHA) to deploy without rebuilding.

Notes / ceilings

  • Auto-deploy targets racknerd2 (the validation host) on every push to main, pinned to the built commit's SHA. If you later add a prod host, gate prod deploys behind a manual approval or a tag/release trigger rather than every push.
  • Single-arch (amd64) only — both the runner host and racknerd2 are amd64, so no buildx/multi-platform is needed.