Files
truenas-stacks/docs/operations/STACK_STANDARDS.md
Fizzlepoof 1b4838504b docs(gitea): document internal SSH remote
Record the working internal Gitea SSH remote as ssh://git@10.5.1.6:2222/<owner>/<repo>.git and replace stale HTTPS/gitea.paccoco.com examples for PD/NOMAD operations. Port 2222 is SSH, while the web UI remains on port 3002.
2026-05-13 17:15:39 +00:00

126 lines
3.6 KiB
Markdown

# Homelab Stack Standards
## Repository Root
`/mnt/docker-ssd/docker/compose`
Remotes:
- GitHub: `git@github.com:Paccoco/truenas-stacks.git`
- Gitea web: `http://10.5.1.6:3002/fizzlepoof/truenas-stacks`
- Gitea SSH: `ssh://git@10.5.1.6:2222/fizzlepoof/truenas-stacks.git`
Use the internal Gitea SSH remote from PD/NOMAD. Port `2222` is SSH, not HTTP.
## Stack Layout
Each production stack lives in a subdirectory of the repo root.
Each stack contains:
- `docker-compose.yaml` — required
- `.env` — required (gitignored)
- `.env.example` — committed with placeholder values
- optional init scripts (e.g. `initdb/`, `initdb-mariadb/`)
## Deployment Order
1. `databases/` — must be up before anything else
2. `infrastructure/` — newt creates the pangolin network
3. All other stacks in any order
## Deployment Workflow
Validate before deploying:
```bash
cd /mnt/docker-ssd/docker/compose/<stack>
docker compose --env-file .env config
```
Deploy:
```bash
docker compose --env-file .env up -d
```
Teardown a single service:
```bash
docker compose --env-file .env down <service>
```
## Networks
### ix-databases_shared-databases
Created by the `databases` stack with `name: ix-databases` at top level. Any stack connecting to shared databases must declare:
```yaml
networks:
ix-databases_shared-databases:
external: true
```
### pangolin
Created by `newt` at runtime. Any externally exposed service must declare:
```yaml
networks:
pangolin:
external: true
```
## Storage Standards
### Local SSD: `/mnt/docker-ssd`
Use `/mnt/docker-ssd/docker/appdata/<service>` for:
- Postgres / MariaDB / Redis data
- SQLite-backed apps
- Apps that chown aggressively on startup
- GPU/model caches
- Write-heavy logs, cache, state
### TrueNAS Pool (tank): `/mnt/tank`
Use `/mnt/tank/docker/appdata/<service>` for:
- General appdata
- Configs
- Uploads
- Non-permission-sensitive state
### Unraid NFS: `/mnt/unraid`
- `/mnt/unraid/data/media/` — media libraries
- `/mnt/unraid/immich` — Immich photo/video storage
## Volume Rules
- no anonymous volumes
- no ad-hoc home directory paths
- all binds use standardized production paths
- evaluate SSD vs tank vs Unraid before deploying any new service
- apps known to have permission issues on NFS go on SSD
## Known Permission Quirks
- `shlink` data directory must be chmod 777 — runs as non-root user
- `immich-ml` cache goes to `/mnt/docker-ssd/docker/appdata/immich-ml` (separate from immich-server)
- `donetick` requires `/mnt/tank/docker/appdata/donetick/config/selfhosted.yaml` — env vars alone insufficient
## Secrets Management
- `.env` files are gitignored
- `.env.example` files are committed with placeholder values
- Database passwords: `openssl rand -hex 24`
- JWT secrets: `openssl rand -hex 32`
## Healthcheck Patterns
Always check available tools before writing healthchecks:
```bash
sudo docker exec <container> sh -c "which curl; which wget" 2>&1
```
- Has curl: `["CMD-SHELL", "curl -sf http://127.0.0.1:<port>/ || exit 1"]`
- Has wget only: `["CMD-SHELL", "wget -qO- http://127.0.0.1:<port>/ >/dev/null 2>&1 || exit 1"]`
- Has neither: `["CMD-SHELL", "cat /proc/net/tcp6 | grep -q <HEX_PORT> || exit 1"]`
- Redirecting endpoint: `["CMD-SHELL", "curl -sfL http://127.0.0.1:<port>/ || exit 1"]`
Common hex port values: 8080=1F90, 3003=BBB, 3000=BB8, 8181=1FF5, 5055=13BF, 2021=7E5
After changing a healthcheck, must `down` then `up` (not just restart).
## Git Workflow
```bash
cd /mnt/docker-ssd/docker/compose
git add .
git commit -m "description"
git push ssh://git@10.5.1.6:2222/fizzlepoof/truenas-stacks.git main
```
All `.env` files are gitignored. Only compose files, `.env.example`, and init scripts are committed.