Centralize demo config in .env and add bootstrap-stack.sh
Expand .env.example with ports, AI, Usenet, WireGuard, demo logins, and bootstrap flags. One script creates dirs, downloads the model, starts compose, heals gluetun, and configures SABnzbd from the same file.
This commit is contained in:
parent
56ddb349b4
commit
3bf1726a99
7 changed files with 410 additions and 76 deletions
195
.env.example
195
.env.example
|
|
@ -1,88 +1,167 @@
|
||||||
# Copy to .env and adjust. The compose file reads these values.
|
################################################################################
|
||||||
|
# arr-stack — single config file for a tech demo / fresh host
|
||||||
|
# Copy to .env and edit. Compose + bootstrap scripts read this file.
|
||||||
|
#
|
||||||
|
# Demo credentials (all local UIs): DEMO_USER / DEMO_PASS (default tim / asdfasdf)
|
||||||
|
# Security is intentionally weak for demos. Change before any real exposure.
|
||||||
|
#
|
||||||
|
# Fresh rebuild checklist:
|
||||||
|
# 1. Put a valid wireguard/wg0.conf (Endpoint IP + # EndpointHost = hostname)
|
||||||
|
# 2. cp .env.example .env && edit paths / host IP / usenet keys
|
||||||
|
# 3. ./scripts/bootstrap-stack.sh
|
||||||
|
################################################################################
|
||||||
|
|
||||||
# --- user / group that owns your media (run `id` on the host) ---
|
# =============================================================================
|
||||||
|
# Demo mode & shared login
|
||||||
|
# =============================================================================
|
||||||
|
DEMO_MODE=true
|
||||||
|
DEMO_USER=tim
|
||||||
|
DEMO_PASS=asdfasdf
|
||||||
|
# Used as OpenAI-compatible key for Bonsai / OpenHands / Open WebUI connections
|
||||||
|
OPENAI_API_KEY=sk-local-bonsai
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Host identity & paths
|
||||||
|
# =============================================================================
|
||||||
PUID=1000
|
PUID=1000
|
||||||
PGID=1000
|
PGID=1000
|
||||||
|
TZ=America/Los_Angeles
|
||||||
|
|
||||||
# --- your timezone (https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) ---
|
# Production-style (server):
|
||||||
TZ=Europe/Berlin
|
# CONFIG_DIR=/opt/stack/config
|
||||||
|
# DATA_DIR=/storage
|
||||||
# --- host paths (relative to this folder is fine) ---
|
# BONSAI_MODELS_DIR=/opt/stack/models/bonsai
|
||||||
|
# AI_WORKSPACE_DIR=/opt/stack/workspace
|
||||||
|
# Laptop / relative demo:
|
||||||
CONFIG_DIR=./config
|
CONFIG_DIR=./config
|
||||||
DATA_DIR=./data
|
DATA_DIR=./data
|
||||||
|
BONSAI_MODELS_DIR=./models/bonsai
|
||||||
|
AI_WORKSPACE_DIR=./workspace
|
||||||
|
|
||||||
# --- your LAN subnet, so the qBittorrent WebUI stays reachable through
|
# LAN used by gluetun FIREWALL_OUTBOUND_SUBNETS (qBittorrent WebUI from LAN)
|
||||||
# gluetun's firewall. If your machine is 192.168.1.50, use 192.168.1.0/24
|
LAN_SUBNET=192.168.8.0/24
|
||||||
LAN_SUBNET=192.168.1.0/24
|
# Public hostname/IP other PCs use to open UIs (landing page, Forgejo ROOT_URL, …)
|
||||||
|
STACK_HOST=192.168.8.123
|
||||||
|
|
||||||
# WireGuard peer hostname (used by scripts/gluetun-endpoint-watch.sh when gluetun is unhealthy)
|
# =============================================================================
|
||||||
|
# Published ports (host → service)
|
||||||
|
# =============================================================================
|
||||||
|
LANDING_PORT=80
|
||||||
|
QB_WEBUI_PORT=8080
|
||||||
|
JELLYFIN_PORT=8096
|
||||||
|
SONARR_PORT=8989
|
||||||
|
RADARR_PORT=7878
|
||||||
|
LIDARR_PORT=8686
|
||||||
|
BAZARR_PORT=6767
|
||||||
|
PROWLARR_PORT=9696
|
||||||
|
SABNZBD_PORT=8085
|
||||||
|
OPEN_WEBUI_PORT=3000
|
||||||
|
BONSAI_PORT=8081
|
||||||
|
OPENHANDS_PORT=3001
|
||||||
|
FORGEJO_HTTP_PORT=3002
|
||||||
|
FORGEJO_SSH_PORT=2222
|
||||||
|
SEARXNG_PORT=8888
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# WireGuard / gluetun
|
||||||
|
# =============================================================================
|
||||||
|
# Peer hostname for gluetun-endpoint-watch (must match provider; dig when unhealthy)
|
||||||
WIREGUARD_ENDPOINT_HOST=las-183-wg.whiskergalaxy.com
|
WIREGUARD_ENDPOINT_HOST=las-183-wg.whiskergalaxy.com
|
||||||
WIREGUARD_ENDPOINT_PORT=443
|
WIREGUARD_ENDPOINT_PORT=443
|
||||||
|
# Path to conf mounted into gluetun (create before first up)
|
||||||
|
WIREGUARD_CONF=./wireguard/wg0.conf
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Local AI (Ternary-Bonsai-27B on SER5 PRO / Ryzen 7735HS)
|
# Local AI — Ternary-Bonsai-27B
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
# GGUF weights directory (populated by scripts/download-bonsai-model.sh)
|
|
||||||
BONSAI_MODELS_DIR=./models/bonsai
|
|
||||||
BONSAI_MODEL_PATH=/models/Ternary-Bonsai-27B-Q2_0.gguf
|
BONSAI_MODEL_PATH=/models/Ternary-Bonsai-27B-Q2_0.gguf
|
||||||
|
BONSAI_HF_REPO=prism-ml/Ternary-Bonsai-27B-gguf
|
||||||
|
BONSAI_MAIN_FILE=Ternary-Bonsai-27B-Q2_0.gguf
|
||||||
|
# Download model during bootstrap-stack.sh
|
||||||
|
DOWNLOAD_MODEL=true
|
||||||
|
DOWNLOAD_VISION=true
|
||||||
|
DOWNLOAD_DRAFTER=false
|
||||||
|
|
||||||
# cpu (default) or vulkan — rebuild image after changing:
|
# Inference: vulkan on AMD iGPU; cpu for safe fallback
|
||||||
# BONSAI_BACKEND=vulkan docker compose build --no-cache bonsai
|
BONSAI_BACKEND=vulkan
|
||||||
BONSAI_BACKEND=cpu
|
BONSAI_NGL=99
|
||||||
|
|
||||||
# GPU layers: 0 = CPU only. With vulkan image + /dev/dri, try 99.
|
|
||||||
BONSAI_NGL=0
|
|
||||||
|
|
||||||
# Context size. 16384 is a solid default on 32–64 GB RAM with the Arr stack.
|
|
||||||
# Raise for long docs / repo work; set BONSAI_KV4=1 if you run OOM.
|
|
||||||
BONSAI_CTX=16384
|
BONSAI_CTX=16384
|
||||||
BONSAI_KV4=0
|
BONSAI_KV4=0
|
||||||
|
BONSAI_THREADS=12
|
||||||
# Leave 0 for auto threads, or e.g. 12 on 16-thread 7735HS to leave headroom
|
|
||||||
BONSAI_THREADS=0
|
|
||||||
|
|
||||||
# Sampling (Prism 27B thinking-mode defaults)
|
|
||||||
BONSAI_TEMP=0.7
|
BONSAI_TEMP=0.7
|
||||||
BONSAI_TOP_P=0.95
|
BONSAI_TOP_P=0.95
|
||||||
BONSAI_TOP_K=20
|
BONSAI_TOP_K=20
|
||||||
|
BONSAI_EXTRA_ARGS=
|
||||||
# Host port for llama-server API (OpenAI-compatible at /v1)
|
|
||||||
BONSAI_PORT=8081
|
|
||||||
|
|
||||||
# Open WebUI
|
# Open WebUI
|
||||||
OPEN_WEBUI_PORT=3000
|
|
||||||
OPENAI_API_KEY=sk-local-bonsai
|
|
||||||
# true = login required (recommended); false = open LAN access
|
|
||||||
WEBUI_AUTH=true
|
WEBUI_AUTH=true
|
||||||
|
OPENWEBUI_DEFAULT_MODEL=openai/Ternary-Bonsai-27B-Q2_0.gguf
|
||||||
|
RAG_EMBEDDING_ENGINE=
|
||||||
|
RAG_CHUNK_SIZE=1000
|
||||||
|
RAG_CHUNK_OVERLAP=100
|
||||||
|
|
||||||
# Folder mounted read-only into Open WebUI as /workspace (RAG + file Q&A)
|
# SearXNG
|
||||||
# Point this at your git projects, notes, docs, etc.
|
SEARXNG_SECRET=demo-searxng-secret-change-me
|
||||||
AI_WORKSPACE_DIR=./workspace
|
|
||||||
|
|
||||||
# SearXNG (web search for RAG) — published on localhost only
|
# OpenHands (compose profile: coding)
|
||||||
SEARXNG_PORT=8888
|
OPENHANDS_MODEL=openai/Ternary-Bonsai-27B-Q2_0.gguf
|
||||||
SEARXNG_SECRET=change-me-to-a-long-random-string
|
# true = also start openhands in bootstrap
|
||||||
|
START_OPENHANDS=false
|
||||||
|
|
||||||
# OpenHands coding agent (docker compose --profile coding up -d)
|
# =============================================================================
|
||||||
OPENHANDS_PORT=3001
|
# Forgejo
|
||||||
# LiteLLM-style id; after first start check http://host:8081/v1/models
|
# =============================================================================
|
||||||
OPENHANDS_MODEL=openai/local
|
FORGEJO_DOMAIN=${STACK_HOST}
|
||||||
|
FORGEJO_ROOT_URL=http://${STACK_HOST}:3002/
|
||||||
|
FORGEJO_DISABLE_REGISTRATION=false
|
||||||
|
# First-run admin (set after install or via bootstrap when API allows)
|
||||||
|
FORGEJO_ADMIN_USER=${DEMO_USER}
|
||||||
|
FORGEJO_ADMIN_PASS=${DEMO_PASS}
|
||||||
|
FORGEJO_ADMIN_EMAIL=tim@stack.local
|
||||||
|
|
||||||
# FlareSolverr (Cloudflare helper for Prowlarr; internal only)
|
# Git remote used by server /opt/stack (see stack_deploy_method: git)
|
||||||
|
STACK_GIT_REPO=ssh://git@127.0.0.1:2222/tim/stack.git
|
||||||
|
STACK_GIT_BRANCH=main
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Usenet / NZB (also read by scripts/configure-usenet.sh)
|
||||||
|
# Prefer these names; legacy aliases still work (see configure-usenet.sh)
|
||||||
|
# =============================================================================
|
||||||
|
# NZBgeek indexer (Prowlarr)
|
||||||
|
NZBGEEKUSERNAME=
|
||||||
|
NZBGEEKEMAIL=
|
||||||
|
NZBGEEKAPI=https://api.nzbgeek.info/
|
||||||
|
NZBKEEPKEY=
|
||||||
|
# aliases: NZBGEEK_API_KEY
|
||||||
|
|
||||||
|
# Newshosting
|
||||||
|
NEWSHOSTINGAPI=news.newshosting.com
|
||||||
|
NEWSHOSTINGSSLPORT=563
|
||||||
|
NEWSHOSTINGUSER=
|
||||||
|
HNEWSHOSTINGPASS=
|
||||||
|
NEWSHOSTINGCONNECTIONS=20
|
||||||
|
|
||||||
|
# Tweaknews
|
||||||
|
TWEAKNEWSAPI=news.tweaknews.eu
|
||||||
|
TWEAKNEWSSSLPORT=563
|
||||||
|
TWEAKNEWSLOGIN=
|
||||||
|
TWEAKNEWSPASSWORD=
|
||||||
|
TWEAKNEWSCONNECTIONS=40
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# FlareSolverr
|
||||||
|
# =============================================================================
|
||||||
FLARESOLVERR_LOG_LEVEL=info
|
FLARESOLVERR_LOG_LEVEL=info
|
||||||
FLARESOLVERR_CAPTCHA_SOLVER=none
|
FLARESOLVERR_CAPTCHA_SOLVER=none
|
||||||
|
|
||||||
# SABnzbd (Usenet) — credentials in secrets/.env, not here
|
# =============================================================================
|
||||||
SABNZBD_PORT=8085
|
# Bootstrap behaviour (scripts/bootstrap-stack.sh)
|
||||||
|
# =============================================================================
|
||||||
# Caddy landing page (LAN service map)
|
# compose profiles to enable: empty, or coding
|
||||||
LANDING_PORT=80
|
COMPOSE_PROFILES=
|
||||||
|
# After containers are up, run configure-usenet.sh
|
||||||
# Forgejo — self-hosted Git (web + SSH clone)
|
CONFIGURE_USENET=true
|
||||||
FORGEJO_HTTP_PORT=3002
|
# Recreate gluetun/qbittorrent if unhealthy after start
|
||||||
FORGEJO_SSH_PORT=2222
|
HEAL_GLUETUN=true
|
||||||
FORGEJO_DOMAIN=192.168.8.123
|
# Pull images before up
|
||||||
FORGEJO_ROOT_URL=http://192.168.8.123:3002/
|
COMPOSE_PULL=true
|
||||||
# true after you create the admin account (blocks random signups)
|
|
||||||
FORGEJO_DISABLE_REGISTRATION=false
|
|
||||||
|
|
|
||||||
|
|
@ -7,3 +7,4 @@
|
||||||
- Never commit secrets: `.env`, `wireguard/wg0.conf`, `config/`, GGUF models, `ansible/.secrets/`.
|
- Never commit secrets: `.env`, `wireguard/wg0.conf`, `config/`, GGUF models, `ansible/.secrets/`.
|
||||||
- Prefer SSH push (`GIT_SSH_COMMAND` with `~/.ssh/id_ed25519` if needed).
|
- Prefer SSH push (`GIT_SSH_COMMAND` with `~/.ssh/id_ed25519` if needed).
|
||||||
- Server checkout: `/opt/stack` is the same repo (`git pull` on ser5). Ansible uses `stack_deploy_method: git`.
|
- Server checkout: `/opt/stack` is the same repo (`git pull` on ser5). Ansible uses `stack_deploy_method: git`.
|
||||||
|
- Prefer **`.env`** for config (see `.env.example`). Demo creds: `DEMO_USER=tim` `DEMO_PASS=asdfasdf`. Fresh host: `./scripts/bootstrap-stack.sh`.
|
||||||
|
|
|
||||||
|
|
@ -192,6 +192,11 @@ ufw_lan_tcp_ports:
|
||||||
wireguard_endpoint_host: las-183-wg.whiskergalaxy.com
|
wireguard_endpoint_host: las-183-wg.whiskergalaxy.com
|
||||||
wireguard_endpoint_port: "443"
|
wireguard_endpoint_port: "443"
|
||||||
|
|
||||||
|
# Tech demo shared credentials (local UIs)
|
||||||
|
demo_user: tim
|
||||||
|
demo_pass: asdfasdf
|
||||||
|
openai_api_key: sk-local-bonsai
|
||||||
|
|
||||||
# Forgejo (self-hosted Git)
|
# Forgejo (self-hosted Git)
|
||||||
forgejo_domain: "192.168.8.123"
|
forgejo_domain: "192.168.8.123"
|
||||||
forgejo_http_port: 3002
|
forgejo_http_port: 3002
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,54 @@
|
||||||
# Generated by Ansible for {{ inventory_hostname }} — do not commit
|
# Generated by Ansible for {{ inventory_hostname }} — do not commit
|
||||||
# Host: {{ ansible_host | default(inventory_hostname) }}
|
# Host: {{ ansible_host | default(inventory_hostname) }}
|
||||||
|
# Tech demo: DEMO_USER/DEMO_PASS used across local UIs when you set them.
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Demo login (local services)
|
||||||
|
################################################################################
|
||||||
|
DEMO_MODE=true
|
||||||
|
DEMO_USER={{ demo_user | default('tim') }}
|
||||||
|
DEMO_PASS={{ demo_pass | default('asdfasdf') }}
|
||||||
|
OPENAI_API_KEY={{ openai_api_key | default('sk-local-bonsai') }}
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Host
|
||||||
|
################################################################################
|
||||||
PUID={{ ansible_user_uid | default(1000) }}
|
PUID={{ ansible_user_uid | default(1000) }}
|
||||||
PGID={{ ansible_user_gid | default(1000) }}
|
PGID={{ ansible_user_gid | default(1000) }}
|
||||||
TZ={{ timezone }}
|
TZ={{ timezone }}
|
||||||
|
|
||||||
CONFIG_DIR={{ config_dir }}
|
CONFIG_DIR={{ config_dir }}
|
||||||
DATA_DIR={{ data_dir }}
|
DATA_DIR={{ data_dir }}
|
||||||
LAN_SUBNET={{ lan_subnet }}
|
LAN_SUBNET={{ lan_subnet }}
|
||||||
|
STACK_HOST={{ forgejo_domain }}
|
||||||
|
|
||||||
# --- AI (Ternary-Bonsai + Vulkan on SER5) ---
|
################################################################################
|
||||||
|
# Ports
|
||||||
|
################################################################################
|
||||||
|
LANDING_PORT=80
|
||||||
|
SABNZBD_PORT=8085
|
||||||
|
OPEN_WEBUI_PORT=3000
|
||||||
|
BONSAI_PORT=8081
|
||||||
|
OPENHANDS_PORT=3001
|
||||||
|
FORGEJO_HTTP_PORT={{ forgejo_http_port }}
|
||||||
|
FORGEJO_SSH_PORT={{ forgejo_ssh_port }}
|
||||||
|
SEARXNG_PORT=8888
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# WireGuard
|
||||||
|
################################################################################
|
||||||
|
WIREGUARD_ENDPOINT_HOST={{ wireguard_endpoint_host | default('las-183-wg.whiskergalaxy.com') }}
|
||||||
|
WIREGUARD_ENDPOINT_PORT={{ wireguard_endpoint_port | default('443') }}
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# AI
|
||||||
|
################################################################################
|
||||||
BONSAI_MODELS_DIR={{ models_dir }}
|
BONSAI_MODELS_DIR={{ models_dir }}
|
||||||
BONSAI_MODEL_PATH=/models/Ternary-Bonsai-27B-Q2_0.gguf
|
BONSAI_MODEL_PATH=/models/Ternary-Bonsai-27B-Q2_0.gguf
|
||||||
|
BONSAI_HF_REPO=prism-ml/Ternary-Bonsai-27B-gguf
|
||||||
|
BONSAI_MAIN_FILE=Ternary-Bonsai-27B-Q2_0.gguf
|
||||||
|
DOWNLOAD_MODEL=true
|
||||||
|
DOWNLOAD_VISION=true
|
||||||
|
DOWNLOAD_DRAFTER=false
|
||||||
BONSAI_BACKEND={{ bonsai_backend }}
|
BONSAI_BACKEND={{ bonsai_backend }}
|
||||||
BONSAI_NGL={{ bonsai_ngl }}
|
BONSAI_NGL={{ bonsai_ngl }}
|
||||||
BONSAI_CTX={{ bonsai_ctx }}
|
BONSAI_CTX={{ bonsai_ctx }}
|
||||||
|
|
@ -20,26 +57,48 @@ BONSAI_THREADS={{ bonsai_threads }}
|
||||||
BONSAI_TEMP=0.7
|
BONSAI_TEMP=0.7
|
||||||
BONSAI_TOP_P=0.95
|
BONSAI_TOP_P=0.95
|
||||||
BONSAI_TOP_K=20
|
BONSAI_TOP_K=20
|
||||||
BONSAI_PORT=8081
|
BONSAI_EXTRA_ARGS=
|
||||||
|
|
||||||
OPEN_WEBUI_PORT=3000
|
|
||||||
OPENAI_API_KEY=sk-local-bonsai
|
|
||||||
WEBUI_AUTH={{ open_webui_auth }}
|
WEBUI_AUTH={{ open_webui_auth }}
|
||||||
|
OPENWEBUI_DEFAULT_MODEL=openai/Ternary-Bonsai-27B-Q2_0.gguf
|
||||||
AI_WORKSPACE_DIR={{ workspace_dir }}
|
AI_WORKSPACE_DIR={{ workspace_dir }}
|
||||||
|
|
||||||
SEARXNG_PORT=8888
|
|
||||||
SEARXNG_SECRET={{ lookup('password', playbook_dir + '/.secrets/searxng_secret chars=ascii_letters,digits length=48') }}
|
SEARXNG_SECRET={{ lookup('password', playbook_dir + '/.secrets/searxng_secret chars=ascii_letters,digits length=48') }}
|
||||||
|
OPENHANDS_MODEL=openai/Ternary-Bonsai-27B-Q2_0.gguf
|
||||||
|
START_OPENHANDS=false
|
||||||
|
|
||||||
OPENHANDS_PORT=3001
|
################################################################################
|
||||||
OPENHANDS_MODEL=openai/local
|
# Forgejo
|
||||||
|
################################################################################
|
||||||
# Forgejo (self-hosted Git)
|
|
||||||
FORGEJO_HTTP_PORT={{ forgejo_http_port }}
|
|
||||||
FORGEJO_SSH_PORT={{ forgejo_ssh_port }}
|
|
||||||
FORGEJO_DOMAIN={{ forgejo_domain }}
|
FORGEJO_DOMAIN={{ forgejo_domain }}
|
||||||
FORGEJO_ROOT_URL={{ forgejo_root_url }}
|
FORGEJO_ROOT_URL={{ forgejo_root_url }}
|
||||||
FORGEJO_DISABLE_REGISTRATION=false
|
FORGEJO_DISABLE_REGISTRATION=false
|
||||||
|
FORGEJO_ADMIN_USER={{ demo_user | default('tim') }}
|
||||||
|
FORGEJO_ADMIN_PASS={{ demo_pass | default('asdfasdf') }}
|
||||||
|
FORGEJO_ADMIN_EMAIL=tim@stack.local
|
||||||
|
STACK_GIT_REPO={{ stack_git_repo }}
|
||||||
|
STACK_GIT_BRANCH={{ stack_git_branch }}
|
||||||
|
|
||||||
# WireGuard peer hostname for gluetun-endpoint-watch (re-dig when tunnel dies)
|
################################################################################
|
||||||
WIREGUARD_ENDPOINT_HOST={{ wireguard_endpoint_host | default('las-183-wg.whiskergalaxy.com') }}
|
# Usenet — fill from secrets or set here for demo
|
||||||
WIREGUARD_ENDPOINT_PORT={{ wireguard_endpoint_port | default('443') }}
|
################################################################################
|
||||||
|
NZBGEEKAPI=https://api.nzbgeek.info/
|
||||||
|
NZBKEEPKEY=
|
||||||
|
NEWSHOSTINGAPI=news.newshosting.com
|
||||||
|
NEWSHOSTINGSSLPORT=563
|
||||||
|
NEWSHOSTINGUSER=
|
||||||
|
HNEWSHOSTINGPASS=
|
||||||
|
NEWSHOSTINGCONNECTIONS=20
|
||||||
|
TWEAKNEWSAPI=news.tweaknews.eu
|
||||||
|
TWEAKNEWSSSLPORT=563
|
||||||
|
TWEAKNEWSLOGIN=
|
||||||
|
TWEAKNEWSPASSWORD=
|
||||||
|
TWEAKNEWSCONNECTIONS=40
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Misc
|
||||||
|
################################################################################
|
||||||
|
FLARESOLVERR_LOG_LEVEL=info
|
||||||
|
FLARESOLVERR_CAPTCHA_SOLVER=none
|
||||||
|
CONFIGURE_USENET=true
|
||||||
|
HEAL_GLUETUN=true
|
||||||
|
COMPOSE_PULL=true
|
||||||
|
COMPOSE_PROFILES=
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,25 @@ Stack: **`/opt/stack`** · Media: **`/storage`** · User: **`tim`**
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Fresh install / tech demo (max automation)
|
||||||
|
|
||||||
|
Almost everything lives in **`.env`** (see `.env.example` — ports, paths, AI, Usenet, demo login, bootstrap flags).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
# set STACK_HOST, LAN_SUBNET, usenet keys (NZBKEEPKEY, NEWSHOSTING*, TWEAKNEWS*)
|
||||||
|
# drop wireguard/wg0.conf (+ # EndpointHost = your.provider.host)
|
||||||
|
|
||||||
|
./scripts/bootstrap-stack.sh
|
||||||
|
# → dirs, model download, compose build/up, gluetun heal, SABnzbd servers
|
||||||
|
```
|
||||||
|
|
||||||
|
Demo login for local apps you create: **`tim` / `asdfasdf`** (`DEMO_USER` / `DEMO_PASS`).
|
||||||
|
|
||||||
|
Still manual once (app APIs differ): Prowlarr↔apps, Jellyfin libraries, first Forgejo/Open WebUI admin (use demo user/pass).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Ansible
|
## Ansible
|
||||||
|
|
||||||
From a machine with this repo and SSH key `~/.ssh/id_ed25519`:
|
From a machine with this repo and SSH key `~/.ssh/id_ed25519`:
|
||||||
|
|
|
||||||
166
scripts/bootstrap-stack.sh
Executable file
166
scripts/bootstrap-stack.sh
Executable file
|
|
@ -0,0 +1,166 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Fresh / demo stack bring-up driven almost entirely by .env
|
||||||
|
#
|
||||||
|
# cp .env.example .env # fill usenet keys + STACK_HOST
|
||||||
|
# # drop wireguard/wg0.conf
|
||||||
|
# ./scripts/bootstrap-stack.sh
|
||||||
|
#
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
STACK_DIR="${STACK_DIR:-$(cd "$SCRIPT_DIR/.." && pwd)}"
|
||||||
|
cd "$STACK_DIR"
|
||||||
|
|
||||||
|
log() { echo "[bootstrap] $*"; }
|
||||||
|
die() { log "ERROR: $*"; exit 1; }
|
||||||
|
|
||||||
|
[[ -f "$STACK_DIR/.env" ]] || die "missing .env — cp .env.example .env and edit"
|
||||||
|
|
||||||
|
set -a
|
||||||
|
# shellcheck disable=SC1091
|
||||||
|
source "$STACK_DIR/.env"
|
||||||
|
set +a
|
||||||
|
|
||||||
|
# Expand nested ${STACK_HOST} style leftovers if someone left them literal
|
||||||
|
STACK_HOST="${STACK_HOST:-192.168.8.123}"
|
||||||
|
DEMO_USER="${DEMO_USER:-tim}"
|
||||||
|
DEMO_PASS="${DEMO_PASS:-asdfasdf}"
|
||||||
|
CONFIG_DIR="${CONFIG_DIR:-$STACK_DIR/config}"
|
||||||
|
DATA_DIR="${DATA_DIR:-$STACK_DIR/data}"
|
||||||
|
BONSAI_MODELS_DIR="${BONSAI_MODELS_DIR:-$STACK_DIR/models/bonsai}"
|
||||||
|
AI_WORKSPACE_DIR="${AI_WORKSPACE_DIR:-$STACK_DIR/workspace}"
|
||||||
|
DOWNLOAD_MODEL="${DOWNLOAD_MODEL:-true}"
|
||||||
|
CONFIGURE_USENET="${CONFIGURE_USENET:-true}"
|
||||||
|
HEAL_GLUETUN="${HEAL_GLUETUN:-true}"
|
||||||
|
COMPOSE_PULL="${COMPOSE_PULL:-true}"
|
||||||
|
START_OPENHANDS="${START_OPENHANDS:-false}"
|
||||||
|
BONSAI_BACKEND="${BONSAI_BACKEND:-vulkan}"
|
||||||
|
|
||||||
|
export STACK_DIR CONFIG_DIR DATA_DIR BONSAI_MODELS_DIR AI_WORKSPACE_DIR
|
||||||
|
|
||||||
|
log "STACK_DIR=$STACK_DIR host=$STACK_HOST demo_user=$DEMO_USER"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Directories
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
log "Creating data / config trees"
|
||||||
|
mkdir -p \
|
||||||
|
"$CONFIG_DIR" \
|
||||||
|
"$BONSAI_MODELS_DIR" \
|
||||||
|
"$AI_WORKSPACE_DIR" \
|
||||||
|
"$DATA_DIR"/media/{movies,tv,music} \
|
||||||
|
"$DATA_DIR"/torrents/{movies,tv,music} \
|
||||||
|
"$DATA_DIR"/usenet/incomplete \
|
||||||
|
"$DATA_DIR"/usenet/complete/{movies,tv,music} \
|
||||||
|
"$STACK_DIR/wireguard" \
|
||||||
|
"$STACK_DIR/secrets"
|
||||||
|
|
||||||
|
# Prefer main .env for usenet if secrets/.env missing
|
||||||
|
if [[ ! -f "$STACK_DIR/secrets/.env" && -f "$STACK_DIR/.env" ]]; then
|
||||||
|
log "Using root .env as secrets source for usenet (DEMO_MODE)"
|
||||||
|
# configure-usenet already can source SECRETS_FILE; symlink for convenience
|
||||||
|
ln -sfn ../.env "$STACK_DIR/secrets/.env" 2>/dev/null || \
|
||||||
|
cp "$STACK_DIR/.env" "$STACK_DIR/secrets/.env"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -f "$STACK_DIR/wireguard/wg0.conf" ]]; then
|
||||||
|
log "WARNING: wireguard/wg0.conf missing — gluetun/qbittorrent will stay unhealthy until you add it"
|
||||||
|
else
|
||||||
|
# Ensure EndpointHost comment for the watcher
|
||||||
|
if [[ -n "${WIREGUARD_ENDPOINT_HOST:-}" ]] && ! grep -q EndpointHost "$STACK_DIR/wireguard/wg0.conf"; then
|
||||||
|
sed -i "1a # EndpointHost = ${WIREGUARD_ENDPOINT_HOST}" "$STACK_DIR/wireguard/wg0.conf" || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Model download
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
if [[ "${DOWNLOAD_MODEL}" == "true" || "${DOWNLOAD_MODEL}" == "1" ]]; then
|
||||||
|
if [[ ! -f "$BONSAI_MODELS_DIR/${BONSAI_MAIN_FILE:-Ternary-Bonsai-27B-Q2_0.gguf}" ]]; then
|
||||||
|
log "Downloading Ternary-Bonsai GGUF (~7GB)…"
|
||||||
|
MODEL_DIR="$BONSAI_MODELS_DIR" \
|
||||||
|
BONSAI_HF_REPO="${BONSAI_HF_REPO:-prism-ml/Ternary-Bonsai-27B-gguf}" \
|
||||||
|
BONSAI_MAIN_FILE="${BONSAI_MAIN_FILE:-Ternary-Bonsai-27B-Q2_0.gguf}" \
|
||||||
|
DOWNLOAD_VISION="${DOWNLOAD_VISION:-1}" \
|
||||||
|
DOWNLOAD_DRAFTER="${DOWNLOAD_DRAFTER:-0}" \
|
||||||
|
"$SCRIPT_DIR/download-bonsai-model.sh"
|
||||||
|
else
|
||||||
|
log "Model already present — skip download"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log "DOWNLOAD_MODEL=false — skip model download"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Docker compose
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
PROFILES=()
|
||||||
|
if [[ -n "${COMPOSE_PROFILES:-}" ]]; then
|
||||||
|
# shellcheck disable=SC2206
|
||||||
|
PROFILES=(--profile ${COMPOSE_PROFILES//,/ --profile })
|
||||||
|
fi
|
||||||
|
if [[ "$START_OPENHANDS" == "true" || "$START_OPENHANDS" == "1" ]]; then
|
||||||
|
PROFILES+=(--profile coding)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$COMPOSE_PULL" == "true" || "$COMPOSE_PULL" == "1" ]]; then
|
||||||
|
log "docker compose pull"
|
||||||
|
docker compose "${PROFILES[@]}" pull || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Building bonsai (BONSAI_BACKEND=$BONSAI_BACKEND)"
|
||||||
|
docker compose build bonsai
|
||||||
|
|
||||||
|
log "docker compose up -d"
|
||||||
|
docker compose "${PROFILES[@]}" up -d
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Heal gluetun if needed
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
if [[ "$HEAL_GLUETUN" == "true" || "$HEAL_GLUETUN" == "1" ]]; then
|
||||||
|
if [[ -x "$SCRIPT_DIR/gluetun-endpoint-watch.sh" ]]; then
|
||||||
|
log "Running gluetun endpoint watch (no-op if healthy)"
|
||||||
|
STACK_DIR="$STACK_DIR" "$SCRIPT_DIR/gluetun-endpoint-watch.sh" || log "gluetun watch returned non-zero (VPN may still need wg0.conf)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Usenet bootstrap
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
if [[ "$CONFIGURE_USENET" == "true" || "$CONFIGURE_USENET" == "1" ]]; then
|
||||||
|
log "Waiting for SABnzbd…"
|
||||||
|
for i in $(seq 1 60); do
|
||||||
|
if curl -fsS "http://127.0.0.1:${SABNZBD_PORT:-8085}/api?mode=version&output=json" >/dev/null 2>&1; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
if [[ -x "$SCRIPT_DIR/configure-usenet.sh" ]]; then
|
||||||
|
SECRETS_FILE="${SECRETS_FILE:-$STACK_DIR/secrets/.env}" \
|
||||||
|
SAB_URL="http://127.0.0.1:${SABNZBD_PORT:-8085}" \
|
||||||
|
"$SCRIPT_DIR/configure-usenet.sh" || log "configure-usenet failed — check SABnzbd wizard / secrets"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Summary
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
log ""
|
||||||
|
log "=== Stack is up (or starting) ==="
|
||||||
|
docker compose ps --format 'table {{.Name}}\t{{.Status}}\t{{.Ports}}' 2>/dev/null || docker compose ps
|
||||||
|
log ""
|
||||||
|
log "Landing: http://${STACK_HOST}/"
|
||||||
|
log "Jellyfin: http://${STACK_HOST}:8096"
|
||||||
|
log "Open WebUI: http://${STACK_HOST}:3000 (first user = admin; use ${DEMO_USER} / ${DEMO_PASS})"
|
||||||
|
log "Forgejo: http://${STACK_HOST}:3002 (install wizard → admin ${DEMO_USER} / ${DEMO_PASS})"
|
||||||
|
log "SABnzbd: http://${STACK_HOST}:${SABNZBD_PORT:-8085}"
|
||||||
|
log "Prowlarr: http://${STACK_HOST}:9696"
|
||||||
|
log "Bonsai: http://${STACK_HOST}:${BONSAI_PORT:-8081}/v1/models"
|
||||||
|
log ""
|
||||||
|
log "Still one-time UI wiring (APIs vary by version):"
|
||||||
|
log " • Prowlarr → NZBgeek + SABnzbd download client → sync to apps"
|
||||||
|
log " • Sonarr/Radarr/Lidarr → root folders /data/media/* + SABnzbd categories"
|
||||||
|
log " • qBittorrent → categories + password ${DEMO_PASS} after first login"
|
||||||
|
log " • Jellyfin → libraries under /data/media/*"
|
||||||
|
log ""
|
||||||
|
log "Demo login for apps you configure: user=${DEMO_USER} pass=${DEMO_PASS}"
|
||||||
|
|
@ -15,7 +15,12 @@ SAB_URL="${SAB_URL:-http://127.0.0.1:8085}"
|
||||||
log() { echo "[configure-usenet] $*"; }
|
log() { echo "[configure-usenet] $*"; }
|
||||||
die() { log "ERROR: $*"; exit 1; }
|
die() { log "ERROR: $*"; exit 1; }
|
||||||
|
|
||||||
[[ -f "$SECRETS_FILE" ]] || die "missing $SECRETS_FILE (see secrets/.env.example)"
|
# Prefer secrets/.env; fall back to root .env (tech-demo: everything in one file)
|
||||||
|
if [[ ! -f "$SECRETS_FILE" && -f "$STACK_DIR/.env" ]]; then
|
||||||
|
SECRETS_FILE="$STACK_DIR/.env"
|
||||||
|
log "using $SECRETS_FILE (no secrets/.env)"
|
||||||
|
fi
|
||||||
|
[[ -f "$SECRETS_FILE" ]] || die "missing $SECRETS_FILE (see secrets/.env.example or .env.example)"
|
||||||
|
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
set -a
|
set -a
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue