When gluetun is unhealthy, re-dig the WireGuard peer hostname, update wg0.conf Endpoint, recreate gluetun then qbittorrent, and verify the VPN exit IP. Keeps torrent path alive when provider endpoints rotate.
184 lines
8.6 KiB
Markdown
184 lines
8.6 KiB
Markdown
# Quickstart
|
||
|
||
Homelab host: **`192.168.8.123`** (SER5, Ubuntu 24.04)
|
||
Stack: **`/opt/stack`** · Media: **`/storage`** · User: **`tim`**
|
||
|
||
---
|
||
|
||
## Ansible
|
||
|
||
From a machine with this repo and SSH key `~/.ssh/id_ed25519`:
|
||
|
||
```bash
|
||
cd ansible
|
||
ansible-galaxy collection install -r requirements.yml
|
||
|
||
ansible-playbook site.yml -K # -K only if sudo still asks once; playbook installs NOPASSWD for tim
|
||
```
|
||
|
||
What it does: OS updates, tools (incl. Microsoft `edit`), Docker, AMD/Vulkan, UFW (LAN only), `/opt/stack` + `/storage`, `.env`, `arr-stack.service`, and **tim** as local admin (passwordless sudo, docker/video/render/…, owns stack + storage, permissive GPU nodes).
|
||
|
||
**Code on the server:** `/opt/stack` is a **git clone** of Forgejo `tim/stack` (`stack_deploy_method: git`).
|
||
Day-to-day updates on ser5:
|
||
|
||
```bash
|
||
ssh tim@192.168.8.123
|
||
cd /opt/stack
|
||
git pull
|
||
docker compose up -d # if compose/images changed
|
||
```
|
||
|
||
Ansible can also refresh code: `ansible-playbook site.yml -K --tags stack,deploy` (runs `git fetch` + checkout).
|
||
Local-only (not in git): `.env`, `config/`, `models/**/*.gguf`, `wireguard/wg0.conf`.
|
||
|
||
**Before first full stack start** (on ser5):
|
||
|
||
```bash
|
||
ssh tim@192.168.8.123
|
||
# new login so docker/video/render groups apply
|
||
cd /opt/stack
|
||
# wireguard/wg0.conf must exist (no IPv6; Endpoint = IP not hostname)
|
||
./scripts/download-bonsai-model.sh # ~7.2 GB GGUF
|
||
docker compose build bonsai
|
||
docker compose up -d
|
||
# or: sudo systemctl enable --now arr-stack
|
||
docker compose ps
|
||
```
|
||
|
||
**Compose GPU notes**
|
||
- Keep `devices`/`group_add` only in `docker-compose.override.yml` (not also in the main file).
|
||
- Use **numeric GIDs** for `group_add`, not names (`video`/`render` fail inside the image). Or omit `group_add` if `/dev/dri` is mode `0666`.
|
||
|
||
|
||
Re-run playbook anytime: `ansible-playbook site.yml -K`
|
||
Tags: `--tags common,docker,amd,firewall,stack`
|
||
|
||
---
|
||
|
||
## Remotes
|
||
|
||
LAN URLs (UFW allows **`192.168.8.0/24`**). Replace host if needed.
|
||
|
||
| Service | URL |
|
||
|---------|-----|
|
||
| Jellyfin | http://192.168.8.123:8096 |
|
||
| qBittorrent | http://192.168.8.123:8080 |
|
||
| Prowlarr | http://192.168.8.123:9696 |
|
||
| Sonarr | http://192.168.8.123:8989 |
|
||
| Radarr | http://192.168.8.123:7878 |
|
||
| Bazarr | http://192.168.8.123:6767 |
|
||
| Lidarr | http://192.168.8.123:8686 |
|
||
| Open WebUI | http://192.168.8.123:3000 |
|
||
| Bonsai API | http://192.168.8.123:8081/v1 |
|
||
| OpenHands | http://192.168.8.123:3001 |
|
||
| **Forgejo** | http://192.168.8.123:3002 |
|
||
| Forgejo Git SSH | `ssh://git@192.168.8.123:2222/user/repo.git` |
|
||
|
||
OpenHands only runs with: `docker compose --profile coding up -d` (or `stack_compose_profiles: [coding]` in Ansible).
|
||
Images are from **GHCR** (`ghcr.io/all-hands-ai/...`); the old `docker.all-hands.dev` host often does not resolve.
|
||
|
||
### Manual config (once)
|
||
|
||
**qBittorrent** — temp password in `docker logs qbittorrent`; user `admin`.
|
||
Advanced → Network interface: `tun0`.
|
||
Depends on **gluetun** healthy. If gluetun is unhealthy with `lookup … i/o timeout`, WireGuard is not passing traffic — usually a **stale Endpoint IP** in `wireguard/wg0.conf`.
|
||
|
||
**Auto-heal:** `scripts/gluetun-endpoint-watch.sh` + systemd timer every **5 minutes** (installed by Ansible). When gluetun is not healthy it: digs `WIREGUARD_ENDPOINT_HOST` → updates `Endpoint` in `wg0.conf` → recreates gluetun → recreates qbittorrent → tests exit IP.
|
||
Manual run: `cd /opt/stack && ./scripts/gluetun-endpoint-watch.sh`
|
||
Logs: `journalctl -u gluetun-endpoint-watch.service -n 50`
|
||
Set host in `.env`: `WIREGUARD_ENDPOINT_HOST=las-183-wg.whiskergalaxy.com`
|
||
|
||
Default save path + categories (WebUI):
|
||
|
||
1. **Tools → Options → Downloads**
|
||
- Default Save Path: `/data/torrents`
|
||
- Keep “Keep incomplete torrents in” off (or a subfolder under `/data/torrents` if you prefer).
|
||
2. **Categories** (same Options → Downloads page, or right‑click empty area in the left “Categories” panel → **Add category**):
|
||
|
||
| Category name | Save path |
|
||
|---------------|-----------|
|
||
| `movies` | `/data/torrents/movies` |
|
||
| `tv` | `/data/torrents/tv` |
|
||
| `music` | `/data/torrents/music` |
|
||
|
||
For each: **Add category** → name exactly `movies` / `tv` / `music` (lowercase, matches Sonarr/Radarr/Lidarr) → set **Save path** to the path above → OK.
|
||
|
||
3. Sonarr/Radarr/Lidarr download-client “Category” fields must match those names so torrents land in the right folder for hardlinks.
|
||
|
||
**Sonarr / Radarr / Lidarr** — Download client qBittorrent: host **`gluetun`**, port **`8080`**, categories `tv` / `movies` / `music`.
|
||
Root folders: `/data/media/tv`, `/data/media/movies`, `/data/media/music` (hardlinks on).
|
||
|
||
**Prowlarr** — Add indexers. Apps: Sonarr `http://sonarr:8989`, Radarr `http://radarr:7878`, Lidarr `http://lidarr:8686` + each API key (*Settings → General*).
|
||
|
||
**FlareSolverr** (Cloudflare / JS challenges for some indexers) — container `flaresolverr` on `arr-net`, no public port by default.
|
||
Prowlarr → **Settings → Indexers** (or **Settings → Connect** / FlareSolverr section depending on version) → add FlareSolverr host:
|
||
|
||
- Host: `http://flaresolverr:8191`
|
||
- Then enable FlareSolverr on indexers that show Cloudflare errors.
|
||
|
||
Does **not** solve every captcha (Turnstile/hCaptcha image puzzles, etc.). Prefer indexers without CF; paid APIs (2captcha / CapSolver) are optional external services, not a free self-hosted magic box.
|
||
|
||
**Bazarr** — Sonarr/Radarr URLs + API keys; paths `/data/media/tv`, `/data/media/movies`.
|
||
|
||
**Jellyfin** — Libraries: Movies `/data/media/movies`, Shows `/data/media/tv`, Music `/data/media/music`.
|
||
|
||
**Open WebUI** — Create admin on first visit. Model connection is pre-set to Bonsai (`http://bonsai:8080/v1`). Enable web search in chat if desired. RAG: Workspace → Knowledge (uploads) or files under `/workspace`.
|
||
|
||
**Bonsai** — No UI login. Smoke test: `curl -s http://192.168.8.123:8081/v1/models`. Needs GGUF in `models/bonsai/` and `docker compose build bonsai` once.
|
||
|
||
**OpenHands** — On the server (`/opt/stack`): `docker compose --profile coding up -d openhands`.
|
||
UI: http://192.168.8.123:3001
|
||
|
||
**LLM (required once)**
|
||
Settings → Advanced: base URL `http://bonsai:8080/v1`, API key `sk-local-bonsai`, model `openai/<id from /v1/models>`.
|
||
|
||
**Connect to Forgejo (pick A and/or B)**
|
||
|
||
**A — Provider (browse / open repos in OpenHands)**
|
||
1. Forgejo → avatar → **Settings → Applications** → generate a token (scopes: `read:repository`, `write:repository`, `read:user` — or full access on a trusted LAN).
|
||
2. OpenHands → **Settings → Git / Integrations** (or **Secrets** / provider list):
|
||
- Provider: **Forgejo** or **Gitea** (same API)
|
||
- Base URL: `http://forgejo:3000` (Docker DNS on `arr-net`; use this from OpenHands)
|
||
From a browser on your PC you use `http://192.168.8.123:3002` — that is *not* what the OpenHands container should use.
|
||
- Token: paste the Forgejo token
|
||
3. Start a conversation → choose repository → `tim/stack`.
|
||
|
||
**B — Workspace mount (most reliable for editing + git push)**
|
||
Compose already mounts `${AI_WORKSPACE_DIR}` (default `/opt/stack/workspace`) into the agent sandbox as `/workspace`.
|
||
On the **server**:
|
||
|
||
```bash
|
||
# HTTPS with token (one-time; token in URL — prefer SSH below)
|
||
# git clone http://tim:TOKEN@192.168.8.123:3002/tim/stack.git /opt/stack/workspace/stack
|
||
|
||
# SSH (key already on Forgejo for user tim)
|
||
git clone ssh://git@192.168.8.123:2222/tim/stack.git /opt/stack/workspace/stack
|
||
```
|
||
|
||
Then in OpenHands: work in `/workspace/stack` (or set the conversation workspace to that path).
|
||
Agent can `git commit` / `git push` if the host clone has credentials (SSH agent or stored remote URL).
|
||
|
||
If OpenHands cannot reach `forgejo` by name, ensure both services are on `arr-net` (`docker compose up -d forgejo openhands`).
|
||
|
||
**Forgejo** — http://192.168.8.123:3002
|
||
If connection refused: crash-loop from dual SSH; compose uses `START_SSH_SERVER=false`. Recreate: `docker compose up -d forgejo --force-recreate`.
|
||
|
||
---
|
||
|
||
## Locals
|
||
|
||
Bound to loopback on the host (not LAN-reachable as published):
|
||
|
||
| Service | URL / path |
|
||
|---------|------------|
|
||
| SearXNG | http://127.0.0.1:8888 |
|
||
|
||
Open WebUI talks to SearXNG on the Docker network (`http://searxng:8080`); you only need the loopback URL for direct browser checks on the server.
|
||
|
||
### Manual config
|
||
|
||
**SearXNG** — None for normal use. Settings: `/opt/stack/searxng/settings.yml`. Secret comes from `.env` (`SEARXNG_SECRET`).
|
||
|
||
**WireGuard** — File only: `/opt/stack/wireguard/wg0.conf` (not a web UI). Must be ready before gluetun is healthy.
|
||
|
||
**Host shell** — `ssh tim@192.168.8.123` · stack logs: `docker compose -f /opt/stack/docker-compose.yml logs -f` · unit: `systemctl status arr-stack`.
|