Build the frontend and backend images in CI, push them to the Forgejo container registry, and deploy to racknerd2 (validation host) over the NetBird mesh. racknerd2 only pulls + runs (1.9 GiB RAM, never builds). - .forgejo/workflows/build.yml: on push to main / manual, build both images and push :latest + :<sha> to forgejo.snsnetlabs.com/sam/... (installs the docker CLI in the job; relies on the runner's docker_host=automount to reach the host engine). - .forgejo/workflows/deploy.yml: manual dispatch; SSH to racknerd2, docker compose pull + up -d, then /api/health check. - deploy/docker-compose.yml: registry-image compose. Ports bound to the mesh IP only (Docker bypasses ufw), so the app is reachable over the mesh, not the public interface. - deploy/.env.example + deploy/README.md: deploy host config + full pipeline/prereq docs. - .gitignore: ignore real .env / deploy/.env. Co-authored-by: Samuel James <ssamjame@amazon.com> Co-authored-by: Kiro <noreply@kiro.dev>
3.4 KiB
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 / manual ─► [build.yml] build + push images ─► forgejo.snsnetlabs.com/sam/{archnest,archnest-backend}
│
manual dispatch ─► [deploy.yml] ssh racknerd2 ─► docker compose pull && up -d
Images
| Image | From | Tags |
|---|---|---|
forgejo.snsnetlabs.com/sam/archnest |
root Dockerfile (React build → nginx) |
latest, <commit-sha> |
forgejo.snsnetlabs.com/sam/archnest-backend |
backend/Dockerfile (Fastify) |
latest, <commit-sha> |
Pushed images appear at https://forgejo.snsnetlabs.com/sam/-/packages (SSO).
One-time setup
1. Forgejo Actions secrets (repo or org settings → Actions → Secrets)
FORGEJO_REGISTRY_TOKEN— Forgejo personal access token forsamwith package scope (NOT the account password). Used bybuild.ymlto log in and push.RACKNERD2_SSH_KEY— private SSH key authorized forroot@racknerd2(mesh IP100.96.217.250). Used bydeploy.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 forgejo.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
- Build: push to
main, or run Build & Push Images manually (Actions tab → Run workflow). - Deploy: run Deploy to racknerd2 manually, entering the tag
(
latestor a specific commit SHA). It pulls, restarts, and health-checks/api/health.
Notes / ceilings
ponytail:deploy is manual (workflow_dispatch), not auto-on-merge — this is a validation host, so deploys are deliberate. Wirebuild.yml→deploy.ymlwithneeds:later if auto-deploy-to-validation is wanted.- Single-arch (amd64) only — both the runner host and racknerd2 are amd64, so no buildx/multi-platform is needed.