1 Deployment & CI
dlawler489 edited this page 2026-06-20 07:29:33 +10:00

Deployment & CI

Self-hosted end to end on the home server (Mac Mini, arm64, OrbStack). Code, CI, and the container registry all live on Forgejo at git.plexultra.com (migrated off GitHub/GHCR in June 2026).

Pipeline

  1. Commit and push to Forgejo (origin is the forgejo: SSH alias → 192.168.1.124:2222, LAN only).
  2. Forgejo Actions (.forgejo/workflows/docker-build.yml) runs on mac-mini-runner and builds + pushes two images, natively arm64 (no QEMU):
    • git.plexultra.com/dlawler489/etsy-finance-tracker:main (API, production stage)
    • git.plexultra.com/dlawler489/etsy-finance-tracker-nginx:main (nginx, nginx-frontend stage)
    • Both also tagged with the commit SHA (rollback targets).
  3. Redeploy in Dockhand, which pulls docker-compose.deploy.yml and recreates the stack.

The deploy host must be docker login git.plexultra.com (or the packages public) to pull.

Runner setup (gitea/act_runner in docker-compose)

The runner runs each job in a container and has /var/run/docker.sock mounted. Key facts that make the workflow work:

  • No Node in job containers → can't use JS actions like actions/checkout. The workflow is node-free: manual git init/fetch/checkout over HTTPS with a token.
  • Job runs in container: docker:cli (Alpine) → shell: sh, and apk add git for checkout.
  • act_runner auto-mounts the Docker socket into job containers — do not add it in the workflow's volumes: (causes Duplicate mount point).
  • runs-on: ubuntu-latest — the label only selects the runner; the docker:cli container override does the work. (The runner re-registered with default labels once a config.yaml/CONFIG_FILE was added, dropping the original docker label.)
  • Runner config.yaml (loaded via CONFIG_FILE=/data/config.yaml) sets container.valid_volumes.

Registry auth

Pushing packages needs a Forgejo token with write:package, stored as the Actions secret REGISTRY_TOKEN and used for docker login. The automatic GITHUB_TOKEN cannot push packages (unauthorized: reqPackageAccess); it's still used for the git checkout.

Traefik

Label discovery was unreliable; routing uses the file provider: /Users/davidlawler/docker/traefik/dynamic/etsy.yml routes Host(etsy.plexultra.com)etsy-nginx:80 (websecure, le certresolver).

Persistence & secrets

  • MongoDB on a named volume (etsy_mongodb) — survives container recreation. Clearing orders/expenses in-app does not touch the volume.
  • JWT_SECRET set in Dockhand env (don't leave the default).
  • Etsy keystring/secret and OAuth tokens are stored unencrypted in MongoDB — fine for single-user self-host, but a DB backup contains them.