Docker Compose media stack (gluetun, *arr, Jellyfin), Ternary-Bonsai AI (Open WebUI, Forgejo, optional OpenHands), and Ansible bootstrap for the SER5 Ubuntu host.
6 KiB
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:
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):
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_addonly indocker-compose.override.yml(not also in the main file). - Use numeric GIDs for
group_add, not names (video/renderfail inside the image). Or omitgroup_addif/dev/driis mode0666.
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):
-
Tools → Options → Downloads
- Default Save Path:
/data/torrents - Keep “Keep incomplete torrents in” off (or a subfolder under
/data/torrentsif you prefer).
- Default Save Path:
-
Categories (same Options → Downloads page, or right‑click empty area in the left “Categories” panel → Add category):
Category name Save path movies/data/torrents/moviestv/data/torrents/tvmusic/data/torrents/musicFor each: Add category → name exactly
movies/tv/music(lowercase, matches Sonarr/Radarr/Lidarr) → set Save path to the path above → OK. -
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:
# 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.