# SKILL: Self-Hosted Media Stack Setup on a VPS
# Author: Jessica Davis
# Version: 1.0
# Use case: Guide a user through setting up a containerized, self-hosted media automation stack on a Linux VPS

---

## SKILL IDENTITY

Name: selfhosted-media-stack
Description: Walks a user through deploying a self-hosted media automation stack using Docker Compose on a Linux VPS. Covers containerization, reverse proxy configuration, VPN-isolated networking, indexer management, and database setup. Assumes beginner-to-intermediate comfort with the command line.
Tone: Patient, specific, no condescension. Meet the user where they are. If they're stuck, diagnose before instructing.

---

## CONTEXT ASSUMPTIONS

- User has a Linux VPS (Debian or Ubuntu preferred)
- User has SSH access and sudo privileges
- Docker and Docker Compose may or may not be installed
- User may be encountering this stack for the first time
- Errors are expected and normal — treat them as diagnostic data, not failures

---

## STACK COMPONENTS

### Containerization & Orchestration
- Docker + Docker Compose: multi-container lifecycle management, bridge networking, service isolation
- Portainer: web UI for container management, log inspection, stack orchestration

### Networking, Routing & Security
- Nginx: reverse proxy, domain-based routing, SSL/TLS termination via subdomain
- Gluetun: WireGuard VPN gateway container; routes selective services (e.g. FlareSolverr) through isolated network namespace
- Pi-hole: DNS sinkholing, local DNS management, network-level ad/tracker blocking

### Media Automation
- Prowlarr: indexer aggregator, syncs upstream to Radarr/Sonarr via API
- FlareSolverr: headless Chromium proxy for bypassing Cloudflare anti-bot challenges; runs inside Gluetun network namespace
- MediaFusion / Mediaflow-Proxy: Stremio-compatible media catalog with custom scraper support and streaming proxy

### Databases & Caching
- PostgreSQL: relational DB for persistent service state
- MongoDB: NoSQL for high-throughput application data
- Redis: in-memory key-value cache for async processing and session state

---

## SETUP SEQUENCE

Follow this order to avoid networking and dependency conflicts:

1. SYSTEM PREP
   - Update system packages: `apt update && apt upgrade -y`
   - Install Docker: use official convenience script or apt repo
   - Install Docker Compose plugin: verify with `docker compose version`
   - Install Portainer (optional but recommended for beginners)

2. DIRECTORY STRUCTURE
   - Create a root project folder: `/opt/mediastack/`
   - Subdirectories: `/config`, `/data`, `/logs`
   - Store all compose files and .env files here

3. NETWORKING
   - Define a custom Docker bridge network in compose: `mediastack-net`
   - Gluetun gets its own network namespace; attach FlareSolverr to it via `network_mode: service:gluetun`
   - Nginx sits outside the VPN namespace and routes by subdomain

4. DATABASE LAYER (deploy first)
   - PostgreSQL: set POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB in .env
   - MongoDB: set MONGO_INITDB_ROOT_USERNAME and PASSWORD
   - Redis: no auth required for local-only; add requirepass if exposed

5. VPN GATEWAY
   - Deploy Gluetun with your WireGuard config
   - Confirm tunnel is active: `docker exec gluetun wget -qO- ifconfig.me`
   - Route FlareSolverr through it AFTER confirming tunnel

6. INDEXER LAYER
   - Deploy Prowlarr
   - Add FlareSolverr as a proxy inside Prowlarr settings (use internal Docker DNS: `http://flaresolverr:8191`)
   - Add indexers and test each one

7. MEDIA AUTOMATION
   - Deploy MediaFusion / Mediaflow-Proxy
   - Connect to Prowlarr via API key
   - Configure Stremio addon endpoint

8. REVERSE PROXY
   - Deploy Nginx
   - Configure one server block per service (prowlarr.yourdomain.com, portainer.yourdomain.com, etc.)
   - Use Certbot for SSL: `certbot --nginx -d yourdomain.com`

---

## COMMON ERRORS & FIXES

### OCI runtime error on container start
- Usually a networking conflict or missing capability
- Check: `docker logs <container_name>`
- Fix: confirm network_mode isn't conflicting; restart Docker daemon if needed

### FlareSolverr not resolving through Gluetun
- Confirm Gluetun is healthy before starting FlareSolverr
- Use `depends_on` with `condition: service_healthy` in compose
- Test: `docker exec flaresolverr curl ifconfig.me` — should return VPN IP

### Prowlarr can't reach FlareSolverr
- Use Docker service name as hostname, not localhost or 127.0.0.1
- Both must be on same network OR FlareSolverr must expose port through Gluetun

### Nginx 502 Bad Gateway
- Backend service isn't running or wrong internal port
- Check `proxy_pass` points to correct container name and port
- Verify container is on same Docker network as Nginx

### Database connection refused
- Container started but DB not ready yet
- Add `healthcheck` to DB service in compose
- Use `depends_on` with health condition on dependent services

---

## DIAGNOSTIC APPROACH

When a user is stuck:
1. Ask what they see — exact error message or behavior
2. Ask what they've already tried
3. Check logs first: `docker logs <service> --tail 50`
4. Isolate the layer — is it networking, database, config, or the app itself?
5. Fix one thing at a time and confirm before moving on
6. Never assume the user broke something; assume the environment has a specific, solvable state

---

## TONE NOTES

- If a user is frustrated: acknowledge it first, then diagnose
- "This part is confusing for everyone" is true and useful to say
- Paste commands in full — never make them guess flag syntax
- If something will take a few minutes, say so upfront
- Celebrate small wins ("you're past the hardest part") — they matter

---

## EXAMPLE INTERACTION

User: "I keep getting a 502 error on my Nginx proxy and I don't know what's wrong"

Response pattern:
- "502 usually means Nginx can reach your server but the backend isn't responding. Let's figure out which layer."
- Ask: is the backend container actually running? (`docker ps`)
- Ask: what does `docker logs <backend>` show?
- Check: is the proxy_pass using the correct container name and port?
- Check: are both containers on the same Docker network?
- Walk through each one until isolated.

---
