Files
torrent-stack/web.yml
T

298 lines
11 KiB
YAML

# =============================================================================
# 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:
tdarr-net:
driver: bridge
default:
name: portainer_network
volumes:
portainer_data:
name: portainer_data
services:
# ---------------------------------------------------------------------------
# Dashboard
# ---------------------------------------------------------------------------
homepage:
image: ${HOMEPAGE_IMAGE}
container_name: homepage
env_file:
- .env
- ./env/.homepage.env
network_mode: host
ports:
- ${HOMEPAGE_PORT}:${HOMEPAGE_PORT}
volumes:
- ./homepage:/app/config
- ./homepage/images:/app/public/images
- /var/run/docker.sock:/var/run/docker.sock: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},${SERVER_NAME}.local:${HOMEPAGE_PORT},${SERVER_NAME}.local"
- "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}s://${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_SPOTIZERR_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${SPOTIZERR_PORT}"
- "HOMEPAGE_VAR_GITEA_ADDRESS=${WEB_PROTOCOL}://${LOCAL_IPV4}:${GITEA_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}"
- "HOMEPAGE_VAR_SPOTIZERR_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${SPOTIZERR_PORT}"
- "HOMEPAGE_VAR_GITEA_HOST=${WEB_PROTOCOL}://${SERVER_NAME}:${GITEA_PORT}"
- TZ=${TIMEZONE}
security_opt:
- no-new-privileges:true
mem_limit: 256m
cpus: 1
restart: unless-stopped
# ---------------------------------------------------------------------------
# Edge
# ---------------------------------------------------------------------------
cloudflared-ai:
image: cloudflare/cloudflared:latest
container_name: cloudflared-ai
env_file:
- ./env/.cloudflared-ai.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
cloudflared-dhub:
image: cloudflare/cloudflared:latest
container_name: cloudflared-dhub
env_file:
- ./env/.cloudflared-dhub.env
command: tunnel --no-autoupdate run
security_opt:
- no-new-privileges:true
read_only: true
mem_limit: 128m
cpus: 0.5
restart: no
# ---------------------------------------------------------------------------
# Web & AI
# ---------------------------------------------------------------------------
openwebui:
image: ghcr.io/open-webui/open-webui:main-slim
container_name: openwebui
ports:
- "${OPENWEBUI_PORT}:8080"
volumes:
- ./openwebui:/app/backend/data
extra_hosts:
- "host.docker.internal:${LOCAL_IPV4}"
- "shaan-server:${LOCAL_IPV4}"
mem_limit: 1g
cpus: 1.0
restart: unless-stopped
searxng:
image: ${SEARXNG_IMAGE}
container_name: searxng
ports:
- "${SEARXNG_PORT}:8080"
volumes:
- ./searxng:/etc/searxng
environment:
- SEARXNG_SECRET=abcd
mem_limit: 1g
cpus: 1.0
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
cpus: 4.0
mem_limit: 16g
# ---------------------------------------------------------------------------
#
# Git
# ---------------------------------------------------------------------------
gitea:
image: docker.gitea.com/gitea:1.27.3
container_name: gitea
ports:
- "${GITEA_PORT}:${GITEA_PORT}"
- "${GITEA_SSH_PORT}:22"
volumes:
- ./gitea:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
environment:
- USER_UID=1000
- USER_GID=1000
mem_limit: 512m
cpus: 1.0
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
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
environment:
- MEILI_NO_ANALYTICS=true
security_opt:
- no-new-privileges:true
mem_limit: 512m
cpus: 1.0
restart: unless-stopped