refactor: split 'compose.yml' into three distinct '.yml' files

This commit is contained in:
2026-09-16 23:07:52 -07:00
parent b9ca020f99
commit fba0546eff
18 changed files with 1515 additions and 1386 deletions
+40 -57
View File
@@ -9,20 +9,16 @@ TIMEZONE=America/Los_Angeles
# ----- Host -----
LOCAL_IPV4=192.168.50.0 # of your device
EXTERNAL_IPV4=123.156.178.190
SERVER_NAME=your-server # most likely, set this to be the same as your local ipv4, unless you use tailscale or any sort of mDNS on your machine. homepage relies on this for hyperlinks, but not api calls.
WEB_NAME=your-server.com
TAILNET_NAME=tasty-food.ts.net
EXTERNAL_IPV4=255.255.255.0
SERVER_NAME=localhost # most likely, set this to be the same as your local ipv4, unless you use tailscale or any sort of mDNS on your machine. homepage relies on this for hyperlinks, but not api calls.
WEB_NAME=127.0.0.1
TAILNET_NAME=favorite-food.ts.net
WEB_PROTOCOL=http
# ----- Storage -----
DISK1=/mnt/media
DISK2=/mnt/11tb_ext
DISK3=/mnt/shaimpy
# ----- Gluetun (VPN) -----
GLUETUN_KEY=
VPN_PRIVATE_KEY=
DISK1=/mnt/disk1
DISK2=/mnt/disk2
DISK3=/mnt/disk3
# ----- Service Ports -----
PLEX_PORT=32400
@@ -39,7 +35,7 @@ LIDARR_PORT=8686
QBITTORRENT_PORT=2161
TDARR_PORT=8265
TDARR_NODE_PORT=8266
ODYSSEUS_PORT=7001
OPENWEBUI_PORT=7001
PORTAINER_PORT=9443
SCRUTINY_PORT=4545
SCRUTINY_ADMIN_PORT=4646
@@ -47,51 +43,38 @@ PIHOLE_PORT=6060
KARAKEEP_PORT=4621
KARAKEEP_CHROME_PORT=9222
MEILI_PORT=7700
LLAMA_PORT=8079
BAZARR_PORT=8787
VLLM_PORT=5081
SPOTIZERR_PORT=7171
SPOTIZERR_REDIS_PORT=6379
TDARR_NODE_ID=ShaanNode
# ----- API Keys (for homepage widgets & service integrations) -----
PLEX_KEY=
PROWLARR_KEY=
RADARR_KEY=
SONARR_KEY=
LIDARR_KEY=
TAUTULLI_KEY=
SEERR_KEY=
QBITTORRENT_KEY=
TAILSCALE_KEY=
TAILSCALE_DEVICE_KEY=
CLOUDFLARED_KEY=
# ----- Docker Images -----
# get with command:
# docker compose ps -q | xargs docker inspect --format '{{.Image}}' | sort -u | xargs docker inspect --format '{{if .RepoDigests}}{{index .RepoDigests 0}}{{else}}{{.Id}} (local build){{end}}'
# ----- Cloudflare (for homepage widget) -----
CLOUDFLARED_ACCOUNT_ID=
CLOUDFLARED_TUNNEL_ID=
CLOUDFLARED_API_KEY=
# ----- Portainer (for homepage widget) -----
PORTAINER_API_KEY=
# ----- Pi-hole -----
PIHOLE_WEBPASSWORD=
# ----- Glances -----
GLANCES_USERNAME=
GLANCES_PASSWORD=
# ----- Karakeep -----
# KARAKEEP
DATA_DIR=/karakeep-data
MEILI_ADDR=http://127.0.0.1:${MEILI_PORT}
MEILI_MASTER_KEY=
NEXTAUTH_URL=http://localhost:3001
NEXTAUTH_SECRET=
OPENAI_API_KEY=ollama
OPENAI_BASE_URL=http://${LOCAL_IPV4}:${LLAMA_PORT}/v1
INFERENCE_TEXT_MODEL=Qwen3.6
INFERENCE_IMAGE_MODEL=Qwen3.6
EMBEDDING_TEXT_MODEL=Qwen3-Embedding
EMBEDDING_DIMENSIONS=2048
EMBEDDING_CONTEXT_LENGTH=4096
EMBEDDING_ENABLE_AUTO_INDEXING=true
TAUTULLI_IMAGE=ghcr.io/tautulli/tautulli:latest
GLUETUN_IMAGE=qmcgaw/gluetun@sha256:e3272b29a4bc177b389fbdcb54cf9716ccbfc30f04d8b7a35b0a5be9cdb58461
QBITTORRENT_IMAGE=lscr.io/linuxserver/qbittorrent@sha256:b6ab43fe86039e5bdd3cc0b59b946414fcff0c8183e93636e6cb438fdac45028
PROWLARR_IMAGE=lscr.io/linuxserver/prowlarr@sha256:1295cff29d10b486c0d8324d1559a552140a5932bf8b3d87e398654414f63f92
SONARR_IMAGE=lscr.io/linuxserver/sonarr@sha256:373159ba768e23a3a1c497d9f2b936addf8fd5b1fdce7dd6a14080ac928bfda0
RADARR_IMAGE=lscr.io/linuxserver/radarr@sha256:a45b5ab0f850f39edb4cc9c95bbd967b52ddc3d4574a4dfb45561177db6c88f4
LIDARR_IMAGE=lscr.io/linuxserver/lidarr@sha256:bfec0ec2dc351fa5928379d785b08be395886f109393b9040ed7973bd1008060
BAZARR_IMAGE=lscr.io/linuxserver/bazarr@sha256:819327cb5da94a4187bd0512a8f6f8632541404e52cf5977a96e8865d983b07b
TDARR_IMAGE=haveagitgat/tdarr@sha256:45742f00e51a58553844c8b13d97e7ae9b2ee9a056bd8e5993d9a8282a45a23f
TDARR_NODE_IMAGE=haveagitgat/tdarr_node@sha256:7542459ac5ed5cd299600530e9625b9d590629d5dc391c0016773f5d6aa3fe75
SEERR_IMAGE=ghcr.io/seerr-team/seerr:latest
SEARXNG_IMAGE=searxng/searxng:latest
OPENWEBUI_IMAGE=ghcr.io/open-webui/open-webui@sha256:f67aea542a85e82cfab8c1de0719487b1566bffbb53c2b5448223611a16889dc
KARAKEEP_IMAGE=ghcr.io/karakeep-app/karakeep@sha256:5467873df817ab3aa837f2b06bd7f2b0974132ba1ae5bce0b7e2fd134abc269b
CLOUDFLARED_IMAGE=cloudflare/cloudflared:latest
PORTUPDATER_IMAGE=curlimages/curl:8.21.0
CADDY_IMAGE=caddy:latest
AUTOHEAL_IMAGE=willfarrell/autoheal:latest
MYSPEED_IMAGE=germannewsmaker/myspeed:latest
HOMEPAGE_IMAGE=ghcr.io/gethomepage/homepage:latest
SCRUTINY_IMAGE=ghcr.io/analogj/scrutiny@sha256:9d0e6d146529d9d1407e1d0a9f685d0b6b840cac070e87e0ab62034d9b80c2b7
PIHOLE_IMAGE=pihole/pihole@sha256:f7d1be836e3bc608b56d82fc9904f5a831cdfbc0dc9c6d58f94e4c985c70038b
PORTAINER_IMAGE=portainer/portainer-ce:lts
SPOTIZERR_IMAGE=spotizerrphoenix/spotizerr:latest
REDIS_IMAGE=redis:alpine
+6 -109
View File
@@ -29,7 +29,12 @@ karakeep-data
SECURITY.md
init-database.*
etc*
!odysseus/.*
openwebui/
searxng/
vllm/
bazarr/
docker-data/
spotizerr/
# Only track homepage structural yaml files, ignore app cache
homepage/*
@@ -38,111 +43,3 @@ homepage/*
!homepage/*.css
!homepage/*.js
!homepage/images/
# from odysseus .gitignore
# Python
__pycache__/
*.pyc
*.pyo
*.egg-info/
dist/
build/
!static/js/editor/build/
venv/
.venv/
*.egg
# Environment
.env
.env.bak.*
!.env.example
# Local uv lockfile (optional, per-platform — see "Faster installs with uv" in README)
requirements.lock
# SOPS workflow — encrypted `secrets.env` is intentionally committable,
# but every variant (plaintext, manual decrypt copy, editor backup)
# must stay out of git. Mirrored in .dockerignore so the same artifacts
# also cannot enter image build layers.
secrets.env.*
!secrets.env.example
# Data — all user data stays local
data/
!services/hwfit/data/
!services/hwfit/data/hf_models.json
logs/
*.log
*.db
*.sqlite
*.sqlite3
# Node
node_modules/
services/node_modules/
services/data/
# IDE / Editor
.aider*
.claude/
.vscode/
.idea/
*.swp
*.swo
*~
# Test capture artifacts (browser session dumps may contain personal content)
.playwright-mcp/
# OS
.DS_Store
Thumbs.db
# Build artifacts
*.cache
cache/
output.txt.txt
# Media (uploaded/generated)
*.jpg
*.jpeg
*.png
*.gif
*.bmp
*.webp
*.tiff
*.pdf
# …except shipped static assets
!static/icons/*.png
# …except shipped demo assets in docs/ that the README links to.
!docs/*.jpg
!docs/*.jpeg
!docs/*.png
!docs/*.gif
!docs/*.webp
# …and curated docs/ subfolder assets (e.g. accessibility before/after shots).
!docs/**/*.png
!docs/**/*.jpg
!docs/**/*.gif
!docs/**/*.webp
# Reports and temp files
reports/
tasks/
scripts/compound/*.json
research_data/
**/search_analytics.json
# Internal dev/review notes — not for public repo
dev-docs/
# Windows-port working docs (local only, not for public repo)
docs/windows-port/
# Local config
compound.config.json
*.error.log
_scratch/
/odysseus/
Regular → Executable
View File
Regular → Executable
+9 -725
View File
@@ -1,10 +1,8 @@
# =============================================================================
# Torrent Stack - Docker Compose
# =============================================================================
name: torrent-stack
networks:
caddy_net:
external: true
#caddy_net:
# external: true
tdarr-net:
driver: bridge
default:
@@ -14,8 +12,8 @@ volumes:
#caddy_config:
#caddy_data:
# external: true
myspeed:
external: true
#myspeed:
# external: true
portainer_data:
name: portainer_data
@@ -27,721 +25,7 @@ volumes:
# → devices → restart
# =============================================================================
services:
# ---------------------------------------------------------------------------
# VPN & Download
# ---------------------------------------------------------------------------
gluetun:
image: qmcgaw/gluetun:latest
container_name: gluetun
env_file:
- .env
- ./env/.gluetun.env
cap_add:
- NET_ADMIN
ports:
- 8888:8000/tcp
- ${QBITTORRENT_PORT}:${QBITTORRENT_PORT}
- ${PROWLARR_PORT}:${PROWLARR_PORT}
- ${SONARR_PORT}:${SONARR_PORT}
- ${RADARR_PORT}:${RADARR_PORT}
- ${LIDARR_PORT}:${LIDARR_PORT}
volumes:
- ./gluetun-data:/tmp/gluetun:rw
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- VPN_SERVICE_PROVIDER=protonvpn
- VPN_TYPE=wireguard
- HTTP_CONTROL_SERVER=ON
- WIREGUARD_MTU=1280
- VPN_PORT_FORWARDING=on
- VPN_PORT_FORWARDING_PROVIDER=protonvpn
- SERVER_COUNTRIES=Netherlands
- NETWORK_LOCAL_ADDRESSES=192.168.50.0/24,172.16.0.0/12
- FIREWALL_OUTBOUND_SUBNETS=192.168.50.0/24
- TZ=${TIMEZONE}
- DNS_UPSTREAM_RESOLVER_TYPE=doh
healthcheck:
test: ["CMD-SHELL", "wget --spider -q http://1.1.1.1 || exit 1"]
interval: 30s
timeout: 15s
retries: 3
start_period: 1m
security_opt:
- no-new-privileges:true
mem_limit: 256m
cpus: 1.0
extra_hosts:
- "host.docker.internal:host-gateway"
devices:
- /dev/net/tun:/dev/net/tun
restart: always
qbittorrent:
image: lscr.io/linuxserver/qbittorrent:latest
container_name: qbittorrent
network_mode: "container:gluetun"
depends_on:
gluetun:
condition: service_healthy
volumes:
- ./qbittorrent:/config
- ${DISK1}:${DISK1}
- ${DISK2}:${DISK2}
- ./gluetun-data:/tmp/gluetun
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- PUID=${HOST_PUID:-1000}
- PGID=${HOST_PGID:-1000}
- TZ=${TIMEZONE}
- WEBUI_PORT=${QBITTORRENT_PORT}
labels:
- "autoheal=true"
healthcheck:
test:
[
"CMD-SHELL",
"curl -sf http://localhost:${QBITTORRENT_PORT}/api/v2/app/version || exit 1",
]
interval: 1m
timeout: 10s
retries: 3
start_period: 30s
security_opt:
- no-new-privileges:true
mem_limit: 512m
cpus: 2.0
restart: unless-stopped
port-updater:
image: curlimages/curl:latest
container_name: port-updater
network_mode: "container:gluetun"
depends_on:
gluetun:
condition: service_healthy
volumes:
- ./gluetun-data:/tmp/gluetun:ro
entrypoint: ["/bin/sh", "-c"]
command:
- |
trap 'exit 0' SIGTERM;
while true; do
while [ ! -s /tmp/gluetun/forwarded_port ]; do
echo "Waiting for Gluetun to provide a port..."
sleep 5
done
read -r PORT_VAL < /tmp/gluetun/forwarded_port;
CLEAN_PORT=$$(echo "$$PORT_VAL" | tr -d '\r\n ');
if [ -n "$$CLEAN_PORT" ]; then
echo "Updating qBit to port: $$CLEAN_PORT";
sleep 10;
curl -s -X POST -d "json={\"listen_port\":$$CLEAN_PORT}" http://localhost:${QBITTORRENT_PORT}/api/v2/app/setPreferences;
sleep 3600 & wait $$!;
else
sleep 10
fi
done
labels:
- "autoheal=true"
healthcheck:
test: ["CMD-SHELL", "curl -sf https://1.1.1.1 || exit 1"]
interval: 1m
timeout: 10s
retries: 3
start_period: 30s
security_opt:
- no-new-privileges:true
read_only: true
tmpfs:
- /tmp
mem_limit: 64m
cpus: 0.25
restart: unless-stopped
# ---------------------------------------------------------------------------
# *arr Stack
# ---------------------------------------------------------------------------
prowlarr:
image: lscr.io/linuxserver/prowlarr:latest
container_name: prowlarr
network_mode: "container:gluetun"
depends_on:
gluetun:
condition: service_healthy
volumes:
- ./prowlarr:/config
- ./notify.sh:/notify.sh:ro
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- PUID=${HOST_PUID:-1000}
- PGID=${HOST_PGID:-1000}
- TZ=${TIMEZONE}
labels:
- "autoheal=true"
healthcheck:
test:
[
"CMD-SHELL",
"curl -sf http://localhost:${PROWLARR_PORT}/ping || exit 1",
]
interval: 1m
timeout: 10s
retries: 3
start_period: 30s
security_opt:
- no-new-privileges:true
mem_limit: 256m
cpus: 1.0
restart: unless-stopped
sonarr:
image: lscr.io/linuxserver/sonarr:latest
container_name: sonarr
network_mode: "container:gluetun"
depends_on:
gluetun:
condition: service_healthy
volumes:
- /var/lib/sonarr:/config
- ${DISK1}:${DISK1}
- ${DISK2}:${DISK2}
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- PUID=${HOST_PUID:-1000}
- PGID=${HOST_PGID:-1000}
- TZ=${TIMEZONE}
labels:
- "autoheal=true"
healthcheck:
test:
["CMD-SHELL", "curl -sf http://localhost:${SONARR_PORT}/ping || exit 1"]
interval: 1m
timeout: 10s
retries: 3
start_period: 60s
security_opt:
- no-new-privileges:true
mem_limit: 256m
cpus: 1.0
restart: unless-stopped
radarr:
image: lscr.io/linuxserver/radarr:latest
container_name: radarr
network_mode: "container:gluetun"
depends_on:
gluetun:
condition: service_healthy
volumes:
- /var/lib/radarr:/config
- ${DISK1}:${DISK1}
- ${DISK2}:${DISK2}
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- PUID=${HOST_PUID:-1000}
- PGID=${HOST_PGID:-1000}
- TZ=${TIMEZONE}
labels:
- "autoheal=true"
healthcheck:
test:
["CMD-SHELL", "curl -sf http://localhost:${RADARR_PORT}/ping || exit 1"]
interval: 1m
timeout: 10s
retries: 3
start_period: 30s
security_opt:
- no-new-privileges:true
mem_limit: 256m
cpus: 1.0
restart: unless-stopped
lidarr:
image: lscr.io/linuxserver/lidarr:latest
container_name: lidarr
network_mode: "container:gluetun"
depends_on:
gluetun:
condition: service_healthy
volumes:
- ./lidarr:/music
- /var/lib/lidarr:/config
- ${DISK1}:${DISK1}
- ${DISK2}:${DISK2}
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- PUID=${HOST_PUID:-1000}
- PGID=${HOST_PGID:-1000}
- TZ=${TIMEZONE}
labels:
- "autoheal=true"
healthcheck:
test:
["CMD-SHELL", "curl -sf http://localhost:${LIDARR_PORT}/ping || exit 1"]
interval: 1m
timeout: 10s
retries: 3
start_period: 30s
security_opt:
- no-new-privileges:true
mem_limit: 256m
cpus: 1.0
restart: unless-stopped
# ---------------------------------------------------------------------------
# Media
# ---------------------------------------------------------------------------
tautulli:
image: ghcr.io/tautulli/tautulli
container_name: tautulli
networks:
- caddy_net
ports:
- ${TAUTULLI_PORT}:${TAUTULLI_PORT}
volumes:
- ./tautulli:/config
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- PUID=${HOST_PUID:-1000}
- PGID=${HOST_PGID:-1000}
- TZ=${TIMEZONE}
security_opt:
- no-new-privileges:true
mem_limit: 256m
cpus: 1.0
restart: unless-stopped
tdarr:
image: haveagitgat/tdarr:latest
container_name: tdarr
networks:
- tdarr-net
ports:
- ${TDARR_PORT}:${TDARR_PORT}
- ${TDARR_NODE_PORT}:${TDARR_NODE_PORT}
volumes:
- ./tdarr/server:/app/server
- ./tdarr/configs:/app/configs
- ./tdarr/temp:/temp
- ${DISK1}:${DISK1}
- ${DISK2}:${DISK2}
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- TZ=${TIMEZONE}
- PUID=${HOST_PUID:-1000}
- PGID=${HOST_PGID:-1000}
- UMASK_SET=002
- serverIP=0.0.0.0
security_opt:
- no-new-privileges:true
mem_limit: 512m
cpus: 2.0
restart: unless-stopped
tdarr-node:
image: haveagitgat/tdarr_node:latest
container_name: tdarr-node
networks:
- tdarr-net
volumes:
- ./tdarr/configs:/app/configs
- ./tdarr/logs:/app/logs
- ./tdarr/temp:/temp
- ${DISK1}:${DISK1}
- ${DISK2}:${DISK2}
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- TZ=${TIMEZONE}
- PUID=${HOST_PUID:-1000}
- PGID=${HOST_PGID:-1000}
- nodeID=${TDARR_NODE_ID}
- nodeIP=0.0.0.0
- serverIP=tdarr
- serverPort=${TDARR_NODE_PORT}
security_opt:
- no-new-privileges:true
mem_limit: 2g
cpus: 4.0
devices:
- /dev/dri:/dev/dri
restart: unless-stopped
seerr:
image: ghcr.io/seerr-team/seerr:latest
container_name: seerr
networks:
- caddy_net
ports:
- ${SEERR_PORT}:${SEERR_PORT}
volumes:
- ./seerr:/app/config
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- LOG_LEVEL=info
- TZ=${TIMEZONE}
- PUID=${HOST_PUID:-1000}
- PGID=${HOST_PGID:-1000}
labels:
- "autoheal=true"
init: true
working_dir: "/app"
healthcheck:
test:
[
"CMD-SHELL",
"wget -qO- http://127.0.0.1:${SEERR_PORT}/api/v1/status || exit 1",
]
interval: 1m
timeout: 10s
retries: 3
start_period: 60s
security_opt:
- no-new-privileges:true
mem_limit: 256m
cpus: 1.0
restart: unless-stopped
# Anubis anti-bot proxy for Seerr (uncomment to enable):
#seerr-anubis:
# image: ghcr.io/techarohq/anubis:latest
# container_name: seerr-anubis
# networks:
# - caddy_net
# environment:
# - BIND=:55055
# - TARGET=http://seerr:${SEERR_PORT}
# security_opt:
# - no-new-privileges:true
# mem_limit: 128m
# cpus: 0.5
# restart: unless-stopped
# ---------------------------------------------------------------------------
# Monitoring
# ---------------------------------------------------------------------------
homepage:
image: ghcr.io/gethomepage/homepage:latest
container_name: homepage
env_file:
- .env
- ./env/.homepage.env
networks:
- caddy_net
ports:
- ${HOMEPAGE_PORT}:${HOMEPAGE_PORT}
volumes:
- ./homepage:/app/config
- ./homepage/images:/app/public/images
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./gluetun-data:/tmp/gluetun:ro
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- "HOMEPAGE_ALLOWED_HOSTS=gethomepage.dev,${LOCAL_IPV4}:${HOMEPAGE_PORT},${SERVER_NAME}:${HOMEPAGE_PORT},${SERVER_NAME}.${TAILNET_NAME}:${HOMEPAGE_PORT}"
- "HOMEPAGE_VAR_DISK1=${DISK1}"
- "HOMEPAGE_VAR_DISK2=${DISK2}"
- "HOMEPAGE_VAR_DISK3=${DISK3}"
- "HOMEPAGE_VAR_WEB_PROTOCOL=${WEB_PROTOCOL}"
- "HOMEPAGE_VAR_LOCAL_IPV4=${LOCAL_IPV4}"
- "HOMEPAGE_VAR_SERVER_NAME=${SERVER_NAME}"
- "HOMEPAGE_VAR_PLEX_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${PLEX_PORT}"
- "HOMEPAGE_VAR_QBITTORRENT_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${QBITTORRENT_PORT}"
- "HOMEPAGE_VAR_PROWLARR_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${PROWLARR_PORT}"
- "HOMEPAGE_VAR_SONARR_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${SONARR_PORT}"
- "HOMEPAGE_VAR_RADARR_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${RADARR_PORT}"
- "HOMEPAGE_VAR_LIDARR_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${LIDARR_PORT}"
- "HOMEPAGE_VAR_TAUTULLI_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${TAUTULLI_PORT}"
- "HOMEPAGE_VAR_TDARR_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${TDARR_PORT}"
- "HOMEPAGE_VAR_HOMEPAGE_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${HOMEPAGE_PORT}"
- "HOMEPAGE_VAR_GLANCES_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${GLANCES_PORT}"
- "HOMEPAGE_VAR_SEERR_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${SEERR_PORT}"
- "HOMEPAGE_VAR_MYSPEED_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${MYSPEED_PORT}"
- "HOMEPAGE_VAR_ODYSSEUS_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${ODYSSEUS_PORT}"
- "HOMEPAGE_VAR_PIHOLE_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${PIHOLE_PORT}"
- "HOMEPAGE_VAR_PORTAINER_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${PORTAINER_PORT}"
- "HOMEPAGE_VAR_SCRUTINY_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${SCRUTINY_PORT}"
- "HOMEPAGE_VAR_KARAKEEP_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${KARAKEEP_PORT}"
- "HOMEPAGE_VAR_PLEX_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${PLEX_PORT}"
- "HOMEPAGE_VAR_QBITTORRENT_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${QBITTORRENT_PORT}"
- "HOMEPAGE_VAR_PROWLARR_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${PROWLARR_PORT}"
- "HOMEPAGE_VAR_SONARR_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${SONARR_PORT}"
- "HOMEPAGE_VAR_RADARR_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${RADARR_PORT}"
- "HOMEPAGE_VAR_LIDARR_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${LIDARR_PORT}"
- "HOMEPAGE_VAR_TAUTULLI_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${TAUTULLI_PORT}"
- "HOMEPAGE_VAR_TDARR_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${TDARR_PORT}"
- "HOMEPAGE_VAR_HOMEPAGE_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${HOMEPAGE_PORT}"
- "HOMEPAGE_VAR_GLANCES_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${GLANCES_PORT}"
- "HOMEPAGE_VAR_SEERR_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${SEERR_PORT}"
- "HOMEPAGE_VAR_MYSPEED_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${MYSPEED_PORT}"
- "HOMEPAGE_VAR_ODYSSEUS_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${ODYSSEUS_PORT}"
- "HOMEPAGE_VAR_PIHOLE_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${PIHOLE_PORT}"
- "HOMEPAGE_VAR_PORTAINER_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${PORTAINER_PORT}"
- "HOMEPAGE_VAR_SCRUTINY_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${SCRUTINY_PORT}"
- "HOMEPAGE_VAR_KARAKEEP_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${KARAKEEP_PORT}"
- TZ=${TIMEZONE}
security_opt:
- no-new-privileges:true
mem_limit: 256m
cpus: 1.0
extra_hosts:
- "host.docker.internal:host-gateway"
restart: unless-stopped
# glances:
# image: nicolargo/glances:latest-full
# container_name: glances
# network_mode: host
# volumes:
# - /etc/os-release:/etc/os-release:ro
# - /:/host:ro
# #- /etc/localtime:/etc/localtime:ro
# #- /etc/timezone:/etc/timezone:ro
# environment:
# - "GLANCES_OPT=-w --password ${GLANCES_PASSWORD}"
# - PUID=${HOST_PUID:-1000}
# - PGID=${HOST_PGID:-1000}
# - TZ=${TIMEZONE}
# healthcheck:
# test:
# ["CMD", "curl", "-f", "http://localhost:${GLANCES_PORT}/api/4/status"]
# interval: 1m
# timeout: 10s
# retries: 3
# start_period: 60s
# security_opt:
# - no-new-privileges:true
# mem_limit: 256m
# cpus: 1.0
# devices:
# - /dev/dri:/dev/dri
# restart: unless-stopped
# # Uncomment to protect Glances WebUI by a login/password (add --password to GLANCES_OPT)
# secrets:
# - source: glances_password
# target: /root/.config/glances/<login>.pwd
# secrets:
# glances_password:
# file: ./secrets/glances_password
autoheal:
image: willfarrell/autoheal:latest
container_name: autoheal
volumes:
- /var/run/docker.sock:/var/run/docker.sock
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- AUTOHEAL_CONTAINER_LABEL=all
- AUTOHEAL_INTERVAL=30
- AUTOHEAL_START_PERIOD=60
- TZ=${TIMEZONE}
security_opt:
- no-new-privileges:true
read_only: true
mem_limit: 64m
cpus: 0.25
restart: unless-stopped
myspeed:
image: germannewsmaker/myspeed
container_name: myspeed
ports:
- ${MYSPEED_PORT}:${MYSPEED_PORT}/tcp
volumes:
- myspeed:/myspeed/data
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- TZ=${TIMEZONE}
security_opt:
- no-new-privileges:true
mem_limit: 256m
cpus: 1.0
restart: unless-stopped
scrutiny:
image: ghcr.io/analogj/scrutiny:nightly-omnibus
container_name: scrutiny
cap_add:
- SYS_RAWIO
ports:
- ${SCRUTINY_PORT}:8080
- ${SCRUTINY_ADMIN_PORT}:8086
volumes:
- /run/udev:/run/udev:ro
- ./scrutiny/config:/opt/scrutiny/config
- ./scrutiny/influxdb:/opt/scrutiny/influxdb
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- TZ=${TIMEZONE}
security_opt:
- no-new-privileges:true
mem_limit: 512m
cpus: 1.0
devices:
- /dev/nvme0n1
- /dev/sda
restart: unless-stopped
# ---------------------------------------------------------------------------
# Infrastructure
# ---------------------------------------------------------------------------
pihole:
image: pihole/pihole:latest
container_name: pihole
cap_add:
- NET_ADMIN # Allows managing network interfaces & sockets
- NET_BIND_SERVICE # Allows non-root users to bind to ports < 1024 (port 53 & 123)
- SYS_TIME # Resolves the NTP system time warning
network_mode: host
ports:
- 53:53/tcp
- 53:53/udp
- 67:67/udp
- ${PIHOLE_PORT}:${PIHOLE_PORT}/tcp
volumes:
- ./etc-pihole:/etc/pihole
- ./etc-dnsmasq.d:/etc/dnsmasq.d
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- TZ=${TIMEZONE}
- FTLCONF_misc_etc_dnsmasq_d=true
# security_opt:
# - no-new-privileges:true
mem_limit: 256m
cpus: 1.0
restart: unless-stopped
cloudflared:
image: cloudflare/cloudflared:latest
container_name: cloudflared
env_file:
- ./env/.cloudflared.env
command: tunnel --no-autoupdate run # --token ${CLOUDFLARED_KEY}
security_opt:
- no-new-privileges:true
read_only: true
mem_limit: 128m
cpus: 0.5
restart: always
portainer:
image: portainer/portainer-ce:lts
container_name: portainer
ports:
- ${PORTAINER_PORT}:${PORTAINER_PORT}
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- portainer_data:/data
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- TZ=${TIMEZONE}
security_opt:
- no-new-privileges:true
mem_limit: 256m
cpus: 1.0
restart: unless-stopped
caddy:
image: caddy:latest
container_name: caddy
networks:
- caddy_net
ports:
- 80:80
- 443:443
volumes:
- ./caddy/Caddyfile:/etc/caddy/Caddyfile
- ./caddy/site:/srv
- ./caddy/caddy_data:/data
- ./caddy/caddy_config:/config
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- TZ=${TIMEZONE}
security_opt:
- no-new-privileges:true
mem_limit: 128m
cpus: 0.5
restart: unless-stopped
# ---------------------------------------------------------------------------
# Karakeep
# ---------------------------------------------------------------------------
karakeep:
image: ghcr.io/karakeep-app/karakeep:${KARAKEEP_VERSION:-release}
container_name: karakeep
env_file:
- ./env/.karakeep.env
ports:
- ${KARAKEEP_PORT}:3000 # must be 3000 as per the dev
volumes:
- ./karakeep-data:/data
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- MEILI_ADDR=http://meilisearch:${MEILI_PORT}
- BROWSER_WEB_URL=http://chrome:${KARAKEEP_CHROME_PORT}
- DATA_DIR=/data
security_opt:
- no-new-privileges:true
mem_limit: 512m
cpus: 2.0
restart: unless-stopped
chrome:
image: gcr.io/zenika-hub/alpine-chrome:124
container_name: karakeep-chromebrowser
command:
- --no-sandbox
- --disable-gpu
- --disable-dev-shm-usage
- --remote-debugging-address=0.0.0.0
- --remote-debugging-port=${KARAKEEP_CHROME_PORT}
- --hide-scrollbars
- --disable-blink-features=AutomationControlled
- --window-size=1440,900
security_opt:
- no-new-privileges:true
mem_limit: 512m
cpus: 1.0
restart: unless-stopped
meilisearch:
image: getmeili/meilisearch:v1.41.0
container_name: karakeep-meilisearch
env_file:
- .env
volumes:
- ./meilisearch:/meili_data
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- MEILI_NO_ANALYTICS=true
security_opt:
- no-new-privileges:true
mem_limit: 512m
cpus: 1.0
restart: unless-stopped
include:
- media.yml
- infra.yml
- web.yml
Regular → Executable
+23 -18
View File
@@ -9,12 +9,18 @@
- "Seerr":
- icon: sh-seerr
href: "{{HOMEPAGE_VAR_SEERR_HOST}}"
- "Odysseus":
- icon: http://shaan-server:7001/static/icons/icon-192.png
- "Open WebUI":
- icon: sh-open-webui
href: https://shaan-server.cc
- "Hermes Agent":
- icon: http://shaan-server:9119/favicon.ico # "{{HOMEPAGE_VAR_HERMES_HOST}}"/favicon.ico
href: http://shaan-server:9119 # "{{HOMEPAGE_VAR_HERMES_HOST}}"
- "Karakeep":
- icon: sh-karakeep-light
href: "{{HOMEPAGE_VAR_KARAKEEP_HOST}}"
- "SearXNG":
- icon: sh-searxng
href: http://shaan-server:5080 # "{{HOMEPAGE_VAR_SEARXNG_HOST}}"
- "Admin":
- "Sonarr":
@@ -38,9 +44,6 @@
- "qBittorrent":
- icon: sh-qbittorrent
href: "{{HOMEPAGE_VAR_QBITTORRENT_HOST}}"
- "Speedtest":
- icon: sh-speedtest-light
href: "{{HOMEPAGE_VAR_MYSPEED_HOST}}"
- "Scrutiny":
- icon: sh-scrutiny-light
href: "{{HOMEPAGE_VAR_SCRUTINY_HOST}}"
@@ -59,9 +62,6 @@
- "Cloudflare":
- icon: sh-cloudflare
href: https://dash.cloudflare.com
- Monitorix:
- icon: "{{HOMEPAGE_VAR_WEB_PROTOCOL}}://{{HOMEPAGE_VAR_LOCAL_IPV4}}:4000/monitorix/monitorixico.png"
href: http://{{HOMEPAGE_VAR_SERVER_NAME}}:4000/monitorix
- Bookmarks:
- "Youtube":
@@ -70,13 +70,18 @@
- "Gmail":
- icon: sh-gmail
href: https://mail.google.com
# - github/dhaan7/torrent-stack:
# - icon: sh-github-light
# href: https://github.com/dhaan7/torrent-stack
# description: link to this GitHub repository
# - Termix:
# - icon: "{{HOMEPAGE_VAR_TERMIX_ADDRESS}}/favicon.ico"
# href: "{{HOMEPAGE_VAR_TERMIX_HOST}}"
# description: Terminal emulator in your web browser
#
#
- "dhaan7/torrent-stack":
- icon: sh-github-light
href: https://github.com/dhaan7/torrent-stack
- "Twitter":
- icon: sh-twitter
href: https://x.com/
- "Roblox":
- icon: sh-roblox
href: https://roblox.com
- "Reddit":
- icon: sh-reddit
href: https://reddit.com
- "Discord":
- icon: sh-discord
href: https://discord.com/channels/@me
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
+39 -33
View File
@@ -1,4 +1,3 @@
---
# For configuration options and examples, please see:
# https://gethomepage.dev/configs/services/
@@ -35,31 +34,24 @@
deviceid: "{{HOMEPAGE_VAR_TAILSCALE_DEVICE_KEY}}"
key: "{{HOMEPAGE_VAR_TAILSCALE_KEY}}"
fields: [address]
- "Speedtest Results >":
icon: sh-speedtest-light
href: "{{HOMEPAGE_VAR_MYSPEED_HOST}}"
server: my-docker
container: myspeed
widget:
type: myspeed
url: "{{HOMEPAGE_VAR_MYSPEED_ADDRESS}}"
fields: [download, upload]
- "Media":
- "Media_1":
- "Odysseus":
icon: "{{HOMEPAGE_VAR_ODYSSEUS_HOST}}/static/icons/icon-192.png"
href: "{{HOMEPAGE_VAR_ODYSSEUS_HOST}}"
siteMonitor: "{{HOMEPAGE_VAR_ODYSSEUS_HOST}}"
description: Local AI Models
- "Open WebUI":
icon: sh-open-webui
href: "{{HOMEPAGE_VAR_OPENWEBUI_HOST}}"
siteMonitor: "{{HOMEPAGE_VAR_OPENWEBUI_ADDRESS}}"
description: Chatbot
- "Port Updater":
icon: sh-docker
description: Shell
server: my-docker
container: port-updater
- "Monitorix":
icon: "{{HOMEPAGE_VAR_WEB_PROTOCOL}}://{{HOMEPAGE_VAR_LOCAL_IPV4}}:4000/monitorix/monitorixico.png"
href: http://{{HOMEPAGE_VAR_SERVER_NAME}}:4000/monitorix
description: System
- "VLLM Status":
icon: sh-vllm
server: my-docker
href: "{{HOMEPAGE_VAR_WEB_PROTOCOL}}://{{HOMEPAGE_VAR_SERVER_NAME}}:9119"
container: vllm
description: LLM Serving
- "Media_2":
- "Plex >":
icon: sh-plex
@@ -185,21 +177,35 @@
- level: warn
when: gt
value: 0
- "Tdarr Node (Intel Arc Pro B70)":
icon: sh-tdarr
- "Bazarr >":
icon: sh-bazarr
href: "{{HOMEPAGE_VAR_BAZARR_HOST}}"
description: Subtitles
server: my-docker
container: tdarr-node
- "Tdarr Node (Intel Graphics UHD 710)":
icon: sh-tdarr
container: bazarr
widget:
type: bazarr
url: "{{HOMEPAGE_VAR_BAZARR_ADDRESS}}"
key: "{{HOMEPAGE_VAR_BAZARR_KEY}}"
# fields: [wanted, queued]
highlight:
queued:
numeric:
- level: warn
when: gt
value: 0
- "Spotizerr":
icon: sh-spotify
href: http://shaan-server:7171
server: my-docker
container: tdarr-node
container: spotizerr
- "Media_Bottom":
- "Tdarr >":
icon: sh-tdarr
href: "{{HOMEPAGE_VAR_TDARR_HOST}}"
description: Transcode
server: my-docker
container: tdarr
container: tdarr-node
widget:
type: tdarr
url: "{{HOMEPAGE_VAR_TDARR_ADDRESS}}"
@@ -226,39 +232,39 @@
container: portainer
widget:
type: portainer
url: https://shaan-server:9443
url: https://portainer:9443
env: 3
key: "{{HOMEPAGE_VAR_PORTAINER_KEY}}"
- "Pi-Hole Adblocker":
icon: sh-pi-hole
href: "http://192.168.50.2:6060/admin/login"
href: "{{HOMEPAGE_VAR_PIHOLE_ADDRESS}}/admin/login"
description: DNS
server: my-docker
container: pihole
widget:
type: pihole
url: http://shaan-server:6060
url: "{{HOMEPAGE_VAR_PIHOLE_ADDRESS}}"
version: 6 # required if running v6 or higher, defaults to 5
key: "{{HOMEPAGE_VAR_PIHOLE_PASSWORD}}"
- "Network_2":
- "Scrutiny":
icon: sh-scrutiny
href: http://192.168.50.2:4545 #"{{HOMEPAGE_VAR_SCRUTINY_ADDRESS}}""
href: "{{HOMEPAGE_VAR_SCRUTINY_HOST}}"
description: S.M.A.R.T.
server: my-docker
container: scrutiny
widget:
type: scrutiny
url: http://192.168.50.2:4545 #"{{HOMEPAGE_VAR_SCRUTINY_ADDRESS}}"
url: "{{HOMEPAGE_VAR_SCRUTINY_ADDRESS}}"
- "Karakeep":
icon: sh-karakeep-light
href: http://192.168.50.2:4545 #"{{HOMEPAGE_VAR_SCRUTINY_ADDRESS}}""
href: "{{HOMEPAGE_VAR_KARAKEEP_HOST}}"
description: Bookmarks
server: my-docker
container: karakeep
widget:
type: karakeep
url: http://shaan-server:4621
url: "{{HOMEPAGE_VAR_KARAKEEP_ADDRESS}}"
key: ak2_bfee1806ec533a9557bd_940feeb8fbe798d465ab6f90a3562c43
- "Network_qbit":
- "Prowlarr >":
Regular → Executable
+4 -4
View File
@@ -6,7 +6,7 @@ instanceName: torrent-stack
# Aesthetics
background:
image: /images/wallhaven-pk997m.jpg
image: ./images/wallhaven-pk997m.jpg
#blur: sm # Options: sm, md, lg, xl
#saturate: 10
#brightness: 10
@@ -44,20 +44,20 @@ layout:
tab: Apps
iconsOnly: false
header: true
disableCollapse: true
initiallyCollapsed: false
style: row
columns: 3
"Admin":
tab: Apps
iconsOnly: false
header: true
disableCollapse: true
initiallyCollapsed: false
style: row
columns: 3
"Bookmarks":
tab: Apps
header: true
initiallyCollapsed: true
initiallyCollapsed: false
style: row
columns: 3
"Top":
Regular → Executable
View File
+157
View File
@@ -0,0 +1,157 @@
# =============================================================================
# Torrent Stack (infra) - DNS, management & monitoring
#
# Split from compose.yml on 2026-09-11 - verbatim service blocks.
# Shared top-level networks:/volumes: are repeated in each subfile so it also
# works standalone (docker compose -f infra.yml up -d). Identical copies merge
# cleanly under compose.yml's `include:` with no change to the live config.
# Keep ./ paths, container names and /mnt data as-is.
# =============================================================================
networks:
#caddy_net:
# external: true
tdarr-net:
driver: bridge
default:
name: portainer_network
volumes:
#caddy_config:
#caddy_data:
# external: true
#myspeed:
# external: true
portainer_data:
name: portainer_data
services:
# ---------------------------------------------------------------------------
# Monitoring
# ---------------------------------------------------------------------------
# glances:
# image: ${GLANCES_IMAGE}
# container_name: glances
# network_mode: host
# volumes:
# - /etc/os-release:/etc/os-release:ro
# - /:/host:ro
# #- /etc/localtime:/etc/localtime:ro
# #- /etc/timezone:/etc/timezone:ro
# environment:
# - "GLANCES_OPT=-w --password ${GLANCES_PASSWORD}"
# - PUID=${HOST_PUID:-1000}
# - PGID=${HOST_PGID:-1000}
# - TZ=${TIMEZONE}
# healthcheck:
# test:
# ["CMD", "curl", "-f", "http://localhost:${GLANCES_PORT}/api/4/status"]
# interval: 1m
# timeout: 10s
# retries: 3
# start_period: 60s
# security_opt:
# - no-new-privileges:true
# mem_limit: 256m
# cpus: 1.0
# devices:
# - /dev/dri:/dev/dri
# restart: unless-stopped
# # Uncomment to protect Glances WebUI by a login/password (add --password to GLANCES_OPT)
# secrets:
# - source: glances_password
# target: /root/.config/glances/<login>.pwd
# secrets:
# glances_password:
# file: ./secrets/glances_password
autoheal:
image: ${AUTOHEAL_IMAGE}
container_name: autoheal
volumes:
- /var/run/docker.sock:/var/run/docker.sock
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- AUTOHEAL_CONTAINER_LABEL=all
- AUTOHEAL_INTERVAL=30
- AUTOHEAL_START_PERIOD=60
- TZ=${TIMEZONE}
security_opt:
- no-new-privileges:true
read_only: true
mem_limit: 64m
cpus: 0.25
restart: unless-stopped
scrutiny:
image: ${SCRUTINY_IMAGE}
container_name: scrutiny
cap_add:
- SYS_RAWIO
ports:
- ${SCRUTINY_PORT}:8080
- ${SCRUTINY_ADMIN_PORT}:8086
volumes:
- /run/udev:/run/udev:ro
- ./scrutiny/config:/opt/scrutiny/config
- ./scrutiny/influxdb:/opt/scrutiny/influxdb
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- TZ=${TIMEZONE}
security_opt:
- no-new-privileges:true
mem_limit: 128m
cpus: 0.25
devices:
- /dev/nvme0n1
- /dev/sda
restart: unless-stopped
# ---------------------------------------------------------------------------
# Infrastructure
# ---------------------------------------------------------------------------
pihole:
image: ${PIHOLE_IMAGE}
container_name: pihole
cap_add:
- NET_ADMIN # Allows managing network interfaces & sockets
- NET_BIND_SERVICE # Allows non-root users to bind to ports < 1024 (port 53 & 123)
- SYS_TIME # Resolves the NTP system time warning
network_mode: host
volumes:
- ./etc-pihole:/etc/pihole
- ./etc-dnsmasq.d:/etc/dnsmasq.d
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- TZ=${TIMEZONE}
- FTLCONF_misc_etc_dnsmasq_d=true
- FTLCONF_webserver_port=${PIHOLE_PORT:-80}
# security_opt:
# - no-new-privileges:true
mem_limit: 256m
cpus: 1.0
restart: unless-stopped
portainer:
image: ${PORTAINER_IMAGE}
container_name: portainer
ports:
- ${PORTAINER_PORT}:${PORTAINER_PORT}
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- portainer_data:/data
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- TZ=${TIMEZONE}
security_opt:
- no-new-privileges:true
mem_limit: 256m
cpus: 1.0
restart: unless-stopped
+517
View File
@@ -0,0 +1,517 @@
# =============================================================================
# Torrent Stack (media) - VPN, *arr stack & media tooling
#
# Split from compose.yml on 2026-09-11 - verbatim service blocks.
# Shared top-level networks:/volumes: are repeated in each subfile so it also
# works standalone (docker compose -f media.yml up -d). Identical copies merge
# cleanly under compose.yml's `include:` with no change to the live config.
# Keep ./ paths, container names and /mnt data as-is.
# =============================================================================
networks:
#caddy_net:
# external: true
tdarr-net:
driver: bridge
default:
name: portainer_network
volumes:
#caddy_config:
#caddy_data:
# external: true
#myspeed:
# external: true
portainer_data:
name: portainer_data
redis-data:
driver: local
services:
# ---------------------------------------------------------------------------
# VPN & Download
# ---------------------------------------------------------------------------
gluetun:
image: ${GLUETUN_IMAGE}
container_name: gluetun
env_file:
- .env
- ./env/.gluetun.env
cap_add:
- NET_ADMIN
ports:
- 8877:8000/tcp
- ${QBITTORRENT_PORT}:${QBITTORRENT_PORT}
- ${PROWLARR_PORT}:${PROWLARR_PORT}
- ${SONARR_PORT}:${SONARR_PORT}
- ${RADARR_PORT}:${RADARR_PORT}
- ${LIDARR_PORT}:${LIDARR_PORT}
- ${BAZARR_PORT}:${BAZARR_PORT}
volumes:
- ./gluetun-data:/tmp/gluetun:rw
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- VPN_SERVICE_PROVIDER=protonvpn
- VPN_TYPE=wireguard
- HTTP_CONTROL_SERVER=ON
- WIREGUARD_MTU=1280
- VPN_PORT_FORWARDING=on
- VPN_PORT_FORWARDING_PROVIDER=protonvpn
- SERVER_COUNTRIES=Netherlands
- NETWORK_LOCAL_ADDRESSES=192.168.50.0/24,172.16.0.0/12
- FIREWALL_OUTBOUND_SUBNETS=192.168.50.0/24
- HEALTH_SERVER_ADDRESS=0.0.0.0:8877
- HEALTH_TARGET_ADDRESSES=${EXTERNAL_IPV4}:${PLEX_PFWD_PORT}
- TZ=${TIMEZONE}
- DNS_UPSTREAM_RESOLVER_TYPE=doh
healthcheck:
test:
[
"CMD-SHELL",
"wget -qO- '${WEB_PROTOCOL}://${EXTERNAL_IPV4}:${PLEX_PFWD_PORT}/identity' | grep -q 'MediaContainer' || exit 1",
]
interval: 1m30s
timeout: 15s
retries: 3
start_period: 45s
security_opt:
- no-new-privileges:true
mem_limit: 256m
cpus: 1.0
devices:
- /dev/net/tun:/dev/net/tun
restart: no
qbittorrent:
image: ${QBITTORRENT_IMAGE}
container_name: qbittorrent
network_mode: "container:gluetun"
depends_on:
gluetun:
condition: service_healthy
volumes:
- ./qbittorrent:/config
- ${DISK1}:${DISK1}
- ${DISK2}:${DISK2}
- ./gluetun-data:/tmp/gluetun
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- PUID=${HOST_PUID:-1000}
- PGID=${HOST_PGID:-1000}
- TZ=${TIMEZONE}
- WEBUI_PORT=${QBITTORRENT_PORT}
labels:
- "autoheal=true"
healthcheck:
test:
[
"CMD-SHELL",
"curl -sf '${WEB_PROTOCOL}://${EXTERNAL_IPV4}:${PLEX_PFWD_PORT}/identity' | grep -q 'MediaContainer' || exit 1",
]
interval: 1m
timeout: 10s
retries: 3
start_period: 30s
security_opt:
- no-new-privileges:true
mem_limit: 512m
cpus: 1.0
restart: unless-stopped
port-updater:
image: curlimages/curl:8.21.0
container_name: port-updater
network_mode: "container:gluetun"
depends_on:
gluetun:
condition: service_healthy
volumes:
- ./gluetun-data:/tmp/gluetun:ro
entrypoint: ["/bin/sh", "-c"]
command:
- |
trap 'exit 0' SIGTERM;
while true; do
while [ ! -s /tmp/gluetun/forwarded_port ]; do
echo "Waiting for Gluetun to provide a port..."
sleep 5
done
read -r PORT_VAL < /tmp/gluetun/forwarded_port;
CLEAN_PORT=$$(echo "$$PORT_VAL" | tr -d '\r\n ');
if [ -n "$$CLEAN_PORT" ]; then
echo "Updating qBit to port: $$CLEAN_PORT";
sleep 10;
curl -s -X POST -d "json={\"listen_port\":$$CLEAN_PORT}" http://localhost:${QBITTORRENT_PORT}/api/v2/app/setPreferences;
sleep 3600 & wait $$!;
else
sleep 10
fi
done
labels:
- "autoheal=true"
healthcheck:
test:
[
"CMD-SHELL",
"curl -sf '${WEB_PROTOCOL}://${EXTERNAL_IPV4}:${PLEX_PFWD_PORT}/identity' | grep -q 'MediaContainer' || exit 1",
]
interval: 1m
timeout: 10s
retries: 3
start_period: 30s
security_opt:
- no-new-privileges:true
read_only: true
tmpfs:
- /tmp
mem_limit: 64m
cpus: 0.25
restart: unless-stopped
# ---------------------------------------------------------------------------
# *arr Stack
# ---------------------------------------------------------------------------
prowlarr:
image: ${PROWLARR_IMAGE}
container_name: prowlarr
network_mode: "container:gluetun"
depends_on:
gluetun:
condition: service_healthy
volumes:
- ./prowlarr:/config
- ./notify.sh:/notify.sh:ro
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- PUID=${HOST_PUID:-1000}
- PGID=${HOST_PGID:-1000}
- TZ=${TIMEZONE}
labels:
- "autoheal=true"
healthcheck:
test:
[
"CMD-SHELL",
"curl -sf '${WEB_PROTOCOL}://${EXTERNAL_IPV4}:${PLEX_PFWD_PORT}/identity' | grep -q 'MediaContainer' || exit 1",
]
interval: 1m
timeout: 10s
retries: 3
start_period: 30s
security_opt:
- no-new-privileges:true
mem_limit: 256m
cpus: 1.0
restart: unless-stopped
sonarr:
image: ${SONARR_IMAGE}
container_name: sonarr
network_mode: "container:gluetun"
depends_on:
gluetun:
condition: service_healthy
volumes:
- ./sonarr:/config
- ${DISK1}:${DISK1}
- ${DISK2}:${DISK2}
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- PUID=${HOST_PUID:-1000}
- PGID=${HOST_PGID:-1000}
- TZ=${TIMEZONE}
labels:
- "autoheal=true"
healthcheck:
test:
[
"CMD-SHELL",
"curl -sf '${WEB_PROTOCOL}://${EXTERNAL_IPV4}:${PLEX_PFWD_PORT}/identity' | grep -q 'MediaContainer' || exit 1",
]
interval: 1m
timeout: 10s
retries: 3
start_period: 60s
security_opt:
- no-new-privileges:true
mem_limit: 256m
cpus: 1.0
restart: unless-stopped
radarr:
image: ${RADARR_IMAGE}
container_name: radarr
network_mode: "container:gluetun"
depends_on:
gluetun:
condition: service_healthy
volumes:
- ./radarr:/config
- ${DISK1}:${DISK1}
- ${DISK2}:${DISK2}
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- PUID=${HOST_PUID:-1000}
- PGID=${HOST_PGID:-1000}
- TZ=${TIMEZONE}
labels:
- "autoheal=true"
healthcheck:
test:
[
"CMD-SHELL",
"curl -sf '${WEB_PROTOCOL}://${EXTERNAL_IPV4}:${PLEX_PFWD_PORT}/identity' | grep -q 'MediaContainer' || exit 1",
]
interval: 1m
timeout: 10s
retries: 3
start_period: 30s
security_opt:
- no-new-privileges:true
mem_limit: 256m
cpus: 1.0
restart: unless-stopped
lidarr:
image: ${LIDARR_IMAGE}
container_name: lidarr
network_mode: "container:gluetun"
depends_on:
gluetun:
condition: service_healthy
volumes:
- ./lidarr:/music
- ./lidarr:/config
- ${DISK1}:${DISK1}
- ${DISK2}:${DISK2}
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- PUID=${HOST_PUID:-1000}
- PGID=${HOST_PGID:-1000}
- TZ=${TIMEZONE}
labels:
- "autoheal=true"
healthcheck:
test:
[
"CMD-SHELL",
"curl -sf '${WEB_PROTOCOL}://${EXTERNAL_IPV4}:${PLEX_PFWD_PORT}/identity' | grep -q 'MediaContainer' || exit 1",
]
interval: 1m
timeout: 10s
retries: 3
start_period: 30s
security_opt:
- no-new-privileges:true
mem_limit: 256m
cpus: 1.0
restart: unless-stopped
bazarr:
image: ${BAZARR_IMAGE}
container_name: bazarr
network_mode: "container:gluetun"
depends_on:
gluetun:
condition: service_healthy
volumes:
- ./bazarr:/config
- ${DISK1}:${DISK1}
- ${DISK2}:${DISK2}
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- PUID=${HOST_PUID:-1000}
- PGID=${HOST_PGID:-1000}
- TZ=${TIMEZONE}
labels:
- "autoheal=true"
healthcheck:
test:
[
"CMD-SHELL",
"curl -sf '${WEB_PROTOCOL}://${EXTERNAL_IPV4}:${PLEX_PFWD_PORT}/identity' | grep -q 'MediaContainer' || exit 1",
]
interval: 1m
timeout: 10s
retries: 3
start_period: 30s
security_opt:
- no-new-privileges:true
mem_limit: 256m
cpus: 1.0
restart: unless-stopped
# ---------------------------------------------------------------------------
# Media
# ---------------------------------------------------------------------------
tautulli:
image: ${TAUTULLI_IMAGE}
container_name: tautulli
#networks:
# - caddy_net
ports:
- ${TAUTULLI_PORT}:${TAUTULLI_PORT}
volumes:
- ./tautulli:/config
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- PUID=${HOST_PUID:-1000}
- PGID=${HOST_PGID:-1000}
- TZ=${TIMEZONE}
security_opt:
- no-new-privileges:true
mem_limit: 256m
cpus: 1.0
restart: unless-stopped
tdarr:
image: ${TDARR_IMAGE}
container_name: tdarr
networks:
- tdarr-net
ports:
- ${TDARR_PORT}:${TDARR_PORT}
- ${TDARR_NODE_PORT}:${TDARR_NODE_PORT}
volumes:
- ./tdarr/server:/app/server
- ./tdarr/configs:/app/configs
- ./tdarr/temp:/temp
- ${DISK1}:${DISK1}
- ${DISK2}:${DISK2}
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- TZ=${TIMEZONE}
- PUID=${HOST_PUID:-1000}
- PGID=${HOST_PGID:-1000}
- UMASK_SET=002
- serverIP=0.0.0.0
security_opt:
- no-new-privileges:true
mem_limit: 512m
cpus: 2.0
restart: unless-stopped
tdarr-node:
image: ${TDARR_NODE_IMAGE}
container_name: tdarr-node
networks:
- tdarr-net
volumes:
- ./tdarr/configs:/app/configs
- ./tdarr/logs:/app/logs
- ./tdarr/temp:/temp
- ${DISK1}:${DISK1}
- ${DISK2}:${DISK2}
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- TZ=${TIMEZONE}
- PUID=${HOST_PUID:-1000}
- PGID=${HOST_PGID:-1000}
- nodeID=${TDARR_NODE_ID}
- nodeIP=0.0.0.0
- serverIP=tdarr
- serverPort=${TDARR_NODE_PORT}
security_opt:
- no-new-privileges:true
mem_limit: 4g
cpus: 4.0
devices:
- /dev/dri:/dev/dri
restart: unless-stopped
seerr:
image: ${SEERR_IMAGE}
container_name: seerr
# networks:
# - caddy_net
ports:
- ${SEERR_PORT}:${SEERR_PORT}
volumes:
- ./seerr:/app/config
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- LOG_LEVEL=info
- TZ=${TIMEZONE}
- PUID=${HOST_PUID:-1000}
- PGID=${HOST_PGID:-1000}
labels:
- "autoheal=true"
init: true
working_dir: "/app"
healthcheck:
test:
[
"CMD-SHELL",
"wget -qO- '${WEB_PROTOCOL}://${EXTERNAL_IPV4}:${PLEX_PFWD_PORT}/identity' | grep -q 'MediaContainer' || exit 1",
]
interval: 1m
timeout: 10s
retries: 3
start_period: 60s
security_opt:
- no-new-privileges:true
mem_limit: 256m
cpus: 1.0
restart: unless-stopped
spotizerr:
image: spotizerrphoenix/spotizerr
user: "1000:1000" # Spotizerr user:group ids
volumes:
# Ensure these directories and the .cache file exist and are writable by the container user
- ./spotizerr:/app/data # data directory, contains config, creds, watch, history
- ${DISK1}/media/Music/spotizerr:/app/downloads # downloads directory, contains downloaded files
- ./spotizerr/logs:/app/logs # logs directory, contains logs
- ./spotizerr/.cache:/app/.cache # cache file
ports:
# Port to expose the app on
- 7171:7171
container_name: spotizerr
restart: unless-stopped
env_file:
# Ensure you have a .env file in the root of the project, with the correct values
- ./.env
depends_on:
- redis
redis:
image: redis:alpine
container_name: spotizerr-redis
restart: unless-stopped
env_file:
- .env
volumes:
- redis-data:/data
command: sh -c 'redis-server --requirepass "$REDIS_PASSWORD" --appendonly yes'
# Anubis anti-bot proxy for Seerr (uncomment to enable):
#seerr-anubis:
# image: ghcr.io/techarohq/anubis:latest
# container_name: seerr-anubis
# networks:
# - caddy_net
# environment:
# - BIND=:55055
# - TARGET=http://seerr:${SEERR_PORT}
# security_opt:
# - no-new-privileges:true
# mem_limit: 128m
# cpus: 0.5
# restart: unless-stopped
+2 -9
View File
@@ -2,12 +2,5 @@
sleep 15
cd /home/shaan/torrent-stack || exit
docker compose up -d gluetun & sleep 90
docker compose up -d
sleep 5
cd /home/shaan/torrent-stack/odysseus || exit
git pull . dev
docker compose pull
docker compose up -d
docker compose config ||> /tmp/startup.log && exit 1
docker compose up -d 2>&1 | tee /tmp/startup.log
+287
View File
@@ -0,0 +1,287 @@
# =============================================================================
# Torrent Stack (web) - dashboard, edge & AI services
#
# Split from compose.yml on 2026-09-11 - verbatim service blocks.
# Shared top-level networks:/volumes: are repeated in each subfile so it also
# works standalone (docker compose -f web.yml up -d). Identical copies merge
# cleanly under compose.yml's `include:` with no change to the live config.
# Keep ./ paths, container names and /mnt data as-is.
# =============================================================================
networks:
#caddy_net:
# external: true
tdarr-net:
driver: bridge
default:
name: portainer_network
volumes:
#caddy_config:
#caddy_data:
# external: true
#myspeed:
# external: true
portainer_data:
name: portainer_data
services:
# ---------------------------------------------------------------------------
# Dashboard
# ---------------------------------------------------------------------------
homepage:
image: ${HOMEPAGE_IMAGE}
container_name: homepage
env_file:
- .env
- ./env/.homepage.env
#networks:
# - caddy_net
ports:
- ${HOMEPAGE_PORT}:${HOMEPAGE_PORT}
volumes:
- ./homepage:/app/config
- ./homepage/images:/app/public/images
- /var/run/docker.sock:/var/run/docker.sock:ro
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- "HOMEPAGE_ALLOWED_HOSTS=gethomepage.dev,${LOCAL_IPV4}:${HOMEPAGE_PORT},${SERVER_NAME}:${HOMEPAGE_PORT},${SERVER_NAME}.${TAILNET_NAME}:${HOMEPAGE_PORT},host.docker.internal:{HOMEPAGE_PORT}"
- "HOMEPAGE_VAR_DISK1=${DISK1}"
- "HOMEPAGE_VAR_DISK2=${DISK2}"
- "HOMEPAGE_VAR_DISK3=${DISK3}"
- "HOMEPAGE_VAR_WEB_PROTOCOL=${WEB_PROTOCOL}"
- "HOMEPAGE_VAR_LOCAL_IPV4=${LOCAL_IPV4}"
- "HOMEPAGE_VAR_SERVER_NAME=${SERVER_NAME}"
- "HOMEPAGE_VAR_PLEX_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${PLEX_PORT}"
- "HOMEPAGE_VAR_QBITTORRENT_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${QBITTORRENT_PORT}"
- "HOMEPAGE_VAR_PROWLARR_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${PROWLARR_PORT}"
- "HOMEPAGE_VAR_SONARR_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${SONARR_PORT}"
- "HOMEPAGE_VAR_RADARR_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${RADARR_PORT}"
- "HOMEPAGE_VAR_LIDARR_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${LIDARR_PORT}"
- "HOMEPAGE_VAR_BAZARR_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${BAZARR_PORT}"
- "HOMEPAGE_VAR_TAUTULLI_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${TAUTULLI_PORT}"
- "HOMEPAGE_VAR_TDARR_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${TDARR_PORT}"
- "HOMEPAGE_VAR_HOMEPAGE_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${HOMEPAGE_PORT}"
- "HOMEPAGE_VAR_GLANCES_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${GLANCES_PORT}"
- "HOMEPAGE_VAR_SEERR_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${SEERR_PORT}"
- "HOMEPAGE_VAR_OPENWEBUI_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${OPENWEBUI_PORT}"
- "HOMEPAGE_VAR_PIHOLE_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${PIHOLE_PORT}"
- "HOMEPAGE_VAR_PORTAINER_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${PORTAINER_PORT}"
- "HOMEPAGE_VAR_SCRUTINY_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${SCRUTINY_PORT}"
- "HOMEPAGE_VAR_KARAKEEP_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${KARAKEEP_PORT}"
- "HOMEPAGE_VAR_VLLM_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${VLLM_PORT}"
- "HOMEPAGE_VAR_PLEX_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${PLEX_PORT}"
- "HOMEPAGE_VAR_QBITTORRENT_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${QBITTORRENT_PORT}"
- "HOMEPAGE_VAR_PROWLARR_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${PROWLARR_PORT}"
- "HOMEPAGE_VAR_SONARR_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${SONARR_PORT}"
- "HOMEPAGE_VAR_RADARR_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${RADARR_PORT}"
- "HOMEPAGE_VAR_LIDARR_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${LIDARR_PORT}"
- "HOMEPAGE_VAR_BAZARR_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${BAZARR_PORT}"
- "HOMEPAGE_VAR_TAUTULLI_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${TAUTULLI_PORT}"
- "HOMEPAGE_VAR_TDARR_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${TDARR_PORT}"
- "HOMEPAGE_VAR_HOMEPAGE_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${HOMEPAGE_PORT}"
- "HOMEPAGE_VAR_GLANCES_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${GLANCES_PORT}"
- "HOMEPAGE_VAR_SEERR_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${SEERR_PORT}"
- "HOMEPAGE_VAR_OPENWEBUI_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${OPENWEBUI_PORT}"
- "HOMEPAGE_VAR_PIHOLE_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${PIHOLE_PORT}"
- "HOMEPAGE_VAR_PORTAINER_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${PORTAINER_PORT}"
- "HOMEPAGE_VAR_SCRUTINY_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${SCRUTINY_PORT}"
- "HOMEPAGE_VAR_KARAKEEP_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${KARAKEEP_PORT}"
- "HOMEPAGE_VAR_VLLM_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${VLLM_PORT}"
- TZ=${TIMEZONE}
security_opt:
- no-new-privileges:true
mem_limit: 256m
cpus: 1
# extra_hosts:
# - "host.docker.internal:${LOCAL_IPV4}"
restart: unless-stopped
# ---------------------------------------------------------------------------
# Edge
# ---------------------------------------------------------------------------
cloudflared:
image: cloudflare/cloudflared:latest
container_name: cloudflared
env_file:
- ./env/.cloudflared.env
command: tunnel --no-autoupdate run # --token ${CLOUDFLARED_KEY}
security_opt:
- no-new-privileges:true
read_only: true
mem_limit: 128m
cpus: 0.5
restart: always
caddy:
image: ${CADDY_IMAGE}
container_name: caddy
#networks:
# - caddy_net
ports:
- 82:80
- 444:443
volumes:
- ./caddy/Caddyfile:/etc/caddy/Caddyfile
- ./caddy/site:/srv
- ./caddy/caddy_data:/data
- ./caddy/caddy_config:/config
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- TZ=${TIMEZONE}
security_opt:
- no-new-privileges:true
mem_limit: 128m
cpus: 0.5
restart: unless-stopped
# ---------------------------------------------------------------------------
# Web & AI
# ---------------------------------------------------------------------------
openwebui:
image: ghcr.io/open-webui/open-webui:main-slim
container_name: openwebui
ports:
- "7001:8080"
volumes:
- ./openwebui:/app/backend/data
mem_limit: 1g
cpus: 1.0
searxng:
image: ${SEARXNG_IMAGE}
container_name: searxng
ports:
- "5080:8080"
volumes:
- ./searxng:/etc/searxng
environment:
- SEARXNG_SECRET=abcd
restart: unless-stopped
vllm:
image: vllm/vllm-openai-xpu@sha256:f01e24f6c7ff01f1e0662234255a1372297d1dbd89d003cf13c8fad3eab1ba4f
container_name: vllm
network_mode: host
ipc: host
privileged: true
devices:
- "/dev/dri:/dev/dri"
volumes:
- ~/llm/huggingface:/root/.cache/huggingface
- ~/llm/:/llm/
- ./vllm/xpu-patches:/patches/:ro
environment:
- "HUGGING_FACE_HUB_TOKEN=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"
- "SYCL_PI_LEVEL_ZERO_USE_IMMEDIATE_COMMANDLISTS=1"
- "ZES_ENABLE_SYSMAN=1"
- "ONEAPI_DEVICE_SELECTOR=level_zero:0"
- "UR_L0_ENABLE_RELAXED_ALLOCATION_LIMITS=1"
- "VLLM_XPU_ENABLE_XPU_GRAPH=1"
- "VLLM_ALLOW_LONG_MAX_MODEL_LEN=1"
- "VLLM_WORKER-MULTIPROC_METHOD=spawn"
- "VLLM_TARGET_DEVICE=xpu"
- "ZE_FLAT_DEVICE_HIERARCHY=COMPOSITE"
- "ZE_AFFINITY_MASK=0"
- "B70_MTP_BF16_DRAFT=1"
- "B70_DRAFT_LMHEAD_INT4=1"
- "B70_DRAFT_MTP_INT4=1"
- "PYTORCH_ALLOC_CONF=expandable_segments:True"
entrypoint: ["/bin/bash", "-lc"]
command:
- |
set -e
python /patches/patch_mtp_nightly.py
python /patches/patch_mtp_boundary.py
python /patches/patch_gdn_mixed_split_v5.py
python /patches/patch_draft_lmhead_int4.py
python /patches/patch_draft_mtp_int4.py
exec vllm serve /llm/Qwen3.8-27B-GPTQINT4-G128-MTP-BF16 \
--quantization gptq \
--dtype float16 \
--max-model-len 131072 \
--gpu-memory-utilization 0.88 \
--kv-cache-dtype fp8 \
--port 5081 \
--max-num-seqs 64 \
--block-size 64 \
--max-num-batched-tokens 8192 \
--served-model-name 'Qwen3.8' \
--language-model-only \
--speculative-config '{"method":"mtp","num_speculative_tokens":4}' \
--reasoning-parser qwen3 \
--tool-call-parser qwen3_coder \
--default-chat-template-kwargs '{"enable_thinking": false, "reasoning_effort": "medium", "reasoning_preserve": "true"}' \
--enable-auto-tool-choice \
--disable-sliding-window \
--enable-prefix-caching \
--enable-chunked-prefill
restart: on-failure:5
# ---------------------------------------------------------------------------
# Karakeep
# ---------------------------------------------------------------------------
karakeep:
image: ghcr.io/karakeep-app/karakeep:${KARAKEEP_VERSION:-release}
container_name: karakeep
env_file:
- ./env/.karakeep.env
ports:
- ${KARAKEEP_PORT}:3000 # must be 3000 as per the dev
volumes:
- ./karakeep-data:/data
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- MEILI_ADDR=http://meilisearch:${MEILI_PORT}
- BROWSER_WEB_URL=http://chrome:${KARAKEEP_CHROME_PORT}
- DATA_DIR=/data
security_opt:
- no-new-privileges:true
mem_limit: 512m
cpus: 1.0
restart: unless-stopped
chrome:
image: zenika/alpine-chrome:124
container_name: karakeep-chromebrowser
command:
- --no-sandbox
- --disable-gpu
- --disable-dev-shm-usage
- --remote-debugging-address=0.0.0.0
- --remote-debugging-port=${KARAKEEP_CHROME_PORT}
- --hide-scrollbars
- --disable-blink-features=AutomationControlled
- --window-size=1440,900
security_opt:
- no-new-privileges:true
mem_limit: 1g
cpus: 1.0
restart: unless-stopped
meilisearch:
image: getmeili/meilisearch:v1.41.0
container_name: karakeep-meilisearch
env_file:
- .env
volumes:
- ./meilisearch:/meili_data
#- /etc/localtime:/etc/localtime:ro
#- /etc/timezone:/etc/timezone:ro
environment:
- MEILI_NO_ANALYTICS=true
security_opt:
- no-new-privileges:true
mem_limit: 512m
cpus: 1.0
restart: no