homelab-infra
Shared CI/CD templates, base Dockerfiles, and compose snippets for all homelab projects. Change once here, every project picks it up.
Repo structure
.gitea/workflows/
build-and-push.yml ← reusable workflow (called by project repos)
base-images/
python-git-ssh/
Dockerfile ← Python 3.12 + git + openssh (build once)
entrypoints/
git-pull-exec.sh ← pull-before-exec entrypoint for live code updates
compose-snippets/
watchtower.yml ← auto-pull on image update
act_runner.yml ← Gitea Actions self-hosted runner (deploy on Pi)
How a project uses the shared workflow
In your project repo, create .gitea/workflows/build.yml:
name: Build
on:
push:
branches: [main]
workflow_dispatch:
jobs:
build:
uses: thethreemagi/homelab-infra/.gitea/workflows/build-and-push.yml@main
with:
image_name: your-project-name
secrets:
REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
Add two secrets to the project repo (Gitea → repo → Settings → Secrets):
REGISTRY_USER— your Gitea username (thethreemagi)REGISTRY_TOKEN— a PAT withwrite:packagesscope
On push to main, the workflow builds a multi-tag image and pushes to
git.thewichersfamily.com/thethreemagi/your-project-name:latest.
One-time server setup (Gitea + runner)
1. Enable Actions in Gitea
Edit your Gitea app.ini (check your Gitea compose volume mount for the path):
[actions]
ENABLED = true
Restart Gitea: docker compose restart gitea
2. Generate runner registration token
Gitea → Site Administration → Actions → Runners → Create Runner → copy token.
3. Deploy act_runner on Pi 5
Copy compose-snippets/act_runner.yml to /srv/act-runner/docker-compose.yml.
Add to .env:
RUNNER_TOKEN=<token from step 2>
docker compose up -d
Verify: Gitea → Site Administration → Actions → Runners → homelab-pi5 online.
4. Generate registry PATs
Runner PAT (push images):
- Gitea → Settings → Applications → Generate Token → scope:
write:packages - Set as
REGISTRY_TOKENsecret in each project repo
Pi pull PAT (Watchtower pulls):
- Gitea → Settings → Applications → Generate Token → scope:
read:packages - On Pi:
docker login git.thewichersfamily.com
5. Deploy Watchtower on Pi
Use compose-snippets/watchtower.yml. Polls every 5 min, restarts containers on new image digest.
Adding a new project
- Ask Claude to create the repo via MCP (needs
write:userscope on token) - Claude adds
.gitea/workflows/build.ymlcalling this shared workflow - Add
REGISTRY_USER+REGISTRY_TOKENsecrets to repo - Update project
docker-compose.yml:image: git.thewichersfamily.com/thethreemagi/<name>:latest - Push — CI runs automatically
Runner note: ARM64 vs x86
Runner runs on Pi 5 — native ARM64 builds, no QEMU needed.
If moved to x86 NAS later, uncomment the QEMU steps in build-and-push.yml.
Requires Gitea 1.21+
Reusable workflows require Gitea 1.21+. Check: Site Administration → Configuration → Gitea Version.