docs: document gitea-first homelab deploys
Switch CLAUDE.md and AGENTS.md to Gitea-primary language. Remove github-runner from the management plane now that Gitea Actions handles Caddy reloads and famapp builds. Disable GitHub deploy workflow. Update apply-compose.ps1 status text. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
e3937dcf52
commit
a44adc4814
@@ -1,30 +1,14 @@
|
||||
name: Deploy to valhalla
|
||||
|
||||
# Post-split deploy. The application stacks (foundry, media, notify, monitoring,
|
||||
# remote, owncloud, resume, famapp, authentik, dev) live under stacks/<domain>/
|
||||
# and are deployed by Portainer git polling — the runner does NOT touch them.
|
||||
# Caddy lives in its own Portainer git stack (stacks/proxy/) and is likewise
|
||||
# deployed by Portainer; this workflow only pushes the Caddyfile and triggers
|
||||
# a hot reload on the running Caddy container.
|
||||
# Deployment moved to Gitea Actions (.gitea/workflows/deploy-caddy.yml).
|
||||
# This workflow is kept as a manual fallback only.
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- Caddyfile
|
||||
- .github/workflows/deploy.yml
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: self-hosted
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Push Caddyfile and reload Caddy
|
||||
run: |
|
||||
cp Caddyfile /config/caddy/Caddyfile
|
||||
docker exec caddy caddy reload --config /etc/caddy/Caddyfile
|
||||
- name: Disabled
|
||||
run: echo "Deployment moved to Gitea Actions."
|
||||
|
||||
@@ -15,9 +15,11 @@ from your tool's user-level instructions (`~/.codex/AGENTS.md` / `~/.gemini/GEMI
|
||||
- **The repo is canonical.** Portainer polls `main` every 5 min and redeploys any app stack whose
|
||||
`stacks/<domain>/*` files changed. Editing here changes nothing until you `git push`.
|
||||
- **Deployment channels:**
|
||||
- `stacks/<domain>/*` → git push → Portainer redeploys that stack (≤ 5 min).
|
||||
- `Caddyfile` → git push → runner workflow `scp`s it + reloads Caddy (or `apply-compose.ps1 -Caddy`).
|
||||
- `stacks/<domain>/*` → git push to Gitea → Portainer polls every 5 min and redeploys that stack. GitHub is a temporary mirror only.
|
||||
- `Caddyfile` → git push → Gitea Actions (`.gitea/workflows/deploy-caddy.yml`) copies it + reloads Caddy. Fallback: `apply-compose.ps1 -Caddy`.
|
||||
- `portainer-compose.yml` / `vault.hcl` → `apply-compose.ps1 -Portainer` (Portainer can't manage itself).
|
||||
- **Portainer app stacks must have empty UI environment variables.** All config comes from `stack.env` via `env_file: stack.env`. Run `scripts/check-portainer-stack-env.ps1` before pushing.
|
||||
- **famapp images** build on Gitea Actions (`v*` tags) and push to `registry.ginnoir.com/ginnoir/famapp`.
|
||||
- **Secrets are committed intentionally.** `.env` and `stacks/*/stack.env` are versioned in this
|
||||
PRIVATE repo. Do **not** scrub or gitignore them.
|
||||
- **Line endings:** `.gitattributes` forces LF. Never push CRLF (especially `stack.env`) — stray
|
||||
|
||||
@@ -6,9 +6,9 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
|
||||
Deployment configuration for a **live, single-host Docker homelab** running on a headless Ubuntu server reachable at `ssh ginnoir@valhalla`. This repo is **not application code** — it is the canonical source for ~50 containers organized into per-domain Portainer-managed stacks.
|
||||
|
||||
**The repo is canonical.** Portainer polls this repo's `main` branch every 5 min and redeploys any application stack whose source files have changed. Editing here changes nothing until you push to GitHub (or, for the management plane, run `apply-compose.ps1 -Portainer`).
|
||||
**The repo is canonical.** Portainer polls the **Gitea** repo `https://gitea.ginnoir.com/ginnoir/homelabstack` on `main` every 5 min and redeploys any application stack whose source files have changed. Editing here changes nothing until you push (or, for the management plane, run `apply-compose.ps1 -Portainer`). GitHub is a temporary push mirror only.
|
||||
|
||||
`.env` and `stacks/*/stack.env` are committed **intentionally** — the GitHub repo `ginnoir/homelabstack` is private and the stack's secrets are versioned with it. Do not scrub or gitignore them.
|
||||
`.env` and `stacks/*/stack.env` are committed **intentionally** — secrets are versioned in the private repos. Do not scrub or gitignore them.
|
||||
|
||||
## Layout
|
||||
|
||||
@@ -25,25 +25,26 @@ Deployment configuration for a **live, single-host Docker homelab** running on a
|
||||
│ ├── notify/ # ntfy, freshrss, vigilant
|
||||
│ ├── monitoring/ # uptime-kuma, homarr
|
||||
│ ├── remote/ # rustdesk hbbr + hbbs
|
||||
│ └── dev/ # gitea, code-server, registry, bookstack, dbx, plane
|
||||
│ └── dev/ # code-server, bookstack, dbx, plane
|
||||
├── portainer-compose.yml # management plane (raw compose, not a Portainer stack)
|
||||
├── vault.hcl # vault config (deployed alongside portainer-compose.yml)
|
||||
├── Caddyfile # caddy config (deployed via runner workflow or -Caddy)
|
||||
├── Caddyfile # caddy config (deployed via Gitea Actions or -Caddy)
|
||||
├── .env # mgmt-plane env + vault unseal keys + global vars
|
||||
├── apply-compose.ps1 # manual ops helper (mgmt plane + Caddyfile + vault unseal)
|
||||
└── .github/workflows/deploy.yml # runner: pushes Caddyfile + reloads caddy on push
|
||||
├── .gitea/workflows/deploy-caddy.yml # Gitea Actions: pushes Caddyfile + reloads caddy on push
|
||||
└── .github/workflows/deploy.yml # disabled — Gitea Actions is primary
|
||||
```
|
||||
|
||||
## Deployment channels — what triggers what
|
||||
|
||||
| Edit | Deploys via | Latency |
|
||||
|---|---|---|
|
||||
| `stacks/<domain>/*` | **git push** → Portainer polls every 5 min and redeploys that one stack | ≤ 5 min |
|
||||
| `Caddyfile` | git push → runner workflow `scp`s to `/config/caddy/Caddyfile` + `docker exec caddy caddy reload` | seconds (when workflow is enabled) |
|
||||
| `stacks/<domain>/*` | **git push to Gitea** → Portainer polls every 5 min and redeploys that one stack | ≤ 5 min |
|
||||
| `Caddyfile` | git push → Gitea Actions copies to `/config/caddy/Caddyfile` + `docker exec caddy caddy reload` | seconds |
|
||||
| `portainer-compose.yml` / `vault.hcl` | `apply-compose.ps1 -Portainer` (Portainer can't manage itself) | seconds |
|
||||
| `~/valhalla-lab/.env` (mgmt-plane env, vault unseal keys) | `apply-compose.ps1 -EnvFile` | seconds |
|
||||
|
||||
The runner workflow gate-filters on `paths: [Caddyfile, .github/workflows/deploy.yml]` so non-Caddyfile pushes don't trigger it.
|
||||
Gitea is primary; GitHub is a temporary push mirror. Portainer app stacks must have **no Portainer UI env vars** — all config comes from `stack.env` via `env_file: stack.env`.
|
||||
|
||||
## On-host topology
|
||||
|
||||
@@ -91,7 +92,7 @@ ssh ginnoir@valhalla "docker compose -f /config/portainer/compose/14/stacks/ownc
|
||||
|
||||
1. Pick the stack it belongs to under `stacks/<domain>/` (or create a new stack and a new Portainer git stack for it).
|
||||
2. Add the service to `stacks/<domain>/docker-compose.yml`. Configs → `/config/<svc>` bind. Blobs → `/storage1/labdata/<svc>` bind. Add `edge` to its `networks:` if Caddy must reach it; otherwise just the per-stack private net.
|
||||
3. Add secrets to `stacks/<domain>/stack.env` (Portainer reads this file directly from the git repo on each redeploy). **Env convention:** every service gets `env_file: stack.env`, and var names in `stack.env` are exactly what the container reads — no `${VAR}` interpolation, no Portainer UI env vars. Composite values (e.g. `DATABASE_URL`) are written out in full in `stack.env`; keep embedded components in sync on rotation. Exception: `stacks/dev` still uses `${VAR}` interpolation backed by Portainer UI env vars — when editing its `stack.env`, mirror the change in the stack's UI env vars too.
|
||||
3. Add secrets to `stacks/<domain>/stack.env` (Portainer reads this file directly from the git repo on each redeploy). **Env convention:** every service gets `env_file: stack.env`, and var names in `stack.env` are exactly what the container reads — no `${VAR}` interpolation, no Portainer UI env vars. Composite values (e.g. `DATABASE_URL`) are written out in full in `stack.env`; keep embedded components in sync on rotation.
|
||||
4. Add a site block to `Caddyfile` (`reverse_proxy <container_name>:<port>`); `import internal_only` for LAN-only.
|
||||
5. Run `./scripts/gen-bookmarks.ps1` to regenerate `bookmarks-domains.html` + `bookmarks-ports.html` from the new Caddy block / published ports.
|
||||
6. `git push`. Portainer redeploys the stack within 5 min; the runner reloads Caddy on the same push if `Caddyfile` changed.
|
||||
@@ -113,7 +114,9 @@ ssh ginnoir@valhalla "docker compose -f /config/portainer/compose/14/stacks/ownc
|
||||
- `Caddyfile` proxies `matrix.ginnoir.com → localhost:8008`, but there is **no Matrix/Synapse service in compose** — it's external/legacy. Likewise `dev.ginnoir.com → 192.168.1.74:3000` points at a different LAN host.
|
||||
- `watchtower` auto-updates `:latest` images, so a running image can drift ahead of what the last `apply` pulled. **Infra images** (Postgres, MariaDB, Redis, MinIO, CouchDB, Caddy, Vault) are **version-pinned** in compose with `com.centurylabs.watchtower.enable=false`; bump tags manually every few months.
|
||||
- Line endings: `.gitattributes` forces **LF** so files stay Unix-clean. Pushing CRLF (especially `stack.env`) to the Linux host appends stray `\r` to values and breaks things.
|
||||
- The runner workflow (`.github/workflows/deploy.yml`) may be **disabled** at the GitHub repo level — check `gh workflow list` if Caddyfile pushes don't trigger a reload. Manual fallback: `apply-compose.ps1 -Caddy`.
|
||||
- **Caddyfile reloads** are handled by `.gitea/workflows/deploy-caddy.yml` (Gitea Actions, `valhalla-management-runner`). If Gitea Actions doesn't trigger, fall back to `apply-compose.ps1 -Caddy`. The GitHub workflow (`.github/workflows/deploy.yml`) is disabled.
|
||||
- **famapp images** are built by Gitea Actions on `v*` tags and pushed to `registry.ginnoir.com/ginnoir/famapp`. Portainer pulls from the self-hosted registry (registered in Portainer as "Valhalla Registry").
|
||||
- **Portainer app stacks must have empty UI environment variables.** All stack config comes from `stack.env` via `env_file`. Run `scripts/check-portainer-stack-env.ps1` to verify before pushing.
|
||||
|
||||
## Skills
|
||||
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ if ($Portainer) {
|
||||
Write-Host "Pushing vault.hcl (Vault lives in the management plane) ..."
|
||||
ssh $server "mkdir -p /config/vault/config /config/vault/data /config/vault/logs"
|
||||
scp $vaultConfigLocal "${server}:/config/vault/config/vault.hcl"
|
||||
Write-Host "Applying management plane (portainer, vault, github-runner, watchtower) ..."
|
||||
Write-Host "Applying management plane (portainer, vault, gitea, registry, watchtower) ..."
|
||||
ssh $server "docker compose -f ~/valhalla-lab/portainer-compose.yml --env-file ~/valhalla-lab/.env up -d"
|
||||
}
|
||||
|
||||
|
||||
@@ -187,24 +187,6 @@ services:
|
||||
labels:
|
||||
- "com.centurylabs.watchtower.enable=false"
|
||||
|
||||
github-runner:
|
||||
container_name: github_runner
|
||||
image: myoung34/github-runner:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- ACCESS_TOKEN=${GITHUB_RUNNER_ACCESS_TOKEN}
|
||||
- REPO_URL=https://github.com/ginnoir/homelabstack
|
||||
- RUNNER_NAME=valhalla
|
||||
- RUNNER_LABELS=self-hosted,Linux,x64
|
||||
- RUNNER_SCOPE=repo
|
||||
- DOCKER_GROUP_GID=1001
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- /home/ginnoir/valhalla-lab:/valhalla-lab
|
||||
- /config/caddy:/config/caddy
|
||||
labels:
|
||||
- "com.centurylabs.watchtower.enable=false"
|
||||
|
||||
watchtower:
|
||||
container_name: watchtower
|
||||
image: containrrr/watchtower:latest
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#
|
||||
# The Caddyfile itself is unchanged — upstreams are service names that resolve
|
||||
# over edge. It is bind-mounted from /config/caddy/Caddyfile and hot-reloaded
|
||||
# by the GitHub Actions runner on push (deploy.yml).
|
||||
# by the Gitea Actions runner on push (.gitea/workflows/deploy-caddy.yml).
|
||||
#
|
||||
# serfriz/caddy-cloudflare-ddns bundles caddy-dns/cloudflare (DNS-01 ACME),
|
||||
# caddy-cloudflare-ip (real client IPs behind CF), caddy-dynamicdns.
|
||||
|
||||
Reference in New Issue
Block a user