# ============================================================================= # 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_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 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 # --------------------------------------------------------------------------- # Web & AI # --------------------------------------------------------------------------- openwebui: image: ghcr.io/open-webui/open-webui:main-slim container_name: openwebui ports: - "7001: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: - "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 cpus: 4.0 mem_limit: 16g # --------------------------------------------------------------------------- # # Git # --------------------------------------------------------------------------- gitea: image: docker.gitea.com/gitea:1.27.3 container_name: gitea ports: - "1333:1333" - "222:22" volumes: - ./gitea:/data - /etc/timezone:/etc/timezone:ro - /etc/localtime:/etc/localtime:ro environment: - USER_UID=1000 - USER_GID=1000 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: no