<!-- skill: Home Server | version: 1.0.0 | description: Self-hosting services with Docker Compose — media, backups, reverse proxy | category: System & Productivity -->
## Instructions
- One compose stack per service, each in its own folder with a .env for secrets (never commit .env). Pin image versions (`:latest` moves under you).
- Reverse proxy first (Nginx Proxy Manager or Traefik): one entry point, automatic HTTPS via Let's Encrypt, subdomains per service. Expose as little as possible to the internet — VPN (Tailscale/WireGuard) for admin UIs.
- Storage: media on big slow disks, databases/appdata on SSD; bind-mounts (not anonymous volumes) so backups are just file copies. Back up appdata nightly (restic/duplicati to external/USB), test restores quarterly.
- Updates: watchtower on a schedule (not instant) or manual `docker compose pull && up -d` weekly; read changelogs for databases before major bumps.
- Resources: set memory limits per container on small machines; `docker stats` to find hogs; logs rotation (`max-size: 10m`) or disk fills silently.
- Directory contract: `/srv/<service>/{compose.yaml,.env,appdata/}` + `/mnt/media/{movies,music,photos}` + `/mnt/backups` — every service follows it, or nobody finds anything at 2am.
- Secrets: one `.env` per stack (`chmod 600`), passwords from `openssl rand -base64 24`, never defaults (`admin/admin` gets scanned within hours of exposure); Vaultwarden/Bitwarden self-hosted for the family once 3+ services exist.
- DNS inside the LAN: Pi-hole/AdGuard (ad-blocking + local DNS) + reverse-proxy hostnames (`jellyfin.lan`, `files.lan`) via wildcard cert or per-host entries — IPs in bookmarks rot.
- Power + data safety: Pi/server on a UPS (USB-cable shutdown via `apcupsd`/`nut`), `smartctl -a` monthly on HDDs, scrub schedule for btrfs/ZFS pools.
- Remote access: Tailscale/WireGuard for admin (no port-forwards for dashboards); Cloudflare Tunnel if a public share is genuinely needed — never raw port 80/443 to the Pi.
## Examples
- Media stack: Jellyfin + *arr suite behind Nginx Proxy Manager, /media on HDD, configs on SSD.
- Backup box: restic nightly to USB + monthly offsite copy.
- Family cloud: Nextcloud (files + calendar + photos backup) + Vaultwarden + Pi-hole — the three that replace Big Tech for most homes.
## Workflows
1. Pick service → compose file + .env → `up -d` → reverse-proxy host → test → backup config.
2. Monthly: updates, backup test-restore, disk check.
3. New service intake: needs (ports/volumes/secrets) → compose → proxy host → backup job → docs line in the home-server README.
4. Incident: `docker compose logs --tail=100` → fix → `up -d` → verify proxy + backup integrity before calling it done.
## Templates
- compose skeleton: version, service (image, restart: unless-stopped, env_file, volumes, ports only if needed, mem_limit, logging limits).
```yaml
# /srv/example/compose.yaml — the house pattern (proxy network + limits + log caps)
services:
  app:
    image: lscr.io/linuxserver/app:1.2.3
    restart: unless-stopped
    env_file: .env
    volumes: ["./appdata:/config", "/mnt/media:/media:ro"]
    networks: [proxy]
    mem_limit: 1g
    logging: { driver: json-file, options: { max-size: "10m", max-file: "3" } }
networks:
  proxy: { external: true, name: npm_default }
```
```bash
# weekly update + prune (cron or timer), verify after
docker compose pull && docker compose up -d && docker compose ps
docker image prune -f; df -h / /mnt/media
```
- Backup job card per service: `what (appdata path) / where (USB + offsite) / when (02:00 daily) / restore-tested (date)`.
```bash
# restore drill (quarterly, 30 min — untested backups are wishes)
docker compose stop svc && mv appdata appdata.hold && cp -a /mnt/backups/svc-latest appdata
docker compose up -d && docker compose logs --tail=20 svc && curl -sI https://svc.lan | head -1
# HTTPS check: curl -sI http://svc.lan must 301 to https; cert-expiry alert 14d ahead in Uptime Kuma
```
## Tools
- SARAH shell_run + file tools for compose management; docker-deploy skill for app shipping.
- Diag trio: `docker stats --no-stream`, `df -h`, `docker compose logs --tail=50 ${SVC}` — in that order.
## Advanced
- Reverse-proxy depth: NPM (GUI-simple) vs Traefik (label-driven) — pick one and standardize; wildcard `*.lan` cert via DNS challenge, HTTP→HTTPS redirect global, Authelia/SSO gate for anything without its own auth.
- Storage tiers: SSD (`appdata`, DBs), HDD (`media`, backups), USB (`offsite rotation`); bind-mounts with `:ro` where the container must not write; `noatime` on media mounts to spare disks.
- Database care: Postgres/MySQL in compose get `pg_dump`/mysqldump sidecar timers (file dumps beat volume copies for restore sanity); major version bumps = dump → fresh volume → restore, never auto-pull majors.
- Monitoring that earns its RAM: Uptime Kuma (HTTP checks + push) + `ncdu`/`dust` for disk forensics; alerts to Pushover/ntfy, not email nobody reads.
- Troubleshooting: container exits instantly → `docker compose logs` shows missing env/volume perms (PUID/PGID wrong on linuxserver images); proxy 502 → app not on the proxy network or listening on 127.0.0.1 inside its container (bind 0.0.0.0); disk full overnight → log caps missing + one chatty container (`docker system df -v` finds it); updates broke a DB → roll the image tag back, restore last dump, pin the tag this time.
- Restore drill (quarterly, 30 min): pick one service → stop → move appdata aside → restore from backup → `up -d` → verify login + data → log the date on the backup card. Untested backups are wishes.
- HTTPS-everywhere check: `curl -sI http://svc.lan` must 301→https; HSTS on public hosts; expired-cert alert in Uptime Kuma 14 days ahead.
## Documentation
- docs.docker.com/compose, linuxserver.io images (well-maintained, documented).
- Nginx Proxy Manager: https://nginxproxymanager.com/guide/
- Selfh.st catalog (service picker): https://selfh.st/apps/
