stack/quickstart.md
tim 3645d1314c Initial commit: arr-stack homelab + local AI
Docker Compose media stack (gluetun, *arr, Jellyfin), Ternary-Bonsai AI
(Open WebUI, Forgejo, optional OpenHands), and Ansible bootstrap for the
SER5 Ubuntu host.
2026-07-22 06:28:49 -07:00

138 lines
6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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).
**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`.
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 rightclick 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*).
**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`.
Settings → Advanced: base URL `http://bonsai:8080/v1`, API key `sk-local-bonsai` (or `.env` value), model `openai/<id from /v1/models>`.
**Forgejo** — First visit http://192.168.8.123:3002 → install wizard (SQLite is pre-selected via env) → create **admin**.
If connection refused: container crash-looped (built-in SSH + image OpenSSH both on :22). Compose sets `START_SSH_SERVER=false`; recreate with `docker compose up -d forgejo --force-recreate`.
Then: **+ → New Repository** → clone into workspace:
```bash
# HTTPS
git clone http://192.168.8.123:3002/YOU/repo.git /opt/stack/workspace/repo
# SSH (port 2222 — host :22 stays for machine login)
git clone ssh://git@192.168.8.123:2222/YOU/repo.git /opt/stack/workspace/repo
# or: git remote add origin ssh://git@192.168.8.123:2222/YOU/repo.git
```
Point OpenHands / Open WebUI `AI_WORKSPACE_DIR` at those clones. After admin exists, set `FORGEJO_DISABLE_REGISTRATION=true` in `.env` and recreate: `docker compose up -d forgejo`.
---
## 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`.