Migration: consolidate Echo6 docs to cortex with full infrastructure cleanup sync
- Documents recent infrastructure cleanup (8 CTs destroyed, 35 DNS records removed, Headscale cleanup) - Adds 24 new runbooks covering Authentik, PeerTube, Meshtastic, RECON, Proxmox, Mailcow, Internet Archive, GPU routing - Adds project documentation for headscale, vaultwarden, peertube, matrix, mmud, advbbs, arr stack - Updates services.md, environment.md, caddy.md, authentik.md to match live infrastructure - Removes 4 deprecated runbook duplicates (canonical versions live in projects/) - Adds .gitignore for binary archives and editor temp files Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
89834796ff
commit
e9231ac24a
93 changed files with 51223 additions and 254 deletions
262
projects/arr-stack-runbook.md
Normal file
262
projects/arr-stack-runbook.md
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
# CC Runbook: Build ARR Media Stack on Proxmox `media` Node
|
||||
|
||||
## Objective
|
||||
|
||||
Build a complete media automation stack on the Proxmox node `media` inside a single Ubuntu VM called `arr`. Each service runs in its own Docker container with a shared bridge network for inter-service communication. All services are exposed on the VM's LAN IP on their respective ports.
|
||||
|
||||
**Services:**
|
||||
- Jellyfin (media server, software transcoding — no GPU)
|
||||
- Jellyseer (request management)
|
||||
- Sonarr (TV automation)
|
||||
- Radarr (Movie automation)
|
||||
- Prowlarr (indexer manager)
|
||||
- SABnzbd (Usenet download client)
|
||||
|
||||
---
|
||||
|
||||
## Phase 0: SSH Prereq Check
|
||||
|
||||
**CRITICAL — Do this first. Do not skip.**
|
||||
|
||||
```bash
|
||||
ssh media "echo 'SSH OK to media node'"
|
||||
```
|
||||
|
||||
If this fails, stop and fix SSH access before proceeding. Use sshpass or key auth per `~/.ssh/config`. Cortex is the management host — all commands originate from here.
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Create Ubuntu VM on `media`
|
||||
|
||||
1. SSH to `media` Proxmox node.
|
||||
2. Find the next available VMID: `pvesh get /cluster/nextid`
|
||||
3. Download Ubuntu 24.04 cloud image if not already cached:
|
||||
- URL: `https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img`
|
||||
- Store in appropriate Proxmox storage.
|
||||
4. Create a VM named `arr` with:
|
||||
- **Network:** bridged to the LAN bridge (likely `vmbr0`)
|
||||
- **Resource allocation:** Decide based on the combined needs of all six services. Jellyfin (software transcoding) and SABnzbd (decompression) are the heaviest. Sonarr/Radarr/Prowlarr/Jellyseer are lightweight. Size the VM accordingly — suggest at minimum 4 cores and 8GB RAM, but use your judgment.
|
||||
- **Disk:** 30GB for OS + container configs (media lives on NFS)
|
||||
- Cloud-init configured with:
|
||||
- Default user: `zvx`
|
||||
- SSH key from cortex (discover from `~/.ssh/id_rsa.pub` or equivalent)
|
||||
- Networking: DHCP or static — check the pattern of other VMs on this node and match it
|
||||
5. Start the VM, wait for boot, discover and record its LAN IP.
|
||||
6. Verify SSH from cortex → arr VM works.
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Base System Setup on `arr` VM
|
||||
|
||||
SSH into the `arr` VM:
|
||||
|
||||
1. `apt update && apt upgrade -y`
|
||||
2. Install Docker + Docker Compose via the official Docker apt repo for Ubuntu.
|
||||
3. Install NFS client: `apt install -y nfs-common`
|
||||
4. Install Tailscale and join the tailnet:
|
||||
- `curl -fsSL https://tailscale.com/install.sh | sh`
|
||||
- `tailscale up` — use an auth key if available. Check how other VMs joined (look at Headscale config if self-hosted).
|
||||
- Record the Tailscale IP of the `arr` VM.
|
||||
5. Discover appropriate PUID/PGID:
|
||||
- Mount the NFS share temporarily and `ls -ln` to check file ownership.
|
||||
- If no files exist, create a `media` user/group (e.g., PUID=1000, PGID=1000) and ensure NFS permissions align.
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: NFS Mount
|
||||
|
||||
1. **Discover the NFS server:**
|
||||
- The NFS export is `/export/arr`, accessible from `100.64.0.0/10` (Tailscale) and `192.168.1.0/24` (LAN).
|
||||
- Find the NFS server IP by checking:
|
||||
- `/etc/fstab` on other VMs on this node
|
||||
- `showmount -e <candidate IPs>` on LAN
|
||||
- Proxmox storage config: `pvesm status` or `/etc/pve/storage.cfg`
|
||||
2. `mkdir -p /mnt/arr`
|
||||
3. `mount -t nfs <NFS_SERVER>:/export/arr /mnt/arr`
|
||||
4. Create subdirectories if they don't exist:
|
||||
```
|
||||
mkdir -p /mnt/arr/{movies,tv,downloads,downloads/complete,downloads/incomplete}
|
||||
```
|
||||
5. Set ownership to discovered PUID:PGID on all subdirs.
|
||||
6. Add to `/etc/fstab` for persistence:
|
||||
```
|
||||
<NFS_SERVER>:/export/arr /mnt/arr nfs defaults,_netdev 0 0
|
||||
```
|
||||
7. Verify: `umount /mnt/arr && mount -a && ls /mnt/arr`
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Docker Containers
|
||||
|
||||
### Setup
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/arr/{jellyfin,jellyseer,sonarr,radarr,prowlarr,sabnzbd}
|
||||
```
|
||||
|
||||
Create a Docker bridge network for inter-service communication:
|
||||
```bash
|
||||
docker network create arr-net
|
||||
```
|
||||
|
||||
### Container Deployment
|
||||
|
||||
Deploy each service as its own standalone container. All containers join `arr-net`. All get `TZ=America/Boise` and the discovered `PUID`/`PGID`.
|
||||
|
||||
**Decide per-container resource limits** (CPU shares, memory limits) based on service needs:
|
||||
- **Heavy:** Jellyfin (transcoding), SABnzbd (decompression) — allocate more CPU/RAM
|
||||
- **Medium:** Sonarr, Radarr — moderate
|
||||
- **Light:** Prowlarr, Jellyseer — minimal
|
||||
|
||||
Use lightweight images (hotio where available, official otherwise).
|
||||
|
||||
#### Jellyfin
|
||||
- Image: `jellyfin/jellyfin:latest`
|
||||
- Container name: `jellyfin`
|
||||
- Port: `8096:8096`
|
||||
- Volumes:
|
||||
- `/opt/arr/jellyfin/config:/config`
|
||||
- `/mnt/arr/movies:/data/movies:ro`
|
||||
- `/mnt/arr/tv:/data/tv:ro`
|
||||
- Network: `arr-net`
|
||||
- Restart: `unless-stopped`
|
||||
|
||||
#### Jellyseer
|
||||
- Image: `fallenbagel/jellyseer:latest`
|
||||
- Container name: `jellyseer`
|
||||
- Port: `5055:5055`
|
||||
- Volumes:
|
||||
- `/opt/arr/jellyseer/config:/app/config`
|
||||
- Network: `arr-net`
|
||||
- Restart: `unless-stopped`
|
||||
|
||||
#### Sonarr
|
||||
- Image: `ghcr.io/hotio/sonarr:latest`
|
||||
- Container name: `sonarr`
|
||||
- Port: `8989:8989`
|
||||
- Volumes:
|
||||
- `/opt/arr/sonarr/config:/config`
|
||||
- `/mnt/arr:/data`
|
||||
- Network: `arr-net`
|
||||
- Restart: `unless-stopped`
|
||||
|
||||
#### Radarr
|
||||
- Image: `ghcr.io/hotio/radarr:latest`
|
||||
- Container name: `radarr`
|
||||
- Port: `7878:7878`
|
||||
- Volumes:
|
||||
- `/opt/arr/radarr/config:/config`
|
||||
- `/mnt/arr:/data`
|
||||
- Network: `arr-net`
|
||||
- Restart: `unless-stopped`
|
||||
|
||||
#### Prowlarr
|
||||
- Image: `ghcr.io/hotio/prowlarr:latest`
|
||||
- Container name: `prowlarr`
|
||||
- Port: `9696:9696`
|
||||
- Volumes:
|
||||
- `/opt/arr/prowlarr/config:/config`
|
||||
- Network: `arr-net`
|
||||
- Restart: `unless-stopped`
|
||||
|
||||
#### SABnzbd
|
||||
- Image: `ghcr.io/hotio/sabnzbd:latest`
|
||||
- Container name: `sabnzbd`
|
||||
- Port: `8080:8080`
|
||||
- Volumes:
|
||||
- `/opt/arr/sabnzbd/config:/config`
|
||||
- `/mnt/arr/downloads:/data/downloads`
|
||||
- Network: `arr-net`
|
||||
- Restart: `unless-stopped`
|
||||
|
||||
### Volume Mapping Design
|
||||
|
||||
Sonarr and Radarr both map `/mnt/arr:/data` so hardlinks/atomic moves work between `/data/downloads/complete` and `/data/movies` or `/data/tv` without cross-filesystem copies. This is critical for avoiding double disk usage.
|
||||
|
||||
### Verify
|
||||
|
||||
All six containers are running: `docker ps`
|
||||
Curl each service on localhost to confirm they respond on their expected ports.
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: Authentik OIDC Setup
|
||||
|
||||
**Discovery:** Find the Authentik instance.
|
||||
- Check Caddy config on `utility` for an existing Authentik route (likely `auth.echo6.co` or `authentik.echo6.co`).
|
||||
- Discover the Authentik API URL and obtain/create an API token from Authentik's docker-compose environment or admin API.
|
||||
|
||||
### Jellyfin OIDC
|
||||
1. Create OAuth2/OpenID Provider in Authentik:
|
||||
- Name: `jellyfin`, Client type: Confidential
|
||||
- Redirect URI: `https://jellyfin.echo6.co/sso/OID/redirect/Authentik`
|
||||
- Scopes: `openid profile email`
|
||||
- Signing key: use existing or create
|
||||
2. Create Application: Name `Jellyfin`, slug `jellyfin`, attach provider.
|
||||
3. Record Client ID + Secret.
|
||||
4. Install SSO-Auth plugin in Jellyfin and configure with Authentik OIDC details (discovery URL, client ID, secret).
|
||||
|
||||
### Jellyseer OIDC
|
||||
1. Create OAuth2/OpenID Provider in Authentik:
|
||||
- Name: `jellyseer`, Client type: Confidential
|
||||
- Redirect URI: `https://requests.echo6.co/api/v1/auth/oidc-callback` (verify actual callback path from Jellyseer docs)
|
||||
- Scopes: `openid profile email`
|
||||
2. Create Application: Name `Jellyseer`, slug `jellyseer`, attach provider.
|
||||
3. Record Client ID + Secret.
|
||||
4. Configure Jellyseer OIDC via its settings.
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: Caddy Reverse Proxy on `utility`
|
||||
|
||||
SSH to `utility`. Discover the Caddyfile location and how Caddy is managed (docker, systemd, etc.).
|
||||
|
||||
Add entries using the **Tailscale IP** of the `arr` VM as the upstream:
|
||||
|
||||
```
|
||||
jellyfin.echo6.co {
|
||||
reverse_proxy <ARR_TAILSCALE_IP>:8096
|
||||
}
|
||||
|
||||
requests.echo6.co {
|
||||
reverse_proxy <ARR_TAILSCALE_IP>:5055
|
||||
}
|
||||
```
|
||||
|
||||
**Do NOT expose Sonarr, Radarr, Prowlarr, or SABnzbd via Caddy.** Those are internal-only, accessible via Tailscale or LAN.
|
||||
|
||||
Reload Caddy.
|
||||
|
||||
---
|
||||
|
||||
## Phase 7: GoDaddy DNS
|
||||
|
||||
**Discovery:** Check if GoDaddy API key/secret exists on cortex or utility. Look at how existing `echo6.co` subdomains are configured for the pattern.
|
||||
|
||||
Create A records (via API if available, otherwise output for manual creation):
|
||||
|
||||
| Type | Name | Value | TTL |
|
||||
|------|------|-------|-----|
|
||||
| A | `jellyfin` | Public IP of Caddy/utility (discover) | 600 |
|
||||
| A | `requests` | Public IP of Caddy/utility (discover) | 600 |
|
||||
|
||||
These are publicly exposed WITHOUT Tailscale. Caddy handles TLS via Let's Encrypt. The upstream uses the Tailscale IP but DNS points to the public-facing Caddy IP.
|
||||
|
||||
---
|
||||
|
||||
## Phase 8: Validation
|
||||
|
||||
1. From `arr` VM, curl all six services on localhost (ports 8096, 5055, 8989, 7878, 9696, 8080)
|
||||
2. `curl -sI https://jellyfin.echo6.co` → 200 with valid TLS
|
||||
3. `curl -sI https://requests.echo6.co` → 200 with valid TLS
|
||||
4. Authentik OIDC login works for both Jellyfin and Jellyseer
|
||||
5. NFS persists after reboot: `reboot`, wait, `df -h /mnt/arr`
|
||||
6. All containers auto-start after reboot: `docker ps` shows all six running
|
||||
|
||||
---
|
||||
|
||||
## Important Notes
|
||||
|
||||
- **Do NOT configure** Prowlarr indexers, Sonarr/Radarr API connections, or SABnzbd Usenet provider credentials. That will be done in a separate prompt.
|
||||
- **All discovery steps are intentional** — do not hardcode IPs or paths. Find them dynamically from the running infrastructure.
|
||||
- **If any phase fails, stop and report the error.** Do not skip phases.
|
||||
Loading…
Add table
Add a link
Reference in a new issue