docs+ops: rewrite CLAUDE.md, skills, runner for post-split topology

Post-monolith documentation and ops cleanup:

- CLAUDE.md rewritten end-to-end. Documents the 11 Portainer-managed stacks
  + raw-compose management plane, the single shared `edge` network, the
  SSD-vs-ZFS bind-mount tiering, the four deployment channels (git push
  for app stacks, runner for Caddyfile, apply-compose.ps1 for mgmt plane /
  Caddyfile / vault), and the fact that the repo is now canonical.
- homelab-apply skill rewritten for the new channels — no more `dc up -d`,
  no more monolith.
- homelab-ssh skill rewritten — no `dc` alias, plain `docker` against
  container names; per-stack compose ops via /data/compose/<id>/...
- homelab-sync skill + sync-prod.ps1 retired. The repo is canonical now;
  pulling from prod is the wrong direction.
- .github/workflows/deploy.yml: drop the dc up -d steps, gate on
  paths:[Caddyfile, .github/workflows/deploy.yml], reload caddy via
  `docker exec` (no longer through compose).
- apply-compose.ps1: drop -Compose and -DevStack flags; -Caddy now reloads
  via `docker exec caddy` (Caddy is in its own Portainer stack now).

No live container is touched by this commit. The runner workflow is
currently disabled at the repo level; re-enabling it makes Caddyfile pushes
auto-deploy again.
This commit is contained in:
ginnoir
2026-06-04 18:17:16 -05:00
parent cee43aa733
commit f995014d48
7 changed files with 221 additions and 221 deletions
+56 -20
View File
@@ -1,6 +1,6 @@
---
name: homelab-ssh
description: Connect to and run commands on the valhalla homelab server (ginnoir@valhalla) over SSH, including docker-compose operations through the `dc` alias. Use when inspecting the live stack, tailing logs, restarting a service, reloading Caddy, or checking container/file state on the server.
description: Connect to and run commands on the valhalla homelab server (ginnoir@valhalla) over SSH. Use when inspecting the live per-domain stacks, tailing logs, restarting a container, reloading Caddy, or checking container/network/volume state. There is no `dc` alias anymore — services are addressed by container name with plain `docker` commands.
---
# homelab-ssh
@@ -15,37 +15,73 @@ Always pass `-o BatchMode=yes` so a missing key or password prompt fails fast in
ssh -o BatchMode=yes -o ConnectTimeout=8 ginnoir@valhalla "<command>"
```
The first `Bash` command of a session is gated by a GateGuard hook — state the user request + what the command does, then retry.
The first `Bash` of a session is gated by a GateGuard hook — state the user request + what the command does, then retry.
## docker-compose via the `dc` alias
## The estate is now 11 Portainer-managed stacks + the management plane
The stack uses Docker Compose v2 (`docker compose` plugin, installed via apt). On the server, `dc` is aliased in `~/.bashrc` to:
| Stack (Portainer project name) | Containers |
|---|---|
| `proxy` | `caddy` |
| `media` | `deluge`, `nzbget`, `sonarr`, `radarr`, `bazarr`, `prowlarr`, `overseerr`, `tautulli`, `qbittorrent`, `whisparr`, `stash` |
| `foundry` | `foundry`, `foundry2`, `5etools` |
| `owncloud` | `owncloud_server`, `owncloud_mariadb`, `owncloud_redis` |
| `resume` | `resume`, `postgres_resume`, `minio_resume`, `chrome` |
| `famapp` | `famapp`, `postgres_famapp`, `minio_famapp` |
| `authentik` | `authentik_server`, `authentik_worker`, `postgres_authentik`, `redis_authentik` |
| `notify` | `ntfy`, `freshrss`, `vigilant` |
| `monitoring` | `uptime_kuma`, `homarr` |
| `remote` | `hbbr`, `hbbs` |
| `dev` | `gitea`, `code_server`, `bookstack`, `mariadb_bookstack`, `dbx`, `registry`, `postgres_gitea`, `postgres_plane`, `redis_plane`, `plane_minio`, `plane_api`, `plane_web`, `plane_worker`, `plane_beat`, `plane_create_bucket` |
| management plane (raw compose at `~/valhalla-lab/portainer-compose.yml`, project name `valhalla-lab`) | `portainer`, `vault`, `github_runner`, `watchtower` |
```
docker compose -f ~/htpc-download-box/docker-compose.yml --env-file ~/htpc-download-box/.env
```
## Common ops by container name
It's a shell alias, so it only exists in an interactive shell. Invoke it with `bash -ic`:
There's no umbrella `dc` alias. Use plain `docker`:
```powershell
ssh ginnoir@valhalla "bash -ic 'dc ps'"
ssh ginnoir@valhalla "bash -ic 'dc logs -f --tail=200 <service>'"
ssh ginnoir@valhalla "bash -ic 'dc restart <service>'"
ssh ginnoir@valhalla "bash -ic 'dc up -d'"
ssh ginnoir@valhalla "bash -ic 'dc exec caddy caddy reload --config /etc/caddy/Caddyfile'"
ssh -o BatchMode=yes ginnoir@valhalla "docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Label \"com.docker.compose.project\"}}'"
ssh -o BatchMode=yes ginnoir@valhalla "docker logs --tail 200 -f <container>"
ssh -o BatchMode=yes ginnoir@valhalla "docker restart <container>"
ssh -o BatchMode=yes ginnoir@valhalla "docker exec <container> <command>"
ssh -o BatchMode=yes ginnoir@valhalla "docker exec caddy caddy reload --config /etc/caddy/Caddyfile"
```
Use the compose **service name** (`sonarr`, `caddy`, `app`, …). Without `bash -ic`, `dc` is "command not found".
## Per-stack `docker compose` operations
Each Portainer git stack lives at `/data/compose/<id>/stacks/<domain>/docker-compose.yml` (Portainer chooses the `<id>` when the stack is created). To inspect a stack's resolved config or run a one-off compose op:
```powershell
# Pick the stack file from its label
ssh ginnoir@valhalla "docker inspect <container> --format '{{ index .Config.Labels \"com.docker.compose.project.config_files\"}}'"
# Then point compose at it
ssh ginnoir@valhalla "docker compose -f /data/compose/14/stacks/owncloud/docker-compose.yml ps"
```
For management plane only:
```powershell
ssh ginnoir@valhalla "docker compose -f ~/valhalla-lab/portainer-compose.yml --env-file ~/valhalla-lab/.env ps"
```
## Key paths on the server
- `~/htpc-download-box/docker-compose.yml`, `~/htpc-download-box/.env` — stack definition
- `/config/caddy/Caddyfile` — Caddy config (mounted into the `caddy` container at `/etc/caddy/Caddyfile`)
- `/config/<service>/` — per-service persisted config; `/storage1/` — media & data; plus named Docker volumes
- `~/htpc-download-box/` contains only `docker-compose.yml` and `.env` — the legacy cruft has been cleaned out
- `~/valhalla-lab/portainer-compose.yml` + `~/valhalla-lab/.env` — management plane (raw compose, applied via `apply-compose.ps1 -Portainer`)
- `/config/caddy/Caddyfile` — Caddy config (mounted into `caddy` at `/etc/caddy/Caddyfile`)
- `/config/<service>/` — per-service persisted config (SSD tier: configs + Postgres + Redis)
- `/storage1/labdata/<service>/` — high-density data on the ZFS pool (MinIO buckets, registry layers, gitea repos)
- `/storage1/` — media library (Books, Tabletop, etc.)
- `/data/compose/<id>/stacks/<domain>/` — Portainer's working copy of each stack (Portainer auto-pulls these from git every 5 min)
## Networks
- `edge` — shared reverse-proxy network. `caddy` + every service Caddy proxies. Anything new that needs to be public must join this.
- `<domain>` (e.g. `media`, `authentik`, `dev`) — per-stack private network for intra-stack traffic (app ↔ its db/cache/minio).
- `portainer_proxy`, `valhalla-lab_default` — management-plane networks (legacy names, kept).
- `bridge`, `host`, `none` — Docker defaults.
## Notes
- Read-only exploration of `/config`, `/storage1`, and container state is fine for answering questions.
- Avoid destructive commands against the live stack without explicit confirmation.
- To push config changes and apply them, use the `homelab-apply` skill; to pull prod config into the repo, use `homelab-sync`.
- For destructive ops (`docker rm -f`, network/volume pruning), get explicit confirmation first.
- To deploy config changes, use the `homelab-apply` skill (per-domain stacks → git push; mgmt plane / Caddyfile → `apply-compose.ps1`).
- `htpc-download-box_*` and `valhalla-lab_*` named volumes still exist as a data safety net from the pre-split monolith — every per-domain stack uses bind mounts now, so those orphans can be reaped once you're confident in the new persistence.