From 2b544ae6aabe1086fff8d5f7c9055d9b5c202988 Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Sat, 8 Aug 2026 17:00:48 +0200 Subject: [PATCH] chore(stacks): remove the deprecated stack copies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This repo was merged into system-admin-toj/containers/ and the copies here kept drifting. Stale infrastructure config is not inert: agents.yml here still carried OLLAMA_DEFAULT_MODEL=mistral-nemo:latest months after the live stack moved to gemma4:e2b, and that setting is what caused the 2026-08-07 outage — mistral-nemo held 9,262 MiB of an 11,264 MiB card, Whisper got 7 MiB, and Speaches returned CUDA OOM for hours while reporting healthy. Anyone deploying from this directory would have reproduced it exactly. The files stay in this repo's history; README.md explains how to read one back and points at the live location. Two had no counterpart in system-admin-toj and were never deployed: appwrite.yml, added in the most recent commit, and penpot.yml, whose service is decommissioned. Both are recoverable from history if wanted; neither should come back to this directory. Left alone deliberately: an uncommitted CONTAINERS.md edit, which is someone's unfinished work and would not have survived a delete. Co-Authored-By: Claude --- stacks/README.md | 155 ++------ stacks/adguard.yml | 56 --- stacks/agents.yml | 155 -------- stacks/appwrite.yml | 670 --------------------------------- stacks/authentik.yml | 214 ----------- stacks/core-api.yml | 101 ----- stacks/gitea.yml | 92 ----- stacks/headscale.yml | 63 ---- stacks/home-assistant.yml | 114 ------ stacks/library-desk.yml | 84 ----- stacks/media.yml | 275 -------------- stacks/models.yml | 199 ---------- stacks/neo4j.yml | 77 ---- stacks/nextcloud.yml | 111 ------ stacks/nginx-proxy-manager.yml | 42 --- stacks/open-webui.yml | 71 ---- stacks/paperless.yml | 110 ------ stacks/penpot.yml | 243 ------------ stacks/portainer.yml | 31 -- stacks/postgres-shared.yml | 137 ------- stacks/qdrant.yml | 69 ---- stacks/redis-shared.yml | 159 -------- stacks/samba.yml | 86 ----- stacks/scheduler.yml | 88 ----- stacks/searxng.yml | 226 ----------- stacks/tatlock-ui.yml | 44 --- stacks/watchtower.yml | 70 ---- stacks/wiki.yml | 97 ----- 28 files changed, 23 insertions(+), 3816 deletions(-) delete mode 100644 stacks/adguard.yml delete mode 100644 stacks/agents.yml delete mode 100644 stacks/appwrite.yml delete mode 100644 stacks/authentik.yml delete mode 100644 stacks/core-api.yml delete mode 100644 stacks/gitea.yml delete mode 100644 stacks/headscale.yml delete mode 100644 stacks/home-assistant.yml delete mode 100644 stacks/library-desk.yml delete mode 100644 stacks/media.yml delete mode 100644 stacks/models.yml delete mode 100644 stacks/neo4j.yml delete mode 100644 stacks/nextcloud.yml delete mode 100644 stacks/nginx-proxy-manager.yml delete mode 100644 stacks/open-webui.yml delete mode 100644 stacks/paperless.yml delete mode 100644 stacks/penpot.yml delete mode 100644 stacks/portainer.yml delete mode 100644 stacks/postgres-shared.yml delete mode 100644 stacks/qdrant.yml delete mode 100644 stacks/redis-shared.yml delete mode 100644 stacks/samba.yml delete mode 100644 stacks/scheduler.yml delete mode 100644 stacks/searxng.yml delete mode 100644 stacks/tatlock-ui.yml delete mode 100644 stacks/watchtower.yml delete mode 100644 stacks/wiki.yml diff --git a/stacks/README.md b/stacks/README.md index 2654f92..263d6fe 100644 --- a/stacks/README.md +++ b/stacks/README.md @@ -1,140 +1,31 @@ -# Docker Compose Stacks +# Docker Compose Stacks — REMOVED -This directory contains version-controlled Docker Compose files for all services in the tower-of-joy infrastructure. +**These files are gone deliberately. Do not restore them and do not deploy from +this directory.** -## Deployment +The live stack definitions are in the `system-admin-toj` repo: -review the http://core-api/docs openapi documentation for infrastructure management REST endpoints. + system-admin-toj/containers/stacks/*.yml -## Stack Inventory +`portainer-core` was merged into `system-admin-toj/containers/` and is +deprecated. The copies that lived here kept drifting out of date, and stale +infrastructure config is not harmless: this directory's `agents.yml` still +carried `OLLAMA_DEFAULT_MODEL=mistral-nemo:latest` long after the live stack had +moved to `gemma4:e2b`. That exact setting caused the 2026-08-07 outage, where +mistral-nemo held 9,262 MiB of an 11,264 MiB card and Whisper was left with 7 MiB +— Speaches returned CUDA OOM for hours while its container reported healthy. +Anyone deploying from here would have reproduced it. -### Phase 1: Foundation +Removed 2026-08-08. The files remain in this repo's git history if you need to +read one: -| Stack | File | Ports | GPU | Description | -|-------|------|-------|-----|-------------| -| **Portainer** | `portainer.yml` | 8080, 8443 | No | Container management UI | -| **Nginx Proxy Manager** | `nginx-proxy-manager.yml` | 8000, 80, 443 | No | Reverse proxy and unified web interface | -| **Models** | `models.yml` | 11434, 11500, 11510 | **Yes** | GPU model serving (Ollama, Stable Audio, TRELLIS) | + git log --oneline -- stacks/ + git show :stacks/.yml -### Phase 2: Networking +## Two that had no counterpart -| Stack | File | Ports | GPU | Description | -|-------|------|-------|-----|-------------| -| **Headscale** | `headscale.yml` | 8085, 9090 | No | Self-hosted Tailscale control server | - -### Phase 3: Optimization - -| Stack | File | Ports | GPU | Description | -|-------|------|-------|-----|-------------| -| **Watchtower** | `watchtower.yml` | - | No | Automatic container updates | -| **Duplicati** | `duplicati.yml` | 8200 | No | Backup solution | - -### Backlog: Applications - -| Stack | File | Ports | GPU | Description | -|-------|------|-------|-----|-------------| -| **Jellyfin** | `jellyfin.yml` | 8096, 8920, 7359, 1900 | **Yes** | Media server with GPU transcoding | -| **Nextcloud** | `nextcloud.yml` | 8082 | No | Cloud storage (includes DB and Redis) | -| **Gitea** | `gitea.yml` | 3002, 2222 | No | Git repository hosting (includes PostgreSQL) | -| **Samba** | `samba.yml` | 139, 445 | No | Network file sharing | -| **Home Assistant** | `home-assistant.yml` | 8123, 5580 | No | Smart home automation + Matter Server | - -## Port Allocation - -### Infrastructure Services (8000-8099) -- 8000: Nginx Proxy Manager (unified web interface) -- 8080: Portainer -- 8081: AMP (game servers - existing) -- 8082: Nextcloud -- 8085: Headscale -- 8096: Jellyfin - -### Home Automation Services (8100-8199) -- 8123: Home Assistant - -### Git & Development Services -- 2222: Gitea SSH -- 3002: Gitea HTTP - -### Backup Services -- 8200: Duplicati - -### ML/API Services (11000+) -- 11434: Ollama -- 11500: Stable Audio Open -- 11510: TRELLIS (3D model generation) - -### Network Services -- 80: HTTP (NPM reverse proxy) -- 443: HTTPS (NPM reverse proxy) -- 139, 445: Samba/SMB -- 9090: Headscale metrics - -## Storage Convention - -All stacks follow the dual-disk strategy: - -**SSD (Performance):** -- Configs: `/home/jpmschweitzer/docker-data//config` -- Cache: `/home/jpmschweitzer/docker-data//cache` -- Databases: `/home/jpmschweitzer/docker-data//db` - -**HDD (Capacity):** -- User content: `/mnt/media//data` -- Media files: `/mnt/media//media` -- Backups: `/mnt/media/backups/` - -## GPU Services - -Stacks requiring GPU access (marked with **Yes** above): -- `models.yml` - Ollama (LLM inference), Stable Audio (~6GB VRAM), TRELLIS (~6-8GB VRAM) -- `jellyfin.yml` - Hardware transcoding - -**Prerequisites:** -- NVIDIA Container Toolkit installed -- GPU verified: `docker run --rm --gpus all nvidia/cuda:11.4.0-base-ubuntu20.04 nvidia-smi` - -## Before Deploying - -1. **Review environment variables** - Change default passwords! -2. **Create directories** - Ensure volume paths exist -3. **Check ports** - Verify no conflicts with existing services -4. **GPU services** - Confirm NVIDIA toolkit installed -5. **Update STATUS.md** - Mark stack as deployed when complete - -## After Deploying - -1. **Test service** - Access web UI or API endpoint -2. **Check logs** - `docker logs ` -3. **Verify GPU** - `docker exec nvidia-smi` (if applicable) -4. **Update documentation** - Add to STATUS.md and CHANGELOG.md -5. **Configure backup** - Add to Duplicati backup job - -## Maintenance - -### Update a Stack -```bash -# Pull latest images -docker compose -f stacks/.yml pull - -# Recreate containers with new images -docker compose -f stacks/.yml up -d - -# Or let Watchtower handle it automatically -``` - -### Backup Stack Configuration -```bash -# Stacks are version-controlled in this directory -# Backup container data separately (see scripts/backup.sh) -``` - -### Troubleshooting -- Container won't start: `docker logs ` -- Port conflicts: `sudo netstat -tulpn | grep ` -- Permission issues: Check volume path ownership -- GPU not detected: Verify NVIDIA toolkit and restart Docker - ---- - -*For detailed implementation instructions, see containers/implementation-plan.md* +`appwrite.yml` and `penpot.yml` existed only here — neither is deployed, and +neither was migrated. If Appwrite is still wanted, recover it from history and +add it to `system-admin-toj/containers/stacks/` rather than reviving this +directory. Penpot is decommissioned (its Redis DB 10 is marked available again +in `CONTAINERS.md`). diff --git a/stacks/adguard.yml b/stacks/adguard.yml deleted file mode 100644 index ccb8698..0000000 --- a/stacks/adguard.yml +++ /dev/null @@ -1,56 +0,0 @@ -version: '3.8' - -# AdGuard Home - Network-wide DNS Ad Blocking -# Infrastructure Layer -# Ports: 53 (DNS), 3053 (Web UI) -# GPU: No -# Storage: SSD (configs and work data) - -services: - adguard: - image: adguard/adguardhome:latest - container_name: adguard - restart: unless-stopped - ports: - - "192.168.86.149:53:53/tcp" # DNS TCP (bound to LAN IP to avoid systemd-resolved conflict) - - "192.168.86.149:53:53/udp" # DNS UDP - - "3053:3000/tcp" # Web UI - volumes: - # SSD storage for configs and query logs - - /home/jpmschweitzer/docker-data/adguard/work:/opt/adguardhome/work - - /home/jpmschweitzer/docker-data/adguard/conf:/opt/adguardhome/conf - environment: - - TZ=Europe/Amsterdam - healthcheck: - test: ["CMD-SHELL", "nslookup localhost 127.0.0.1 || exit 1"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 30s - networks: - - docker-dataplane - -networks: - docker-dataplane: - external: true - name: docker-dataplane - -# Setup Instructions: -# 1. Deploy this stack via Portainer -# 2. Access setup wizard at http://192.168.86.149:3053 -# 3. Configure: -# - Admin interface: Listen on port 3053, all interfaces -# - DNS server: Listen on port 53, all interfaces -# - Create admin username/password -# 4. After setup, configure in AdGuard Home UI: -# - Settings → DNS settings → Upstream DNS: -# https://dns.quad9.net/dns-query -# - Enable "Parallel requests" -# - Filters → DNS blocklists → Add recommended lists -# 5. Configure router DNS to 192.168.86.149 -# -# Testing: -# dig @192.168.86.149 google.com # Should resolve -# dig @192.168.86.149 ads.google.com # Should be blocked -# -# Internal access: http://dns.schweitz.internal (requires NPM proxy + /etc/hosts entry) diff --git a/stacks/agents.yml b/stacks/agents.yml deleted file mode 100644 index 5068f74..0000000 --- a/stacks/agents.yml +++ /dev/null @@ -1,155 +0,0 @@ -version: '3.8' - -# Agents Stack - LLM Agent Services -# Purpose: AI agent orchestration APIs (Tatlock + Webber) -# Ports: 8000 (Tatlock), 8086 (Webber) -# Network: docker-dataplane -# Images: git.schweitz.internal/jpmschweitzer/tatlock, git.schweitz.internal/jpmschweitzer/webber - -services: - # ============================================ - # Tatlock - The Homelab Butler - # OpenAI-compatible API with LLM agent orchestration - # Port: 8000 - # ============================================ - tatlock: - image: git.schweitz.internal/jpmschweitzer/tatlock:latest - container_name: tatlock - restart: unless-stopped - - ports: - - "8000:8000" - - environment: - - ENVIRONMENT=production - - API_HOST=0.0.0.0 - - API_PORT=8000 - # Ollama (shared service) - - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} - - ANTHROPIC_MODEL=claude-sonnet-4-20250514 - - PREFER_CLOUD_BACKEND=truec - # Ollama (shared service) - - OLLAMA_HOST=http://ollama:11434 - - OLLAMA_DEFAULT_MODEL=mistral-nemo:latest - - OLLAMA_TIMEOUT=120 - # SearXNG (optional, shared service) - - SEARXNG_HOST=http://searxng:8080 - - SEARXNG_TIMEOUT=30 - # Qdrant (vector database) - - QDRANT_HOST=qdrant - - QDRANT_PORT=6333 - # Redis (shared service) - - REDIS_HOST=redis-shared - - REDIS_PORT=6379 - - REDIS_MEMORY_DB=1 - - REDIS_BENCHMARK_DB=6 - - REDIS_TIMEOUT=5 - # Features - - ENABLE_BENCHMARKS=true - - CORS_ORIGINS=["*"] - - PYTHONPATH=/app - # Library Desk API - - LIBRARY_DESK_HOST=http://library-desk:8089 - - LIBRARY_DESK_API_KEY=${LIBRARY_API_KEY} - - volumes: - - /home/jpmschweitzer/docker-data/tatlock/logs:/app/logs - - networks: - - docker-dataplane - - deploy: - resources: - limits: - cpus: '1.0' - memory: 1G - reservations: - memory: 256M - - labels: - - "com.centurylinklabs.watchtower.enable=true" - - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8000/health"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 30s - - # ============================================ - # Webber - LLM Agent Orchestration API - # Autonomous agent execution with tool use - # Port: 8086 - # ============================================ - webber: - image: git.schweitz.internal/jpmschweitzer/webber:latest - container_name: webber - restart: unless-stopped - - ports: - - "8086:8086" - - environment: - # App settings - - DEBUG=false - - LOG_LEVEL=INFO - - PYTHONPATH=/app - # Server config - - HOST=0.0.0.0 - - PORT=8086 - # CORS - - CORS_ORIGINS=["*"] - - CORS_METHODS=["*"] - - CORS_HEADERS=["*"] - # Ollama (shared service) - - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} - - ANTHROPIC_MODEL=claude-sonnet-4-20250514 - - PREFER_CLOUD_BACKEND=truec - # Ollama (shared service) - LLM Models hot in VRAM - - OLLAMA_URL=http://ollama:11434 - - OLLAMA_AGENT_MODEL=mistral-nemo:latest - - OLLAMA_EMBED_MODEL=nomic-embed-text:latest - # Auth - Tatlock API for user validation - - TATLOCK_API_URL=http://tatlock:8000 - - TATLOCK_API_KEY=${WEBBER_TATLOCK_API_KEY} - # Tool execution settings - - TOOL_TIMEOUT_SECONDS=30 - - SANDBOX_ENABLED=true - - ALLOWED_PATHS=["/app/sandbox","/tmp"] - # Session management - - SESSION_TTL_HOURS=24 - - MAX_CONTEXT_TOKENS=8192 - # Redis (shared service) - - REDIS_HOST=redis-shared - - REDIS_PORT=6379 - - REDIS_DB=9 - - volumes: - - /home/jpmschweitzer/docker-data/webber/logs:/app/logs - - /home/jpmschweitzer/docker-data/webber/sandbox:/app/sandbox - - networks: - - docker-dataplane - - deploy: - resources: - limits: - cpus: '1.0' - memory: 1G - reservations: - memory: 256M - - labels: - - "com.centurylinklabs.watchtower.enable=true" - - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8086/health"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 30s - -networks: - docker-dataplane: - external: true - name: docker-dataplane diff --git a/stacks/appwrite.yml b/stacks/appwrite.yml deleted file mode 100644 index cb3ae90..0000000 --- a/stacks/appwrite.yml +++ /dev/null @@ -1,670 +0,0 @@ -version: '3.8' - -# Appwrite - Backend-as-a-Service Platform -# Purpose: Self-hosted BaaS for development and testing (auth, database, storage, functions) -# Port: 8093 (console + API via Traefik), 8094 (HTTPS) -# GPU: No -# External: LAN only (dev/testing) -# Storage: SSD (all data), internal MariaDB + Redis -# Containers: ~20 (API, console, realtime, workers, schedulers, executor, MariaDB, Redis) - -x-logging: &x-logging - logging: - driver: 'json-file' - options: - max-file: '5' - max-size: '10m' - -x-appwrite-env: &appwrite-env - _APP_ENV: production - _APP_WORKER_PER_CORE: 6 - _APP_LOCALE: en - _APP_DOMAIN: 192.168.86.149 - _APP_DOMAIN_TARGET_CNAME: 192.168.86.149 - _APP_DOMAIN_TARGET_A: 192.168.86.149 - _APP_DOMAIN_FUNCTIONS: functions.localhost - _APP_DOMAIN_SITES: sites.localhost - _APP_CONSOLE_WHITELIST_ROOT: enabled - _APP_OPENSSL_KEY_V1: 2b8fd82b91b25205332c9d219a5838a752125965c6ea8ecfb0afe02e3307e163 - _APP_EXECUTOR_SECRET: gbnq4JwfnXBm4J7h7kUNEWt+OhwV3agu - _APP_EXECUTOR_HOST: http://openruntimes-executor/v1 - _APP_DB_ADAPTER: mariadb - _APP_DB_HOST: appwrite-mariadb - _APP_DB_PORT: '3306' - _APP_DB_SCHEMA: appwrite - _APP_DB_USER: appwrite - _APP_DB_PASS: ML8QbYgD05+7ZDHuRfmSTgDBr5VusxLi87ifyO5RlJg= - _APP_DB_ROOT_PASS: 2b8fd82b91b25205332c9d219a5838a752125965c6ea8ecfb0afe02e3307e163 - _APP_REDIS_HOST: appwrite-redis - _APP_REDIS_PORT: '6379' - _APP_REDIS_USER: '' - _APP_REDIS_PASS: '' - _APP_STORAGE_LIMIT: '30000000' - _APP_STORAGE_PREVIEW_LIMIT: '20000000' - _APP_STORAGE_ANTIVIRUS: disabled - _APP_STORAGE_DEVICE: local - _APP_USAGE_STATS: enabled - _APP_OPTIONS_ABUSE: enabled - _APP_OPTIONS_FORCE_HTTPS: disabled - _APP_OPTIONS_ROUTER_PROTECTION: disabled - _APP_OPTIONS_ROUTER_FORCE_HTTPS: disabled - _APP_SYSTEM_EMAIL_NAME: Appwrite - _APP_SYSTEM_EMAIL_ADDRESS: appwrite@schweitz.net - _APP_EMAIL_SECURITY: '' - _APP_SYSTEM_RESPONSE_FORMAT: '' - _APP_GRAPHQL_INTROSPECTION: disabled - _APP_GRAPHQL_MAX_BATCH_SIZE: '10' - _APP_GRAPHQL_MAX_COMPLEXITY: '250' - _APP_GRAPHQL_MAX_DEPTH: '3' - _APP_MAINTENANCE_INTERVAL: '86400' - _APP_MAINTENANCE_RETENTION_EXECUTION: '1209600' - _APP_MAINTENANCE_RETENTION_CACHE: '2592000' - _APP_MAINTENANCE_RETENTION_ABUSE: '86400' - _APP_MAINTENANCE_RETENTION_AUDIT: '1209600' - _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE: '1209600' - _APP_MAINTENANCE_RETENTION_USAGE_HOURLY: '8640000' - _APP_MAINTENANCE_RETENTION_SCHEDULES: '86400' - _APP_FUNCTIONS_TIMEOUT: '900' - _APP_SITES_TIMEOUT: '900' - _APP_COMPUTE_BUILD_TIMEOUT: '900' - _APP_COMPUTE_CPUS: '0' - _APP_COMPUTE_MEMORY: '0' - _APP_COMPUTE_SIZE_LIMIT: '30000000' - _APP_FUNCTIONS_RUNTIMES: node-22.0,python-3.12,php-8.3,dart-3.5,bun-1.1 - _APP_SITES_RUNTIMES: static-1.0 - _APP_COMPUTE_RUNTIMES_NETWORK: runtimes - _APP_COMPUTE_INACTIVE_THRESHOLD: '60' - _APP_COMPUTE_MAINTENANCE_INTERVAL: '60' - _APP_LOGGING_CONFIG: '' - _APP_DNS: '' - _APP_COMPRESSION_MIN_SIZE_BYTES: '1024' - TZ: Europe/Amsterdam - -services: - # ============================================ - # Traefik - Internal Reverse Proxy - # Port: 8093 (HTTP), 8094 (HTTPS) - # Routes /console to console, /v1/realtime to realtime, everything else to API - # ============================================ - appwrite-traefik: - image: traefik:3.6 - container_name: appwrite-traefik - <<: *x-logging - restart: unless-stopped - command: - - --providers.file.directory=/storage/config - - --providers.file.watch=true - - --providers.docker=true - - --providers.docker.exposedByDefault=false - - --providers.docker.constraints=Label(`traefik.constraint-label-stack`,`appwrite`) - - --entrypoints.appwrite_web.address=:80 - - --entrypoints.appwrite_websecure.address=:443 - ports: - - "8093:80" - - "8094:443" - volumes: - - /var/run/docker.sock:/var/run/docker.sock - - /home/jpmschweitzer/docker-data/appwrite/config:/storage/config:ro - - /home/jpmschweitzer/docker-data/appwrite/certificates:/storage/certificates:ro - depends_on: - - appwrite - networks: - - appwrite - - docker-dataplane - - # ============================================ - # Appwrite API Server - # Internal only - served via Traefik - # ============================================ - appwrite: - image: appwrite/appwrite:1.9.0 - container_name: appwrite - <<: *x-logging - restart: unless-stopped - networks: - - appwrite - labels: - - traefik.enable=true - - traefik.constraint-label-stack=appwrite - - traefik.docker.network=appwrite - - traefik.http.services.appwrite_api.loadbalancer.server.port=80 - - traefik.http.routers.appwrite_api_http.entrypoints=appwrite_web - - traefik.http.routers.appwrite_api_http.rule=PathPrefix(`/`) - - traefik.http.routers.appwrite_api_http.service=appwrite_api - - traefik.http.routers.appwrite_api_https.entrypoints=appwrite_websecure - - traefik.http.routers.appwrite_api_https.rule=PathPrefix(`/`) - - traefik.http.routers.appwrite_api_https.service=appwrite_api - - traefik.http.routers.appwrite_api_https.tls=true - - "com.centurylinklabs.watchtower.enable=false" - volumes: - - /home/jpmschweitzer/docker-data/appwrite/uploads:/storage/uploads:rw - - /home/jpmschweitzer/docker-data/appwrite/imports:/storage/imports:rw - - /home/jpmschweitzer/docker-data/appwrite/cache:/storage/cache:rw - - /home/jpmschweitzer/docker-data/appwrite/config:/storage/config:rw - - /home/jpmschweitzer/docker-data/appwrite/certificates:/storage/certificates:rw - - /home/jpmschweitzer/docker-data/appwrite/functions:/storage/functions:rw - - /home/jpmschweitzer/docker-data/appwrite/sites:/storage/sites:rw - - /home/jpmschweitzer/docker-data/appwrite/builds:/storage/builds:rw - depends_on: - appwrite-redis: - condition: service_healthy - appwrite-mariadb: - condition: service_healthy - environment: - <<: *appwrite-env - - # ============================================ - # Appwrite Console - Web UI - # Internal only - served via Traefik at /console - # ============================================ - appwrite-console: - image: appwrite/console:7.8.26 - container_name: appwrite-console - <<: *x-logging - restart: unless-stopped - networks: - - appwrite - labels: - - traefik.enable=true - - traefik.constraint-label-stack=appwrite - - traefik.docker.network=appwrite - - traefik.http.services.appwrite_console.loadbalancer.server.port=80 - - traefik.http.routers.appwrite_console_http.entrypoints=appwrite_web - - traefik.http.routers.appwrite_console_http.rule=PathPrefix(`/console`) - - traefik.http.routers.appwrite_console_http.service=appwrite_console - - traefik.http.routers.appwrite_console_https.entrypoints=appwrite_websecure - - traefik.http.routers.appwrite_console_https.rule=PathPrefix(`/console`) - - traefik.http.routers.appwrite_console_https.service=appwrite_console - - traefik.http.routers.appwrite_console_https.tls=true - - "com.centurylinklabs.watchtower.enable=false" - - # ============================================ - # Appwrite Realtime - WebSocket Server - # Internal only - served via Traefik at /v1/realtime - # ============================================ - appwrite-realtime: - image: appwrite/appwrite:1.9.0 - entrypoint: realtime - container_name: appwrite-realtime - <<: *x-logging - restart: unless-stopped - networks: - - appwrite - labels: - - traefik.enable=true - - traefik.constraint-label-stack=appwrite - - traefik.docker.network=appwrite - - traefik.http.services.appwrite_realtime.loadbalancer.server.port=80 - - traefik.http.routers.appwrite_realtime_ws.entrypoints=appwrite_web - - traefik.http.routers.appwrite_realtime_ws.rule=PathPrefix(`/v1/realtime`) - - traefik.http.routers.appwrite_realtime_ws.service=appwrite_realtime - - traefik.http.routers.appwrite_realtime_wss.entrypoints=appwrite_websecure - - traefik.http.routers.appwrite_realtime_wss.rule=PathPrefix(`/v1/realtime`) - - traefik.http.routers.appwrite_realtime_wss.service=appwrite_realtime - - traefik.http.routers.appwrite_realtime_wss.tls=true - depends_on: - appwrite-redis: - condition: service_healthy - appwrite-mariadb: - condition: service_healthy - environment: - <<: *appwrite-env - - # ============================================ - # Workers - Background Processing - # ============================================ - appwrite-worker-audits: - image: appwrite/appwrite:1.9.0 - entrypoint: worker-audits - container_name: appwrite-worker-audits - <<: *x-logging - restart: unless-stopped - networks: - - appwrite - depends_on: - appwrite-redis: - condition: service_healthy - appwrite-mariadb: - condition: service_healthy - environment: - <<: *appwrite-env - - appwrite-worker-webhooks: - image: appwrite/appwrite:1.9.0 - entrypoint: worker-webhooks - container_name: appwrite-worker-webhooks - <<: *x-logging - restart: unless-stopped - networks: - - appwrite - depends_on: - appwrite-redis: - condition: service_healthy - appwrite-mariadb: - condition: service_healthy - environment: - <<: *appwrite-env - - appwrite-worker-deletes: - image: appwrite/appwrite:1.9.0 - entrypoint: worker-deletes - container_name: appwrite-worker-deletes - <<: *x-logging - restart: unless-stopped - networks: - - appwrite - volumes: - - /home/jpmschweitzer/docker-data/appwrite/uploads:/storage/uploads:rw - - /home/jpmschweitzer/docker-data/appwrite/cache:/storage/cache:rw - - /home/jpmschweitzer/docker-data/appwrite/functions:/storage/functions:rw - - /home/jpmschweitzer/docker-data/appwrite/sites:/storage/sites:rw - - /home/jpmschweitzer/docker-data/appwrite/builds:/storage/builds:rw - - /home/jpmschweitzer/docker-data/appwrite/certificates:/storage/certificates:rw - depends_on: - appwrite-redis: - condition: service_healthy - appwrite-mariadb: - condition: service_healthy - environment: - <<: *appwrite-env - - appwrite-worker-databases: - image: appwrite/appwrite:1.9.0 - entrypoint: worker-databases - container_name: appwrite-worker-databases - <<: *x-logging - restart: unless-stopped - networks: - - appwrite - depends_on: - appwrite-redis: - condition: service_healthy - appwrite-mariadb: - condition: service_healthy - environment: - <<: *appwrite-env - - appwrite-worker-builds: - image: appwrite/appwrite:1.9.0 - entrypoint: worker-builds - container_name: appwrite-worker-builds - <<: *x-logging - restart: unless-stopped - networks: - - appwrite - volumes: - - /home/jpmschweitzer/docker-data/appwrite/functions:/storage/functions:rw - - /home/jpmschweitzer/docker-data/appwrite/sites:/storage/sites:rw - - /home/jpmschweitzer/docker-data/appwrite/builds:/storage/builds:rw - - /home/jpmschweitzer/docker-data/appwrite/uploads:/storage/uploads:rw - depends_on: - appwrite-redis: - condition: service_healthy - appwrite-mariadb: - condition: service_healthy - environment: - <<: *appwrite-env - - appwrite-worker-certificates: - image: appwrite/appwrite:1.9.0 - entrypoint: worker-certificates - container_name: appwrite-worker-certificates - <<: *x-logging - restart: unless-stopped - networks: - - appwrite - volumes: - - /home/jpmschweitzer/docker-data/appwrite/config:/storage/config:rw - - /home/jpmschweitzer/docker-data/appwrite/certificates:/storage/certificates:rw - depends_on: - appwrite-redis: - condition: service_healthy - appwrite-mariadb: - condition: service_healthy - environment: - <<: *appwrite-env - - appwrite-worker-functions: - image: appwrite/appwrite:1.9.0 - entrypoint: worker-functions - container_name: appwrite-worker-functions - <<: *x-logging - restart: unless-stopped - networks: - - appwrite - depends_on: - appwrite-redis: - condition: service_healthy - appwrite-mariadb: - condition: service_healthy - openruntimes-executor: - condition: service_started - environment: - <<: *appwrite-env - - appwrite-worker-mails: - image: appwrite/appwrite:1.9.0 - entrypoint: worker-mails - container_name: appwrite-worker-mails - <<: *x-logging - restart: unless-stopped - networks: - - appwrite - depends_on: - appwrite-redis: - condition: service_healthy - appwrite-mariadb: - condition: service_healthy - environment: - <<: *appwrite-env - - appwrite-worker-messaging: - image: appwrite/appwrite:1.9.0 - entrypoint: worker-messaging - container_name: appwrite-worker-messaging - <<: *x-logging - restart: unless-stopped - networks: - - appwrite - volumes: - - /home/jpmschweitzer/docker-data/appwrite/uploads:/storage/uploads:rw - depends_on: - appwrite-redis: - condition: service_healthy - appwrite-mariadb: - condition: service_healthy - environment: - <<: *appwrite-env - - appwrite-worker-migrations: - image: appwrite/appwrite:1.9.0 - entrypoint: worker-migrations - container_name: appwrite-worker-migrations - <<: *x-logging - restart: unless-stopped - networks: - - appwrite - volumes: - - /home/jpmschweitzer/docker-data/appwrite/imports:/storage/imports:rw - depends_on: - appwrite-redis: - condition: service_healthy - appwrite-mariadb: - condition: service_healthy - environment: - <<: *appwrite-env - - # ============================================ - # Stats Workers - # ============================================ - appwrite-task-stats-resources: - image: appwrite/appwrite:1.9.0 - entrypoint: stats-resources - container_name: appwrite-task-stats-resources - <<: *x-logging - restart: unless-stopped - networks: - - appwrite - depends_on: - appwrite-redis: - condition: service_healthy - appwrite-mariadb: - condition: service_healthy - environment: - <<: *appwrite-env - - appwrite-worker-stats-resources: - image: appwrite/appwrite:1.9.0 - entrypoint: worker-stats-resources - container_name: appwrite-worker-stats-resources - <<: *x-logging - restart: unless-stopped - networks: - - appwrite - depends_on: - appwrite-redis: - condition: service_healthy - appwrite-mariadb: - condition: service_healthy - environment: - <<: *appwrite-env - - appwrite-worker-stats-usage: - image: appwrite/appwrite:1.9.0 - entrypoint: worker-stats-usage - container_name: appwrite-worker-stats-usage - <<: *x-logging - restart: unless-stopped - networks: - - appwrite - depends_on: - appwrite-redis: - condition: service_healthy - appwrite-mariadb: - condition: service_healthy - environment: - <<: *appwrite-env - - # ============================================ - # Task Schedulers - # ============================================ - appwrite-task-scheduler-functions: - image: appwrite/appwrite:1.9.0 - entrypoint: schedule-functions - container_name: appwrite-task-scheduler-functions - <<: *x-logging - restart: unless-stopped - networks: - - appwrite - depends_on: - appwrite-redis: - condition: service_healthy - appwrite-mariadb: - condition: service_healthy - environment: - <<: *appwrite-env - - appwrite-task-scheduler-executions: - image: appwrite/appwrite:1.9.0 - entrypoint: schedule-executions - container_name: appwrite-task-scheduler-executions - <<: *x-logging - restart: unless-stopped - networks: - - appwrite - depends_on: - appwrite-redis: - condition: service_healthy - appwrite-mariadb: - condition: service_healthy - environment: - <<: *appwrite-env - - appwrite-task-scheduler-messages: - image: appwrite/appwrite:1.9.0 - entrypoint: schedule-messages - container_name: appwrite-task-scheduler-messages - <<: *x-logging - restart: unless-stopped - networks: - - appwrite - depends_on: - appwrite-redis: - condition: service_healthy - appwrite-mariadb: - condition: service_healthy - environment: - <<: *appwrite-env - - # ============================================ - # Maintenance Task - # ============================================ - appwrite-task-maintenance: - image: appwrite/appwrite:1.9.0 - entrypoint: maintenance - container_name: appwrite-task-maintenance - <<: *x-logging - restart: unless-stopped - networks: - - appwrite - depends_on: - appwrite-redis: - condition: service_healthy - appwrite-mariadb: - condition: service_healthy - environment: - <<: *appwrite-env - - # ============================================ - # Browser Service (SSR rendering) - # ============================================ - appwrite-browser: - image: appwrite/browser:0.3.2 - container_name: appwrite-browser - <<: *x-logging - restart: unless-stopped - networks: - - appwrite - - # ============================================ - # OpenRuntimes - Serverless Function Executor - # Requires Docker socket for spawning runtime containers - # ============================================ - openruntimes-executor: - image: openruntimes/executor:0.7.22 - container_name: openruntimes-executor - hostname: exc1 - <<: *x-logging - restart: unless-stopped - stop_signal: SIGINT - networks: - - appwrite - - runtimes - volumes: - - /var/run/docker.sock:/var/run/docker.sock - - /home/jpmschweitzer/docker-data/appwrite/builds:/storage/builds:rw - - /home/jpmschweitzer/docker-data/appwrite/functions:/storage/functions:rw - - /home/jpmschweitzer/docker-data/appwrite/sites:/storage/sites:rw - - /tmp:/tmp:rw - environment: - - OPR_EXECUTOR_INACTIVE_TRESHOLD=${_APP_COMPUTE_INACTIVE_THRESHOLD:-60} - - OPR_EXECUTOR_MAINTENANCE_INTERVAL=${_APP_COMPUTE_MAINTENANCE_INTERVAL:-60} - - OPR_EXECUTOR_NETWORK=runtimes - - OPR_EXECUTOR_ENV=production - - OPR_EXECUTOR_RUNTIMES=node-22.0,python-3.12,php-8.3,dart-3.5,bun-1.1,static-1.0 - - OPR_EXECUTOR_SECRET=gbnq4JwfnXBm4J7h7kUNEWt+OhwV3agu - - OPR_EXECUTOR_RUNTIME_VERSIONS=v5 - - OPR_EXECUTOR_LOGGING_CONFIG= - - OPR_EXECUTOR_STORAGE_DEVICE=local - - # ============================================ - # MariaDB - Internal Database (Appwrite core component) - # NOT exposed to host - Appwrite-only - # ============================================ - appwrite-mariadb: - image: mariadb:10.11 - container_name: appwrite-mariadb - <<: *x-logging - restart: unless-stopped - networks: - - appwrite - volumes: - - /home/jpmschweitzer/docker-data/appwrite/mariadb:/var/lib/mysql:rw - environment: - MYSQL_ROOT_PASSWORD: 2b8fd82b91b25205332c9d219a5838a752125965c6ea8ecfb0afe02e3307e163 - MYSQL_DATABASE: appwrite - MYSQL_USER: appwrite - MYSQL_PASSWORD: ML8QbYgD05+7ZDHuRfmSTgDBr5VusxLi87ifyO5RlJg= - MARIADB_AUTO_UPGRADE: '1' - TZ: Europe/Amsterdam - command: 'mysqld --innodb-flush-method=fsync' - healthcheck: - test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] - interval: 10s - timeout: 10s - retries: 10 - start_period: 30s - - - # ============================================ - # Redis - Internal Cache/Queue (Appwrite core component) - # NOT exposed to host - lives and dies with the stack - # ============================================ - appwrite-redis: - image: redis:7.4-alpine - container_name: appwrite-redis - <<: *x-logging - restart: unless-stopped - networks: - - appwrite - command: > - redis-server - --maxmemory 512mb - --maxmemory-policy allkeys-lru - --maxmemory-samples 5 - volumes: - - /home/jpmschweitzer/docker-data/appwrite/redis:/data:rw - healthcheck: - test: ["CMD", "redis-cli", "ping"] - interval: 10s - timeout: 5s - retries: 5 - start_period: 10s - -networks: - appwrite: - name: appwrite - runtimes: - name: runtimes - docker-dataplane: - external: true - name: docker-dataplane - -# ============================================================================= -# DEPLOYMENT INSTRUCTIONS -# ============================================================================= -# -# Phase 1: Data Directories (DONE) -# Created at ~/docker-data/appwrite/{uploads,imports,cache,config,certificates, -# functions,sites,builds,mariadb,redis} -# -# Phase 2: Deploy Stack -# Deploy via Portainer as stack "appwrite" -# All environment variables are inline - no .env file needed -# -# Phase 3: Initial Setup -# 1. Access http://192.168.86.149:8093/console -# 2. Create admin account (first user becomes admin) -# 3. Create a project for development -# -# Phase 4: (Optional) NPM Proxy Configuration -# If external access is needed later: -# 1. Create proxy host: appwrite.schweitz.net -> 192.168.86.149:8093 -# 2. Enable: Block Common Exploits, Websockets Support -# 3. SSL: Let's Encrypt, Force SSL, HTTP/2 -# -# ============================================================================= -# CREDENTIALS -# ============================================================================= -# MariaDB: -# Host: appwrite-mariadb:3306 (internal only) -# Database: appwrite -# User: appwrite -# Password: ML8QbYgD05+7ZDHuRfmSTgDBr5VusxLi87ifyO5RlJg= -# Root Password: 2b8fd82b91b25205332c9d219a5838a752125965c6ea8ecfb0afe02e3307e163 -# -# Redis: -# Host: appwrite-redis:6379 (internal only, no password) -# -# OpenSSL Key: 2b8fd82b91b25205332c9d219a5838a752125965c6ea8ecfb0afe02e3307e163 -# Executor Secret: gbnq4JwfnXBm4J7h7kUNEWt+OhwV3agu -# -# ============================================================================= -# ARCHITECTURE NOTES -# ============================================================================= -# - MariaDB and Redis are internal to the stack (not exposed to host) -# - Traefik handles routing: /console -> console, /v1/realtime -> realtime, / -> API -# - OpenRuntimes executor needs Docker socket to spawn function containers -# - The 'runtimes' network is used for executor <-> function container communication -# - Watchtower is disabled (label) - Appwrite upgrades should be deliberate -# - All 20+ containers share the same appwrite/appwrite:1.9.0 image with different entrypoints diff --git a/stacks/authentik.yml b/stacks/authentik.yml deleted file mode 100644 index 1acf979..0000000 --- a/stacks/authentik.yml +++ /dev/null @@ -1,214 +0,0 @@ -version: '3.8' - -# Authentik Identity Provider (SSO) -# Purpose: Centralized authentication for all homelab services -# Ports: 9000 (web UI), 9444 (standalone proxy outpost) -# GPU: No -# Storage: SSD (configs), PostgreSQL shared (user data) -# Note: Using standalone outpost - embedded outpost has issues in 2024.8.4 - -services: - authentik-server: - image: ghcr.io/goauthentik/server:2025.10.3 # Pinned version (2024.10 has redirect loop issues) - container_name: authentik-server - restart: unless-stopped - command: server - environment: - # External URLs (CRITICAL for redirect loop prevention) - AUTHENTIK_HOST: https://auth.schweitz.net - AUTHENTIK_HOST_BROWSER: https://auth.schweitz.net - - # Cookie settings (CRITICAL for SSO across subdomains) - AUTHENTIK_COOKIE_DOMAIN: .schweitz.net - AUTHENTIK_COOKIE_SAMESITE: lax - - # SSL/TLS - AUTHENTIK_INSECURE: false - - # PostgreSQL (shared) - AUTHENTIK_POSTGRESQL__HOST: postgres-shared - AUTHENTIK_POSTGRESQL__PORT: 5432 - AUTHENTIK_POSTGRESQL__NAME: authentik - AUTHENTIK_POSTGRESQL__USER: authentik_user - AUTHENTIK_POSTGRESQL__PASSWORD: F//j0ktck7cX06Vfgh0YXceONOtlSsHvadqROICeDx8= - AUTHENTIK_POSTGRESQL__CONN_MAX_AGE: 0 - AUTHENTIK_POSTGRESQL__CONN_HEALTH_CHECKS: true - - # Worker scaling (reduced to limit DB connections) - AUTHENTIK_WEB__WORKERS: 1 - AUTHENTIK_WEB__THREADS: 2 - - # Secret key (generated: openssl rand -base64 32) - AUTHENTIK_SECRET_KEY: TnFaTZ//RDcO2hxVR4QGOBORd5tfXe4Vok+lcAz/AdE= - - # Resource optimization - AUTHENTIK_LOG_LEVEL: warning - AUTHENTIK_ERROR_REPORTING__ENABLED: false - AUTHENTIK_AVATARS: none - AUTHENTIK_FOOTER_LINKS: '[]' - - # Embedded outpost configuration - AUTHENTIK_OUTPOSTS__DOCKER_IMAGE_BASE: "ghcr.io/goauthentik/%(type)s:%(version)s" - - # Timezone - TZ: Europe/Amsterdam - - ports: - - "9000:9000" # Web UI + Embedded outpost (path: /outpost.goauthentik.io/*) - - volumes: - - /home/jpmschweitzer/docker-data/authentik/media:/media - - /home/jpmschweitzer/docker-data/authentik/custom-templates:/templates - - networks: - - docker-dataplane - - healthcheck: - test: ["CMD-SHELL", "python3 -c \"import urllib.request; urllib.request.urlopen('http://localhost:9000/-/health/live/')\" || exit 1"] - start_period: 60s - interval: 30s - timeout: 10s - retries: 3 - - deploy: - resources: - limits: - memory: 512M - cpus: '0.5' - reservations: - memory: 256M - - authentik-worker: - image: ghcr.io/goauthentik/server:2025.10.3 # Same version as server - container_name: authentik-worker - restart: unless-stopped - command: worker - environment: - # Same environment as server (MUST match exactly) - AUTHENTIK_HOST: https://auth.schweitz.net - AUTHENTIK_HOST_BROWSER: https://auth.schweitz.net - AUTHENTIK_COOKIE_DOMAIN: .schweitz.net - AUTHENTIK_COOKIE_SAMESITE: lax - AUTHENTIK_INSECURE: false - AUTHENTIK_POSTGRESQL__HOST: postgres-shared - AUTHENTIK_POSTGRESQL__PORT: 5432 - AUTHENTIK_POSTGRESQL__NAME: authentik - AUTHENTIK_POSTGRESQL__USER: authentik_user - AUTHENTIK_POSTGRESQL__PASSWORD: F//j0ktck7cX06Vfgh0YXceONOtlSsHvadqROICeDx8= - AUTHENTIK_POSTGRESQL__CONN_MAX_AGE: 0 - AUTHENTIK_POSTGRESQL__CONN_HEALTH_CHECKS: true - AUTHENTIK_SECRET_KEY: TnFaTZ//RDcO2hxVR4QGOBORd5tfXe4Vok+lcAz/AdE= - AUTHENTIK_LOG_LEVEL: warning - AUTHENTIK_ERROR_REPORTING__ENABLED: false - TZ: Europe/Amsterdam - - # Worker-specific configuration - AUTHENTIK_WORKER__CONCURRENCY: 1 - AUTHENTIK_WORKER__THREADS: 2 - - volumes: - - /home/jpmschweitzer/docker-data/authentik/media:/media - - /home/jpmschweitzer/docker-data/authentik/custom-templates:/templates - - /home/jpmschweitzer/docker-data/authentik/certs:/certs - - /var/run/docker.sock:/var/run/docker.sock # For outpost management - - networks: - - docker-dataplane - - depends_on: - - authentik-server - - healthcheck: - test: ["CMD-SHELL", "ak healthcheck || exit 1"] - start_period: 60s - interval: 30s - timeout: 10s - retries: 3 - - deploy: - resources: - limits: - memory: 1G - cpus: '0.3' - reservations: - memory: 128M - - authentik-proxy: - image: ghcr.io/goauthentik/proxy:2025.10.3 # Standalone outpost (embedded outpost not working in 2024.8.4) - container_name: authentik-proxy - restart: unless-stopped - environment: - # Authentik server connection - AUTHENTIK_HOST: https://auth.schweitz.net - AUTHENTIK_INSECURE: false - AUTHENTIK_TOKEN: 9blMGz71CFMJszs7AedQefgydpTnwvybjmMn0AlYilIKBV5LIq7snqnCodwX - - # Logging - AUTHENTIK_LOG_LEVEL: info - - # Timezone - TZ: Europe/Amsterdam - - ports: - - "9444:9443" # Proxy outpost endpoint (9443 used by Portainer) - - networks: - - docker-dataplane - - depends_on: - - authentik-server - - healthcheck: - test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:9300/outpost.goauthentik.io/ping || exit 1"] - start_period: 30s - interval: 30s - timeout: 10s - retries: 3 - - deploy: - resources: - limits: - memory: 256M - cpus: '0.2' - reservations: - memory: 128M - -networks: - docker-dataplane: - external: true - name: docker-dataplane - -# Setup Instructions: -# -# 1. Create directories: -# mkdir -p ~/docker-data/authentik/{media,custom-templates,certs} -# -# 2. Deploy stack: -# docker-compose -f stacks/authentik.yml up -d -# -# 3. Watch logs: -# docker logs -f authentik-server -# docker logs -f authentik-worker -# -# 4. Wait for migrations to complete (~2-3 minutes): -# docker logs authentik-server 2>&1 | grep "Applying migration" -# -# 5. Access web UI: -# https://auth.schweitz.net (should show setup wizard) -# -# 6. Complete setup wizard: -# - Email: admin@schweitz.net -# - Password: -# - Finish setup -# -# Monitoring: -# -# Memory usage: -# docker stats authentik-server authentik-worker --no-stream -# -# Database connectivity: -# docker exec authentik-server ak check -# -# Outpost status (embedded outpost on port 9000): -# curl http://authentik-server:9000/outpost.goauthentik.io/ping -# curl http://192.168.86.149:9000/outpost.goauthentik.io/auth/nginx (should return 401, not 404) \ No newline at end of file diff --git a/stacks/core-api.yml b/stacks/core-api.yml deleted file mode 100644 index cf238b1..0000000 --- a/stacks/core-api.yml +++ /dev/null @@ -1,101 +0,0 @@ -version: '3.8' - -# Core API - OpenAPI-compatible functions and AI orchestration for Open WebUI -# Purpose: Provides OpenAI-compatible API (/v1/chat/completions) and tool functions (web scraping) -# Port: 8083 (HTTP API) -# Network: docker-dataplane (shared infrastructure network) -# -# Container image built from: git.schweitz.internal/jpmschweitzer/core-api (internal registry) - -services: - core-api: - image: git.schweitz.internal/jpmschweitzer/core-api:latest - container_name: core-api - restart: unless-stopped - - ports: - - "8083:8083" - - environment: - # Application - - APP_NAME=Core API - - DEBUG=false - - OIDC_ENABLED=true - - # Server - - HOST=0.0.0.0 - - PORT=8083 - - # Logging - - LOG_LEVEL=INFO - - # Portainer API - - PORTAINER_URL=http://192.168.86.149:8001 - - PORTAINER_API_KEY=${PORTAINER_API_KEY} - - # Nginx Proxy Manager API - - NPM_URL=http://192.168.86.149:81 - - NPM_EMAIL=${NPM_EMAIL} - - NPM_PASSWORD=${NPM_PASSWORD} - - # Postgres Shared Database API - - POSTGRES_HOST=192.168.86.149:5432 - - POSTGRES_USER=core_api - - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} - - # Authentik Configuration - - AUTHENTIK_URL=https://auth.schweitz.net - - AUTHENTIK_USERNAME=${AUTHENTIK_USERNAME} - - AUTHENTIK_PASSWORD=${AUTHENTIK_PASSWORD} - - AUTHENTIK_CORE_API_TOKEN=${AUTHENTIK_CORE_API_TOKEN} - - # HOME ASSITANT VARIABLES - - HOMEASSISTANT_URL=http://192.168.86.149:8123 - - HOMEASSISTANT_TOKEN=${HOMEASSISTANT_TOKEN} - - # AI SYSTEM VARIABLES - - SEARXNG_URL=http://192.168.86.149:8080 - - QDRANT_HOST=192.168.86.149 - - QDRANT_PORT=6333 - - # Python path - - PYTHONPATH=/app - - - PSUTIL_PROCFS_PATH=/host/proc - - HOSTFS_ROOT=/hostfs - - volumes: - # Data volumes only - no source code - - /home/jpmschweitzer/docker-data/core-api/logs:/app/logs - # Docker socket for direct container access (fallback when Portainer API incomplete) - - /var/run/docker.sock:/var/run/docker.sock:ro - - /proc:/host/proc:ro - - /sys:/host/sys:ro - - /:/hostfs:ro - - deploy: - resources: - limits: - cpus: '2.0' - memory: 6G - reservations: - memory: 1G - devices: - - driver: nvidia - count: all - capabilities: [gpu] - - labels: - - "com.centurylinklabs.watchtower.enable=true" - - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8083/health"] - interval: 30s - timeout: 20s - retries: 3 - start_period: 60s - -networks: - docker-dataplane: - external: true - name: docker-dataplane diff --git a/stacks/gitea.yml b/stacks/gitea.yml deleted file mode 100644 index fee63b6..0000000 --- a/stacks/gitea.yml +++ /dev/null @@ -1,92 +0,0 @@ -version: '3.8' - -# Gitea - Self-Hosted Git Service -# Application Layer -# Ports: 3002 (HTTP), 2222 (SSH) -# GPU: No -# Storage: SSD (repositories) -# Database: postgres-shared (gitea database, gitea_user) - -services: - gitea: - image: gitea/gitea:latest - container_name: gitea - restart: unless-stopped - ports: - - "3002:3000" # HTTP web interface - - "2222:22" # SSH git access (mapped to avoid host SSH conflict) - volumes: - # Git repositories and app config on SSD - - /home/jpmschweitzer/docker-data/gitea/data:/data - - /etc/timezone:/etc/timezone:ro - - /etc/localtime:/etc/localtime:ro - environment: - - USER_UID=1000 - - USER_GID=1000 - - GITEA__database__DB_TYPE=postgres - - GITEA__database__HOST=postgres-shared:5432 - - GITEA__database__NAME=gitea - - GITEA__database__USER=gitea_user - - GITEA__database__PASSWD=cCav64d76NX1zdEEAbVOM9uvao14aY8HojjNdxsSpMM= - - TZ=Europe/Amsterdam - healthcheck: - test: ["CMD-SHELL", "curl -fSs http://git.schweitz.internal/api/healthz || exit 1"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 60s - networks: - - docker-dataplane - - gitea-runner: - image: gitea/act_runner:latest - container_name: gitea-runner - restart: unless-stopped - volumes: - - /var/run/docker.sock:/var/run/docker.sock - - /home/jpmschweitzer/docker-data/gitea/runner:/data - environment: - - CONFIG_FILE=/data/config.yaml - - GITEA_INSTANCE_URL=http://gitea:3000 - - GITEA_RUNNER_REGISTRATION_TOKEN=${GITEA_RUNNER_TOKEN} - - GITEA_RUNNER_NAME=docker-runner - - TZ=Europe/Amsterdam - healthcheck: - test: ["CMD-SHELL", "pgrep -x act_runner || exit 1"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 30s - depends_on: - - gitea - networks: - - docker-dataplane - -networks: - docker-dataplane: - external: true - name: docker-dataplane - -# Database: Uses postgres-shared stack (gitea database, gitea_user) -# Ensure postgres-shared is running before deploying this stack. -# -# Access: http://localhost:3002 or https://git.schweitz.net (via NPM) -# -# SSH Git Clone Usage: -# git clone ssh://git@localhost:2222/username/repo.git -# -# Nginx Proxy Manager Setup (for external access): -# 1. Add proxy host: git.schweitz.net → http://gitea:3000 -# 2. Enable SSL with Let's Encrypt -# 3. Update GITEA__server__ROOT_URL in environment to https://git.schweitz.net -# -# Features: -# - Git repository hosting -# - Organizations and teams -# - Issue tracking -# - Pull requests and code review -# - Wiki and project documentation -# - CI/CD integration (Gitea Actions) -# - Webhooks for automation -# - Migration from GitHub/GitLab -# - Lightweight and fast diff --git a/stacks/headscale.yml b/stacks/headscale.yml deleted file mode 100644 index 33eba82..0000000 --- a/stacks/headscale.yml +++ /dev/null @@ -1,63 +0,0 @@ -version: '3.8' - -# Headscale - Self-Hosted Tailscale Control Server -# Phase 2: Networking & External Access -# Ports: 8085 (Web/API), 9090 (Metrics) -# GPU: No -# Storage: SSD (config and database) - -services: - headscale: - image: headscale/headscale:latest - container_name: headscale - restart: unless-stopped - ports: - - "8085:8080" # Web/API port - - "9090:9090" # Metrics port (optional) - volumes: - - /home/jpmschweitzer/docker-data/headscale/config:/etc/headscale - - /home/jpmschweitzer/docker-data/headscale/data:/var/lib/headscale - command: serve - environment: - - TZ=Europe/Amsterdam - healthcheck: - test: ["CMD-SHELL", "wget -qO- http://localhost:8080/health || exit 1"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 30s - networks: - - docker-dataplane - -networks: - docker-dataplane: - external: true - name: docker-dataplane - -# Setup Instructions: -# 1. Create directories: -# mkdir -p ~/docker-data/headscale/{config,data} -# -# 2. Generate config: -# docker exec headscale headscale config generate > ~/docker-data/headscale/config/config.yaml -# -# 3. Edit config (important settings): -# - server_url: http://tower-of-joy:8085 (or your IP) -# - db_type: sqlite3 -# - db_path: /var/lib/headscale/db.sqlite -# -# 4. Restart container: docker restart headscale -# -# 5. Create user: docker exec headscale headscale users create homelab -# -# 6. Generate pre-auth key: -# docker exec headscale headscale preauthkeys create --user homelab --expiration 24h -# -# 7. Connect devices: -# - Install Tailscale client on devices -# - Run: tailscale up --login-server=http://tower-of-joy:8085 --authkey= -# -# Verify: -# - Health check: curl http://localhost:8085/health -# - List users: docker exec headscale headscale users list -# - List nodes: docker exec headscale headscale nodes list diff --git a/stacks/home-assistant.yml b/stacks/home-assistant.yml deleted file mode 100644 index 77703a7..0000000 --- a/stacks/home-assistant.yml +++ /dev/null @@ -1,114 +0,0 @@ -version: '3.8' - -# Home Assistant - Smart Home Automation Platform -# Ports: 8123 (Home Assistant), 5580 (Matter Server WebSocket) -# GPU: No -# Storage: SSD (config and database) - -services: - home-assistant: - image: ghcr.io/home-assistant/home-assistant:stable - container_name: home-assistant - restart: unless-stopped - network_mode: host - volumes: - # Config on SSD (includes database, automations, scripts) - - /home/jpmschweitzer/docker-data/home-assistant/config:/config - environment: - - TZ=Europe/Amsterdam - # Privileged mode for USB device access (Z-Wave, Zigbee dongles) - privileged: true - healthcheck: - test: ["CMD-SHELL", "curl -fSs http://localhost:8123/manifest.json || exit 1"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 120s - deploy: - resources: - limits: - memory: 1G - - matter-server: - image: ghcr.io/matter-js/python-matter-server:stable - container_name: matter-server - restart: unless-stopped - network_mode: host - security_opt: - - apparmor:unconfined - volumes: - # Matter fabric data on SSD (BACK UP before upgrades - contains device credentials) - - /home/jpmschweitzer/docker-data/matter-server/data:/data - # D-Bus socket for Bluetooth commissioning - - /run/dbus:/run/dbus:ro - environment: - - TZ=Europe/Amsterdam - healthcheck: - test: ["CMD-SHELL", "curl -fSs http://localhost:5580 || exit 1"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 60s - deploy: - resources: - limits: - memory: 512M - -# Note: Both services use network_mode: host for LAN device discovery (mDNS, SSDP, Matter) -# Home Assistant reaches Matter Server at ws://localhost:5580/ws -# Other containers should reach HA via http://192.168.86.149:8123 - -# Setup Instructions: -# 1. Create config directory: -# mkdir -p /home/jpmschweitzer/docker-data/home-assistant/config -# -# 2. Deploy this stack via Portainer or: -# docker compose -f home-assistant.yml up -d -# -# 3. Wait for initial setup (1-2 minutes) -# -# 4. Access web interface: http://localhost:8123 -# or external: https://housekeeping.schweitz.net -# -# 5. Complete the onboarding wizard: -# - Create admin account -# - Set home location -# - Configure integrations -# -# USB Device Access: -# Privileged mode is enabled for Z-Wave, Zigbee, or other USB devices. -# For specific device mapping instead, replace privileged: true with: -# devices: -# - /dev/ttyUSB0:/dev/ttyUSB0 -# - /dev/ttyACM0:/dev/ttyACM0 -# -# NPM Configuration: -# Domain: housekeeping.schweitz.net -# Scheme: http -# Forward Hostname/IP: 192.168.86.149 -# Forward Port: 8123 -# Websockets Support: ENABLED (required for HA) -# SSL: Let's Encrypt -# -# After NPM setup, add to configuration.yaml: -# -# http: -# use_x_forwarded_for: true -# trusted_proxies: -# - 172.16.0.0/12 -# - 192.168.0.0/16 -# - 10.0.0.0/8 -# -# Matter Server Setup: -# 1. Create data directory: -# mkdir -p /home/jpmschweitzer/docker-data/matter-server/data -# -# 2. After deploying, connect from Home Assistant: -# Settings > Devices & Services > Add Integration > Matter -# Deselect "Install the official Matter Server app" -# Enter URL: ws://localhost:5580/ws -# -# 3. Commission devices via the HA Companion app (uses phone Bluetooth) -# or add --bluetooth-adapter 0 to the matter-server command for local BT -# -# Source: https://github.com/matter-js/python-matter-server diff --git a/stacks/library-desk.yml b/stacks/library-desk.yml deleted file mode 100644 index d62bca1..0000000 --- a/stacks/library-desk.yml +++ /dev/null @@ -1,84 +0,0 @@ -version: '3.8' - -# Library - Front Desk API (Coordination Service) -# Application Layer -# Port: 8089 (HTTP) -# GPU: No -# Source: git.schweitz.internal/jpmschweitzer/library-desk (internal registry) - -services: - library-desk: - image: git.schweitz.internal/jpmschweitzer/library-desk:latest - container_name: library-desk - restart: unless-stopped - ports: - - "8089:8089" # FastAPI HTTP - environment: - # API Configuration - - LIBRARY_API_KEY=${LIBRARY_API_KEY} - - # Neo4j Configuration - - NEO4J_URI=bolt://neo4j:7687 - - NEO4J_USER=neo4j - - NEO4J_PASSWORD=${NEO4J_PASSWORD} - - # Qdrant Configuration - - QDRANT_HOST=qdrant - - QDRANT_PORT=6333 - - # Wiki.js Configuration - - WIKIJS_URL=http://wiki:3000 - - WIKIJS_USERNAME=librarian@schweitz.net - - WIKIJS_PASSWORD=${WIKIJS_PASSWORD} - - WIKIJS_DB_PASSWORD=${WIKIJS_DB_PASSWORD} - - # SearXNG Configuration - - SEARXNG_URL=http://searxng:8080 - - # Paperless Configuration - - PAPERLESS_URL=http://paperless:8000 - - PAPERLESS_TOKEN=${PAPERLESS_TOKEN} - - # Ollama Configuration (for embeddings) - - OLLAMA_URL=http://ollama:11434 - - OLLAMA_MODEL=nomic-embed-text - - # Redis Configuration - - REDIS_HOST=redis-shared - - REDIS_PORT=6379 - - REDIS_DB=4 - - # Central Settings Database - - SYSTEM_SETTINGS_HOST=postgres-shared - - SYSTEM_SETTINGS_PORT=5432 - - SYSTEM_SETTINGS_DB=system_settings - - SYSTEM_SETTINGS_USER=settings - - SYSTEM_SETTINGS_PASSWORD=${SYSTEM_SETTINGS_PASSWORD} - - # Scheduler Service - - SCHEDULER_URL=http://scheduler:8090 - - # Python Configuration - - PYTHONUNBUFFERED=1 - - TZ=${TZ:-Europe/Amsterdam} - labels: - - "com.centurylinklabs.watchtower.enable=true" - networks: - - docker-dataplane - deploy: - resources: - reservations: - memory: 256M - limits: - memory: 768M - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8089/health"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 60s - -networks: - docker-dataplane: - external: true - name: docker-dataplane diff --git a/stacks/media.yml b/stacks/media.yml deleted file mode 100644 index 7ed6558..0000000 --- a/stacks/media.yml +++ /dev/null @@ -1,275 +0,0 @@ -version: "3.8" - -# Media Stack - Unified Media Management & Streaming -# Services: Jellyfin, Sonarr, Radarr, Prowlarr, SABnzbd -# Ports: 8096 (Jellyfin), 8989 (Sonarr), 7878 (Radarr), 9696 (Prowlarr), 8880 (SABnzbd) -# GPU: YES (Jellyfin only) - Requires NVIDIA Container Toolkit -# Storage: SSD (configs), HDD (media, downloads) -# Database: PostgreSQL (postgres-shared) for Sonarr, Radarr, Prowlarr - -services: - # ============================================================ - # JELLYFIN - Media Server with GPU Transcoding - # ============================================================ - jellyfin: - image: jellyfin/jellyfin:latest - container_name: jellyfin - user: 1000:1000 - restart: unless-stopped - ports: - - "8096:8096" # HTTP web interface - - "8920:8920" # HTTPS web interface - - "7359:7359/udp" # Auto-discovery - - "1900:1900/udp" # DLNA - volumes: - # Config and cache on SSD (performance-critical) - - /home/jpmschweitzer/docker-data/jellyfin/config:/config - - /home/jpmschweitzer/docker-data/jellyfin/cache:/cache - # Media files on HDD (read-only for safety) - - /mnt/media/jellyfin/movies:/media/movies:ro - - /mnt/media/jellyfin/series:/media/series:ro - environment: - - NVIDIA_VISIBLE_DEVICES=all - - NVIDIA_DRIVER_CAPABILITIES=all - - TZ=Europe/Amsterdam - healthcheck: - test: - [ - "CMD-SHELL", - "curl -fSs http://localhost:8096/health || exit 1", - ] - interval: 30s - timeout: 10s - retries: 3 - start_period: 60s - deploy: - resources: - limits: - memory: 16G - reservations: - memory: 8G - devices: - - driver: nvidia - count: 1 - capabilities: [gpu] - networks: - - docker-dataplane - - # ============================================================ - # SONARR - TV Show Management & Automation - # ============================================================ - sonarr: - image: linuxserver/sonarr:latest - container_name: sonarr - restart: unless-stopped - ports: - - "8989:8989" - volumes: - # Config on SSD - - /home/jpmschweitzer/docker-data/sonarr:/config - # Media and downloads on HDD - - /mnt/media/jellyfin/series:/tv - - /mnt/media/downloads:/downloads - environment: - - PUID=1000 - - PGID=1000 - - TZ=Europe/Amsterdam - # PostgreSQL (postgres-shared) - - SONARR__POSTGRES__HOST=postgres-shared - - SONARR__POSTGRES__PORT=5432 - - SONARR__POSTGRES__USER=media_user - - SONARR__POSTGRES__PASSWORD=${MEDIA_DB_PASSWORD} - - SONARR__POSTGRES__MAINDB=sonarr - healthcheck: - test: - ["CMD-SHELL", "curl -fSs http://localhost:8989/ping || exit 1"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 60s - depends_on: - - sabnzbd - - prowlarr - networks: - - docker-dataplane - - # ============================================================ - # RADARR - Movie Management & Automation - # ============================================================ - radarr: - image: linuxserver/radarr:latest - container_name: radarr - restart: unless-stopped - ports: - - "7878:7878" - volumes: - # Config on SSD - - /home/jpmschweitzer/docker-data/radarr:/config - # Media and downloads on HDD - - /mnt/media/jellyfin/movies:/movies - - /mnt/media/downloads:/downloads - environment: - - PUID=1000 - - PGID=1000 - - TZ=Europe/Amsterdam - # PostgreSQL (postgres-shared) - - RADARR__POSTGRES__HOST=postgres-shared - - RADARR__POSTGRES__PORT=5432 - - RADARR__POSTGRES__USER=media_user - - RADARR__POSTGRES__PASSWORD=${MEDIA_DB_PASSWORD} - - RADARR__POSTGRES__MAINDB=radarr - healthcheck: - test: - ["CMD-SHELL", "curl -fSs http://localhost:7878/ping || exit 1"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 60s - depends_on: - - sabnzbd - - prowlarr - networks: - - docker-dataplane - - # ============================================================ - # PROWLARR - Indexer Management - # ============================================================ - prowlarr: - image: linuxserver/prowlarr:latest - container_name: prowlarr - restart: unless-stopped - ports: - - "9696:9696" - volumes: - # Config on SSD - - /home/jpmschweitzer/docker-data/prowlarr:/config - environment: - - PUID=1000 - - PGID=1000 - - TZ=Europe/Amsterdam - # PostgreSQL (postgres-shared) - - PROWLARR__POSTGRES__HOST=postgres-shared - - PROWLARR__POSTGRES__PORT=5432 - - PROWLARR__POSTGRES__USER=media_user - - PROWLARR__POSTGRES__PASSWORD=${MEDIA_DB_PASSWORD} - - PROWLARR__POSTGRES__MAINDB=prowlarr - healthcheck: - test: - ["CMD-SHELL", "curl -fSs http://localhost:9696/ping || exit 1"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 60s - networks: - - docker-dataplane - - # ============================================================ - # SABNZBD - Usenet Download Client - # ============================================================ - sabnzbd: - image: linuxserver/sabnzbd:latest - container_name: sabnzbd - restart: unless-stopped - ports: - - "8880:8080" # Web UI (remapped to avoid Portainer conflict) - volumes: - # Config on SSD - - /home/jpmschweitzer/docker-data/sabnzbd:/config - # Downloads on HDD - - /mnt/media/downloads:/downloads - - /mnt/media/downloads/incomplete:/incomplete-downloads - environment: - - PUID=1000 - - PGID=1000 - - TZ=Europe/Amsterdam - - HOST_WHITELIST=sabnzbd,localhost,192.168.86.149,tower-of-joy - healthcheck: - test: - [ - "CMD-SHELL", - "curl -fSs http://localhost:8080/api?mode=version || exit 1", - ] - interval: 30s - timeout: 10s - retries: 3 - start_period: 60s - networks: - - docker-dataplane - -networks: - docker-dataplane: - external: true - name: docker-dataplane - -# ============================================================ -# SETUP GUIDE -# ============================================================ -# -# 0. Create PostgreSQL databases (run once): -# docker exec -it postgres-shared psql -U postgres -c " -# CREATE USER media_user WITH PASSWORD ''; -# CREATE DATABASE sonarr OWNER media_user; -# CREATE DATABASE radarr OWNER media_user; -# CREATE DATABASE prowlarr OWNER media_user; -# GRANT ALL PRIVILEGES ON DATABASE sonarr TO media_user; -# GRANT ALL PRIVILEGES ON DATABASE radarr TO media_user; -# GRANT ALL PRIVILEGES ON DATABASE prowlarr TO media_user; -# " -# -# 1. Create download directories: -# mkdir -p /mnt/media/downloads/complete /mnt/media/downloads/incomplete /mnt/media/downloads/usenet -# chown -R 1000:1000 /mnt/media/downloads -# -# 2. Create config directories: -# mkdir -p ~/docker-data/sonarr ~/docker-data/radarr ~/docker-data/prowlarr ~/docker-data/sabnzbd -# chown -R 1000:1000 ~/docker-data/sonarr ~/docker-data/radarr ~/docker-data/prowlarr ~/docker-data/sabnzbd -# -# 3. Set MEDIA_DB_PASSWORD environment variable in Portainer stack or export before deploy -# -# 4. Deploy the stack via Portainer or: -# docker stack deploy -c media.yml media -# -# 5. Configure services in order: -# -# a) SABnzbd (http://localhost:8880): -# - Complete setup wizard -# - Add Usenet server credentials -# - Configure download categories: tv, movies -# - Note the API key for Sonarr/Radarr -# -# b) Prowlarr (http://localhost:9696): -# - Add indexers (NZBgeek, DrunkenSlug, etc.) -# - Add Sonarr as application: http://sonarr:8989 -# - Add Radarr as application: http://radarr:7878 -# - Sync indexers to apps -# -# c) Sonarr (http://localhost:8989): -# - Settings → Media Management → Root Folder: /tv -# - Settings → Download Clients → Add SABnzbd: -# Host: sabnzbd, Port: 8080, API Key: -# - Indexers will be synced from Prowlarr -# -# d) Radarr (http://localhost:7878): -# - Settings → Media Management → Root Folder: /movies -# - Settings → Download Clients → Add SABnzbd: -# Host: sabnzbd, Port: 8080, API Key: -# - Indexers will be synced from Prowlarr -# -# e) Jellyfin (http://localhost:8096): -# - Already configured (migrated from previous stack) -# - Libraries should auto-detect new content -# -# 5. Inter-service communication (use container names): -# - SABnzbd from Sonarr/Radarr: http://sabnzbd:8080 -# - Prowlarr from Sonarr: http://prowlarr:9696 -# - All services on docker-dataplane network -# -# STORAGE LAYOUT: -# /mnt/media/ -# ├── downloads/ -# │ ├── complete/ # Completed downloads -# │ ├── incomplete/ # In-progress downloads -# │ └── usenet/ # Usenet-specific -# └── jellyfin/ -# ├── movies/ # Radarr imports here -# └── series/ # Sonarr imports here diff --git a/stacks/models.yml b/stacks/models.yml deleted file mode 100644 index 6e68273..0000000 --- a/stacks/models.yml +++ /dev/null @@ -1,199 +0,0 @@ -version: '3.8' - -# Models Stack - GPU-Accelerated ML/AI Model Services -# Purpose: All GPU model serving (LLM inference, audio generation, 3D generation) -# Ports: 11434 (Ollama API), 11500 (Stable Audio UI), 11510 (TRELLIS UI) -# GPU: YES - Shared RTX 2080 Ti (11GB VRAM) -# Network: docker-dataplane - -services: - # ============================================ - # Ollama - LLM Inference Server - # API: http://localhost:11434 - # ============================================ - ollama: - image: ollama/ollama:latest - container_name: ollama - restart: unless-stopped - ports: - - "11434:11434" - volumes: - # Model storage on SSD for fast load times - - /home/jpmschweitzer/docker-data/ollama/models:/root/.ollama - environment: - - TZ=Europe/Amsterdam - - NVIDIA_VISIBLE_DEVICES=all - - NVIDIA_DRIVER_CAPABILITIES=all - # Process requests sequentially to avoid batch overflow panics - - OLLAMA_NUM_PARALLEL=1 - # Unload LLMs after 5 minutes idle (keeps VRAM free for other services) - - OLLAMA_KEEP_ALIVE=5m - # Only keep one model loaded at a time (embedding model stays, LLMs swap) - - OLLAMA_MAX_LOADED_MODELS=1 - healthcheck: - test: ["CMD-SHELL", "curl -fSs http://localhost:11434/api/tags || exit 1"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 60s - deploy: - resources: - limits: - memory: 8G - reservations: - memory: 1G - devices: - - driver: nvidia - count: 1 - capabilities: [gpu] - networks: - - docker-dataplane - - # ============================================ - # Stable Audio Open - AI Audio Generation - # Web UI: http://localhost:11500 - # Image: locally built - # ============================================ - stable-audio: - image: stable-audio-open:local - container_name: stable-audio - restart: unless-stopped - ports: - - "11500:8000" - volumes: - # HuggingFace cache for model weights (~6GB) - - /home/jpmschweitzer/docker-data/stable-audio/hf-cache:/root/.cache/huggingface - environment: - - TZ=Europe/Amsterdam - - NVIDIA_VISIBLE_DEVICES=all - - NVIDIA_DRIVER_CAPABILITIES=compute,utility - - HF_TOKEN=${HF_TOKEN} - healthcheck: - test: ["CMD-SHELL", "curl -fSs http://localhost:8000/ || exit 1"] - interval: 60s - timeout: 30s - retries: 3 - start_period: 300s - deploy: - resources: - limits: - memory: 16G - reservations: - memory: 8G - devices: - - driver: nvidia - count: 1 - capabilities: [gpu] - labels: - - "com.centurylinklabs.watchtower.enable=false" - networks: - - docker-dataplane - - # ============================================ - # TRELLIS - 3D Model Generation (Low-VRAM Fork) - # Web UI: http://localhost:11510 - # Image: locally built - # ============================================ - trellis: - image: trellis:local - container_name: trellis - restart: unless-stopped - ports: - - "11510:7860" - volumes: - # SSD: HuggingFace model cache (~5GB) - - /home/jpmschweitzer/docker-data/trellis/hf-cache:/root/.cache/huggingface - # HDD: Generated GLB files - - /mnt/media/trellis/outputs:/app/outputs - # Entrypoint script (patches Gradio bugs without rebuilding image) - - /home/jpmschweitzer/docker-data/trellis/entrypoint.sh:/app/entrypoint.sh:ro - command: ["bash", "/app/entrypoint.sh"] - environment: - - TZ=Europe/Amsterdam - - NVIDIA_VISIBLE_DEVICES=all - - NVIDIA_DRIVER_CAPABILITIES=compute,utility - # VRAM Optimization (for 11GB card) - - ATTN_BACKEND=xformers - - SPCONV_ALGO=native - - PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True - # Gradio must bind to 0.0.0.0 inside Docker - - GRADIO_SERVER_NAME=0.0.0.0 - # HuggingFace token for model downloads (set in Portainer) - - HF_TOKEN=${HF_TOKEN:-} - healthcheck: - test: ["CMD-SHELL", "curl -fSs http://localhost:7860/ || exit 1"] - interval: 60s - timeout: 30s - retries: 3 - start_period: 300s - memswap_limit: 20G - deploy: - resources: - limits: - memory: 20G - reservations: - memory: 4G - devices: - - driver: nvidia - count: 1 - capabilities: [gpu] - labels: - - "com.centurylinklabs.watchtower.enable=false" - networks: - - docker-dataplane - -networks: - docker-dataplane: - external: true - name: docker-dataplane - -# ============================================================================= -# GPU CONTENTION -# ============================================================================= -# -# All three services share the RTX 2080 Ti (11GB VRAM). -# Ollama unloads models after 5 min idle to free VRAM. -# For heavy Stable Audio or TRELLIS generations, consider stopping Ollama: -# docker stop ollama && && docker start ollama -# -# ============================================================================= -# OLLAMA -# ============================================================================= -# -# After Deployment: -# 1. Verify GPU access: docker exec ollama nvidia-smi -# 2. Pull a model: docker exec ollama ollama pull llama3.2:3b -# 3. List models: docker exec ollama ollama list -# 4. Test inference: docker exec ollama ollama run llama3.2:3b "Hello" -# -# Recommended Models for RTX 2080 Ti (11GB VRAM): -# - llama3.2:3b (2GB) - Fast, general purpose -# - mistral:7b (4GB) - High quality, coding -# - codellama:7b (4GB) - Code-specialized -# - phi3:mini (2GB) - Fast reasoning -# -# ============================================================================= -# STABLE AUDIO -# ============================================================================= -# -# Prerequisites: -# 1. Build image: cd ~/docker-data/stable-audio-open && docker build -t stable-audio-open:local . -# 2. Create HuggingFace token: https://huggingface.co/settings/tokens (read access) -# 3. Accept model license: https://huggingface.co/stabilityai/stable-audio-open-1.0 -# 4. mkdir -p ~/docker-data/stable-audio/hf-cache -# 5. Set HF_TOKEN in Portainer environment variables -# -# ============================================================================= -# TRELLIS -# ============================================================================= -# -# Prerequisites: -# 1. Build image: cd ~/docker-data/trellis && docker build -t trellis:local . (~20-30 min) -# 2. mkdir -p ~/docker-data/trellis/hf-cache /mnt/media/trellis/outputs -# -# Game Asset Pipeline: -# 1. Open Gradio UI at http://tower-of-joy:11510 -# 2. Upload reference image or enter text prompt -# 3. Generate 3D model (uses 6-8GB VRAM) -# 4. Download GLB file from outputs -# 5. Import into Blender, apply textures, render sprites diff --git a/stacks/neo4j.yml b/stacks/neo4j.yml deleted file mode 100644 index 41962f9..0000000 --- a/stacks/neo4j.yml +++ /dev/null @@ -1,77 +0,0 @@ -version: '3.8' - -# Library - Neo4j Knowledge Graph -# Storage Layer -# Ports: 7474 (Browser), 7687 (Bolt) -# GPU: No -# Storage: SSD (graph database) - -services: - neo4j: - image: neo4j:5-community - container_name: neo4j - restart: unless-stopped - ports: - - "7474:7474" # Neo4j Browser (web UI) - - "7687:7687" # Bolt protocol (API) - volumes: - # Graph database on SSD for performance - - /home/jpmschweitzer/docker-data/library-neo4j/data:/data - - /home/jpmschweitzer/docker-data/library-neo4j/logs:/logs - - /home/jpmschweitzer/docker-data/library-neo4j/plugins:/plugins - environment: - - NEO4J_AUTH=neo4j/${NEO4J_PASSWORD} - - NEO4J_PLUGINS=["apoc"] - - NEO4J_dbms_memory_heap_initial__size=512m - - NEO4J_dbms_memory_heap_max__size=2g - - NEO4J_dbms_memory_pagecache_size=512m - - NEO4J_apoc_export_file_enabled=true - - NEO4J_apoc_import_file_enabled=true - - NEO4J_apoc_import_file_use__neo4j__config=true - - TZ=${TZ:-Europe/Amsterdam} - networks: - - docker-dataplane - deploy: - resources: - reservations: - memory: 1G - limits: - memory: 4G - healthcheck: - test: ["CMD", "cypher-shell", "-u", "neo4j", "-p", "${NEO4J_PASSWORD}", "RETURN 1"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 40s - -networks: - docker-dataplane: - external: true - name: docker-dataplane - -# ⚠️ SECURITY WARNING: -# Set NEO4J_PASSWORD in environment variables before deploying! -# Use a strong, unique password. -# -# After Deployment: -# 1. Access Neo4j Browser: http://192.168.86.149:7474 -# 2. Login: neo4j / -# 3. Run schema initialization (see DEPLOYMENT.md Phase 4.1) -# 4. Install APOC plugin (should auto-install from NEO4J_PLUGINS setting) -# -# Features: -# - Knowledge graph for entities, relationships, versions -# - APOC procedures for advanced graph operations -# - Cypher query language for graph traversal -# - Mind map generation for Wiki.js -# - Version tracking for documentation -# - Compatibility relationships between projects -# -# Memory Configuration: -# - Heap: 512MB initial → 2GB max -# - Page cache: 512MB -# - Reserved: 1GB, Limit: 4GB -# -# Backups: -# - Managed by Scheduler (weekly, Sunday 03:00) -# - Location: /mnt/media/backups/library/neo4j/ diff --git a/stacks/nextcloud.yml b/stacks/nextcloud.yml deleted file mode 100644 index 903d82e..0000000 --- a/stacks/nextcloud.yml +++ /dev/null @@ -1,111 +0,0 @@ -services: - nextcloud: - image: nextcloud:stable - container_name: nextcloud - restart: unless-stopped - ports: - - "8082:80" - volumes: - # Fresh config directory - - /home/jpmschweitzer/docker-data/nextcloud/config:/var/www/html/config - # Fresh user data directory - - /mnt/media/nextcloud/data:/var/www/html/data - environment: - # PostgreSQL configuration - - POSTGRES_HOST=postgres-shared - - POSTGRES_DB=nextcloud - - POSTGRES_USER=nextcloud_user - - POSTGRES_PASSWORD=${NEXTCLOUD_DB_PASSWORD} - - # Redis configuration (Database 7) - - REDIS_HOST=redis-shared - - REDIS_HOST_PORT=6379 - - REDIS_DB_INDEX=7 - - # Timezone - - TZ=Europe/Amsterdam - networks: - - docker-dataplane - deploy: - resources: - limits: - memory: 1G - -networks: - docker-dataplane: - external: true - name: docker-dataplane - -# Nextcloud - Personal Cloud Storage (Using Shared Infrastructure) -# Port: 8082 -# GPU: No -# Dependencies: postgres-shared, redis-shared -# -# Prerequisites: -# -# 1. Shared infrastructure must be running: -# docker ps | grep -E 'postgres-shared|redis-shared' -# -# 2. Database and user already created in postgres-shared: -# - Database: nextcloud -# - User: nextcloud_user -# - Redis DB: 7 -# -# 3. Create .env file with: -# NEXTCLOUD_DB_PASSWORD= -# -# 4. Deploy this stack: -# cd /mnt/media/Projects/portainer-core/stacks -# docker compose -f nextcloud-shared.yml --env-file .env.nextcloud-shared up -d -# -# After Deployment: -# -# 1. Wait for initialization (2-3 minutes) -# -# 2. Access web interface: http://localhost:8082 or https://cloud.schweitz.net -# -# 3. First-time setup wizard: -# - Admin username: admin -# - Admin password: -# - Data folder: /var/www/html/data (default) -# - Database: PostgreSQL -# - Database user: nextcloud_user -# - Database password: -# - Database name: nextcloud -# - Database host: postgres-shared -# -# 4. Configure trusted domains: -# docker exec -u www-data nextcloud php occ config:system:set trusted_domains 1 --value=cloud.schweitz.net -# docker exec -u www-data nextcloud php occ config:system:set trusted_domains 2 --value=192.168.86.149 -# -# 5. Configure Redis caching: -# docker exec -u www-data nextcloud php occ config:system:set redis host --value=redis-shared -# docker exec -u www-data nextcloud php occ config:system:set redis port --value=6379 -# docker exec -u www-data nextcloud php occ config:system:set redis dbindex --value=7 -# docker exec -u www-data nextcloud php occ config:system:set memcache.local --value='\\OC\\Memcache\\APCu' -# docker exec -u www-data nextcloud php occ config:system:set memcache.distributed --value='\\OC\\Memcache\\Redis' -# docker exec -u www-data nextcloud php occ config:system:set memcache.locking --value='\\OC\\Memcache\\Redis' -# -# 6. Optimize database: -# docker exec -u www-data nextcloud php occ db:add-missing-indices -# docker exec -u www-data nextcloud php occ db:convert-filecache-bigint -# -# 7. Configure background jobs: -# docker exec -u www-data nextcloud php occ background:cron -# -# Connection Details: -# -# Database: -# - Host: postgres-shared (from containers) / localhost (from host) -# - Port: 5432 -# - Database: nextcloud -# - User: nextcloud_user -# -# Cache: -# - Host: redis-shared (from containers) / localhost (from host) -# - Port: 6379 -# - Database: 7 -# -# Resource Usage: -# - Nextcloud: 1GB RAM limit -# - Savings: ~110-120 MB RAM + 2 fewer containers (MariaDB + Redis removed) diff --git a/stacks/nginx-proxy-manager.yml b/stacks/nginx-proxy-manager.yml deleted file mode 100644 index dcc57a4..0000000 --- a/stacks/nginx-proxy-manager.yml +++ /dev/null @@ -1,42 +0,0 @@ -version: '3.8' - -# Nginx Proxy Manager - Reverse Proxy & Unified Web Interface -# Phase 1: Foundation Setup -# Ports: 8000 (Admin UI), 80 (HTTP), 443 (HTTPS) -# GPU: No -# Storage: SSD (configs and SSL certificates) - -services: - nginx-proxy-manager: - image: jc21/nginx-proxy-manager:latest - container_name: nginx-proxy-manager - restart: unless-stopped - ports: - - "8000:81" # Admin web interface (unified entry point) - - "80:80" # HTTP reverse proxy traffic - - "443:443" # HTTPS reverse proxy traffic - volumes: - # SSD storage for configs and certificates (performance-critical) - - /home/jpmschweitzer/docker-data/nginx-proxy-manager/data:/data - - /home/jpmschweitzer/docker-data/nginx-proxy-manager/letsencrypt:/etc/letsencrypt - environment: - - DB_SQLITE_FILE=/data/database.sqlite - - TZ=Europe/Amsterdam - healthcheck: - test: ["CMD-SHELL", "curl -fSs http://localhost:81/api/ || exit 1"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 30s - -# Setup Instructions: -# 1. Deploy this stack -# 2. Access http://localhost:8000 -# 3. Default login: admin@example.com / changeme -# 4. IMPORTANT: Change admin credentials immediately! -# 5. Add proxy hosts for your services (Portainer, Jellyfin, etc.) -# -# Example Proxy Host Configuration: -# - Domain: portainer.tower-of-joy.local -# - Forward to: portainer:9000 -# - Enable SSL with Let's Encrypt (optional) diff --git a/stacks/open-webui.yml b/stacks/open-webui.yml deleted file mode 100644 index 558b0ba..0000000 --- a/stacks/open-webui.yml +++ /dev/null @@ -1,71 +0,0 @@ -services: - open-webui: - image: ghcr.io/open-webui/open-webui:main - container_name: open-webui - restart: unless-stopped - - ports: - - "82:8080" - - healthcheck: - test: ["CMD-SHELL", "curl -fSs http://localhost:8080/health || exit 1"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 60s - - environment: - # Ollama connection (direct - fallback) - - OLLAMA_BASE_URL=http://192.168.86.149:11434 - - # Tatlock AI Orchestrator (OpenAI-compatible endpoint) - - OPENAI_API_BASE_URLS=http://core-api:8083/v1 - - OPENAI_API_KEYS=dummy - - # Default model - - DEFAULT_MODELS=gemma3:12b - - # Enable features - - ENABLE_RAG_WEB_SEARCH=true - - ENABLE_OLLAMA_API=true - - WEBUI_AUTH=true - - # Web search configuration - - RAG_WEB_SEARCH_ENGINE=duckduckgo - - # RAG & Vector Database - Qdrant for conversation memory - - VECTOR_DB=qdrant - - QDRANT_URI=http://qdrant:6333 - - RAG_EMBEDDING_ENGINE=ollama - - RAG_EMBEDDING_MODEL=nomic-embed-text - - RAG_EMBEDDING_MODEL_AUTO_UPDATE=true - - # Enable native memory feature - - ENABLE_MEMORY=true - - MEMORY_COLLECTION_NAME=open-webui_memories - - # Session settings - - WEBUI_SESSION_COOKIE_SAME_SITE=lax - - WEBUI_SESSION_COOKIE_SECURE=false - - volumes: - - /home/jpmschweitzer/docker-data/open-webui:/app/backend/data - - deploy: - resources: - limits: - cpus: '1.0' - memory: 1G - reservations: - memory: 512M - - networks: - - docker-dataplane - - labels: - - "com.centurylinklabs.watchtower.enable=true" - -networks: - docker-dataplane: - external: true - name: docker-dataplane diff --git a/stacks/paperless.yml b/stacks/paperless.yml deleted file mode 100644 index e5873e2..0000000 --- a/stacks/paperless.yml +++ /dev/null @@ -1,110 +0,0 @@ -version: '3.8' - -# Paperless-ngx - Document Management System -# Port: 8091 (HTTP) -# GPU: No -# External: documents.schweitz.net -# Storage: Configs on SSD (backed up), documents on HDD - -services: - paperless: - image: ghcr.io/paperless-ngx/paperless-ngx:latest - container_name: paperless - restart: unless-stopped - ports: - - "8091:8000" - volumes: - # SSD - configs and database (backed up) - - /home/jpmschweitzer/docker-data/paperless/data:/usr/src/paperless/data - # HDD - document storage - - /mnt/media/paperless/media:/usr/src/paperless/media - - /mnt/media/paperless/consume:/usr/src/paperless/consume - - /mnt/media/paperless/export:/usr/src/paperless/export - environment: - # Database (shared PostgreSQL) - PAPERLESS_DBENGINE: postgresql - PAPERLESS_DBHOST: postgres-shared - PAPERLESS_DBPORT: 5432 - PAPERLESS_DBNAME: paperless - PAPERLESS_DBUSER: paperless_user - PAPERLESS_DBPASS: ${PAPERLESS_DB_PASSWORD} - - # Redis (shared, DB 8) - PAPERLESS_REDIS: redis://redis-shared:6379/8 - - # Security - PAPERLESS_SECRET_KEY: ${PAPERLESS_SECRET_KEY} - - # URLs - PAPERLESS_URL: https://documents.schweitz.net - PAPERLESS_ALLOWED_HOSTS: "*" - PAPERLESS_CORS_ALLOWED_HOSTS: "http://localhost:8091,https://documents.schweitz.net" - - # OCR Settings - PAPERLESS_OCR_LANGUAGE: eng - PAPERLESS_OCR_LANGUAGES: nld # Install Dutch on first startup - PAPERLESS_OCR_MODE: skip - PAPERLESS_OCR_OUTPUT_TYPE: pdfa - - # Webhooks (for Library Desk integration) - PAPERLESS_WEBHOOKS_ALLOW_INTERNAL_REQUESTS: "true" - - # Admin user (created on first run) - PAPERLESS_ADMIN_USER: admin - PAPERLESS_ADMIN_PASSWORD: ${PAPERLESS_ADMIN_PASSWORD} - - # Timezone - TZ: Europe/Amsterdam - labels: - - "com.centurylinklabs.watchtower.enable=true" - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8000"] - interval: 30s - timeout: 10s - retries: 5 - start_period: 60s - deploy: - resources: - limits: - memory: 4G - reservations: - memory: 512M - networks: - - docker-dataplane - -networks: - docker-dataplane: - external: true - name: docker-dataplane - -# ============================================================================= -# DEPLOYMENT INSTRUCTIONS -# ============================================================================= -# -# 1. Environment variables required in Portainer: -# PAPERLESS_DB_PASSWORD= -# PAPERLESS_SECRET_KEY= -# PAPERLESS_ADMIN_PASSWORD= -# -# 2. Database already created in postgres-shared: -# Database: paperless -# User: paperless_user -# -# 3. Redis using shared instance DB 8 -# -# 4. After deployment, configure NPM: -# Domain: documents.schweitz.net -# Forward: paperless:8000 or 192.168.86.149:8091 -# SSL: Let's Encrypt -# Websockets: Enable -# -# 5. Post-deployment in Paperless UI: -# - Create API token (My Profile → API Token) -# - Create custom fields: source_url, library_indexed, library_doc_id, collection -# - Create webhook workflow to http://library-desk:8089/documents/webhook -# -# 6. ClamAV is running on host at 192.168.86.149:3310 -# Configure Library Desk with: -# CLAMAV_HOST=192.168.86.149 -# CLAMAV_PORT=3310 -# CLAMAV_ENABLED=true diff --git a/stacks/penpot.yml b/stacks/penpot.yml deleted file mode 100644 index b10a4ea..0000000 --- a/stacks/penpot.yml +++ /dev/null @@ -1,243 +0,0 @@ -version: '3.8' - -# Penpot - Open Source Design Platform -# Purpose: Self-hosted Figma alternative for design and prototyping -# Port: 9001 (web UI) -# GPU: No -# External: penpot.schweitz.net -# Storage: SSD (assets), PostgreSQL shared, Redis shared (DB 10) - -services: - # ============================================ - # Penpot Frontend - Web UI (nginx) - # Port: 9001 (exposed) - # ============================================ - penpot-frontend: - image: penpotapp/frontend:latest - container_name: penpot-frontend - restart: unless-stopped - - ports: - - "9001:8080" - - volumes: - - /home/jpmschweitzer/docker-data/penpot/assets:/opt/data/assets - - environment: - # Feature flags - PENPOT_FLAGS: enable-login-with-oidc disable-login-with-password disable-registration enable-oidc-registration disable-email-verification - - # Backend connection (internal DNS) - PENPOT_BACKEND_URI: http://penpot-backend:6060 - PENPOT_EXPORTER_URI: http://penpot-exporter:6061 - - # Timezone - TZ: Europe/Amsterdam - - networks: - - docker-dataplane - - depends_on: - - penpot-backend - - penpot-exporter - - labels: - - "com.centurylinklabs.watchtower.enable=true" - - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8080"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 30s - - # ============================================ - # Penpot Backend - API Server (Clojure) - # Internal only - connects to postgres-shared + redis-shared - # ============================================ - penpot-backend: - image: penpotapp/backend:latest - container_name: penpot-backend - restart: unless-stopped - - volumes: - - /home/jpmschweitzer/docker-data/penpot/assets:/opt/data/assets - - environment: - # Public URI (for callbacks and links) - PENPOT_PUBLIC_URI: https://penpot.schweitz.net - - # Feature flags (same as frontend) - PENPOT_FLAGS: enable-login-with-oidc disable-login-with-password disable-registration enable-oidc-registration disable-email-verification - - # PostgreSQL (shared) - PENPOT_DATABASE_URI: postgresql://postgres-shared:5432/penpot - PENPOT_DATABASE_USERNAME: penpot_user - PENPOT_DATABASE_PASSWORD: YLmawuhcm0TLEMztkg/2Rzw+oAV6KPgHa3dZEYTkWr8= - - # Redis (shared, DB 10) - PENPOT_REDIS_URI: redis://redis-shared:6379/10 - - # Assets storage (updated for Penpot 2.11+) - PENPOT_OBJECTS_STORAGE_BACKEND: fs - PENPOT_OBJECTS_STORAGE_FS_DIRECTORY: /opt/data/assets - - # Secret key (512-bit base64) - PENPOT_SECRET_KEY: +g+fSbAzBWVA2gE8ZnJUcpgq9L1BCqtuB+h9w73iF9qcgxhjMF1QU23XUjQ5ZdxpPmwoyuuQEm/frtKUV2M6pA== - - # Telemetry (disabled) - PENPOT_TELEMETRY_ENABLED: false - - # ============================================ - # Authentik OIDC Configuration - # ============================================ - PENPOT_OIDC_CLIENT_ID: ${PENPOT_OIDC_CLIENT_ID} - PENPOT_OIDC_CLIENT_SECRET: ${PENPOT_OIDC_CLIENT_SECRET} - PENPOT_OIDC_BASE_URI: https://auth.schweitz.net/application/o/penpot/ - PENPOT_OIDC_AUTH_URI: https://auth.schweitz.net/application/o/authorize/ - PENPOT_OIDC_TOKEN_URI: https://auth.schweitz.net/application/o/token/ - PENPOT_OIDC_USER_URI: https://auth.schweitz.net/application/o/userinfo/ - PENPOT_OIDC_SCOPES: openid profile email - PENPOT_OIDC_NAME_ATTR: name - PENPOT_OIDC_EMAIL_ATTR: email - - # SMTP Configuration (optional - for notifications) - # PENPOT_SMTP_DEFAULT_FROM: penpot@schweitz.net - # PENPOT_SMTP_DEFAULT_REPLY_TO: penpot@schweitz.net - # PENPOT_SMTP_HOST: smtp.example.com - # PENPOT_SMTP_PORT: 587 - # PENPOT_SMTP_USERNAME: username - # PENPOT_SMTP_PASSWORD: password - # PENPOT_SMTP_TLS: true - - # Timezone - TZ: Europe/Amsterdam - - networks: - - docker-dataplane - - labels: - - "com.centurylinklabs.watchtower.enable=true" - - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:6060/readyz"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 60s - - deploy: - resources: - limits: - memory: 1G - reservations: - memory: 256M - - # ============================================ - # Penpot Exporter - PDF/SVG Export Worker - # Internal only - # ============================================ - penpot-exporter: - image: penpotapp/exporter:latest - container_name: penpot-exporter - restart: unless-stopped - - environment: - # Public URI (for rendering) - PENPOT_PUBLIC_URI: https://penpot.schweitz.net - - # Redis (shared, DB 10) - PENPOT_REDIS_URI: redis://redis-shared:6379/10 - - # Secret key (must match backend) - PENPOT_SECRET_KEY: +g+fSbAzBWVA2gE8ZnJUcpgq9L1BCqtuB+h9w73iF9qcgxhjMF1QU23XUjQ5ZdxpPmwoyuuQEm/frtKUV2M6pA== - - # Timezone - TZ: Europe/Amsterdam - - networks: - - docker-dataplane - - labels: - - "com.centurylinklabs.watchtower.enable=true" - - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:6061/readyz"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 30s - - deploy: - resources: - limits: - memory: 1G - reservations: - memory: 256M - -networks: - docker-dataplane: - external: true - name: docker-dataplane - -# ============================================================================= -# DEPLOYMENT INSTRUCTIONS -# ============================================================================= -# -# Phase 1: Database Setup (DONE) -# Database 'penpot' and user 'penpot_user' created in postgres-shared -# -# Phase 2: Authentik OIDC Configuration -# 1. Go to https://auth.schweitz.net/if/admin/#/core/providers -# 2. Create new OAuth2/OpenID Provider: -# - Name: Penpot -# - Authorization flow: default-provider-authorization-implicit-consent -# - Client type: Confidential -# - Client ID: (auto-generated, copy to Portainer env vars) -# - Client Secret: (auto-generated, copy to Portainer env vars) -# - Redirect URIs/Origins: https://penpot.schweitz.net/api/auth/oauth/oidc/callback -# - Scopes: openid profile email -# - Signing Key: authentik Self-signed Certificate -# -# 3. Create Application: -# - Name: Penpot -# - Slug: penpot -# - Provider: Penpot (created above) -# - Launch URL: https://penpot.schweitz.net -# -# 4. Add environment variables in Portainer: -# PENPOT_OIDC_CLIENT_ID= -# PENPOT_OIDC_CLIENT_SECRET= -# -# Phase 3: Deploy Stack -# Deploy via Portainer with the OIDC environment variables -# -# Phase 4: NPM Proxy Configuration -# 1. Create new proxy host in NPM (http://192.168.86.149:81) -# 2. Domain: penpot.schweitz.net -# 3. Forward Host: 192.168.86.149 -# 4. Forward Port: 9001 -# 5. Enable: Block Common Exploits, Websockets Support -# 6. SSL: Request new Let's Encrypt certificate, Force SSL, HTTP/2 -# -# Phase 5: Verify Deployment -# 1. Access https://penpot.schweitz.net -# 2. Click "Login with OIDC" - should redirect to auth.schweitz.net -# 3. Login with Authentik credentials -# 4. Verify redirect back to Penpot with authenticated session -# 5. Create a test project to verify functionality -# -# ============================================================================= -# CREDENTIALS (stored in Portainer env vars) -# ============================================================================= -# PostgreSQL: -# Host: postgres-shared:5432 -# Database: penpot -# User: penpot_user -# Password: YLmawuhcm0TLEMztkg/2Rzw+oAV6KPgHa3dZEYTkWr8= -# -# Redis: -# Host: redis-shared:6379 -# Database: 10 -# -# Secret Key: +g+fSbAzBWVA2gE8ZnJUcpgq9L1BCqtuB+h9w73iF9qcgxhjMF1QU23XUjQ5ZdxpPmwoyuuQEm/frtKUV2M6pA== diff --git a/stacks/portainer.yml b/stacks/portainer.yml deleted file mode 100644 index c5a2c78..0000000 --- a/stacks/portainer.yml +++ /dev/null @@ -1,31 +0,0 @@ -version: '3.8' - -# Portainer - Container Management UI -# Phase 1: Foundation Setup -# Ports: 8080 (HTTP), 8443 (HTTPS) -# GPU: No -# Storage: Docker volume (portainer_data) - -services: - portainer: - image: portainer/portainer-ce:latest - container_name: portainer - restart: always - ports: - - "8080:9000" # Main Portainer web UI - - "8443:9443" # Portainer HTTPS access - volumes: - - /var/run/docker.sock:/var/run/docker.sock # Docker socket for container management - - portainer_data:/data # Persistent data storage - environment: - - TZ=Europe/Amsterdam - healthcheck: - test: ["CMD-SHELL", "wget -qO- http://localhost:9000/api/system/status || exit 1"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 30s - -volumes: - portainer_data: - name: portainer_data diff --git a/stacks/postgres-shared.yml b/stacks/postgres-shared.yml deleted file mode 100644 index dda7071..0000000 --- a/stacks/postgres-shared.yml +++ /dev/null @@ -1,137 +0,0 @@ -version: '3.8' - -# Shared PostgreSQL Database -# Purpose: Centralized database for all homelab applications -# Port: 5432 -# GPU: No -# Storage: SSD (PostgreSQL data and backups) - -services: - postgres-shared: - image: postgres:16-alpine - container_name: postgres-shared - restart: unless-stopped - healthcheck: - test: ["CMD-SHELL", "pg_isready -U postgres"] - start_period: 20s - interval: 30s - retries: 5 - timeout: 5s - ports: - - "5432:5432" - volumes: - - /home/jpmschweitzer/docker-data/postgres-shared/data:/var/lib/postgresql/data - - /home/jpmschweitzer/docker-data/postgres-shared/backups:/backups - environment: - POSTGRES_PASSWORD: ${POSTGRES_ADMIN_PASSWORD:?admin password required} - TZ: Europe/Amsterdam - - # Performance tuning (adjust based on available RAM) - # Shared buffers: 25% of RAM allocated to PostgreSQL - POSTGRES_SHARED_BUFFERS: 512MB - # Effective cache: 50-75% of RAM allocated to PostgreSQL - POSTGRES_EFFECTIVE_CACHE_SIZE: 2GB - # Max connections: adjust based on number of applications - POSTGRES_MAX_CONNECTIONS: 200 - deploy: - resources: - limits: - cpus: '2.0' - memory: 2G - reservations: - memory: 512M - networks: - - docker-dataplane - -networks: - docker-dataplane: - external: true - name: docker-dataplane - -# Setup Instructions: -# -# 1. Create directories: -# mkdir -p ~/docker-data/postgres-shared/{data,backups} -# -# 2. Deploy stack via core-api (recommended) or docker-compose -# -# 3. Initialize databases (run ONCE after first deployment): -# docker exec -i postgres-shared psql -U postgres <<'EOF' -# -- Authentik database -# CREATE DATABASE authentik; -# CREATE USER authentik_user WITH PASSWORD 'F//j0ktck7cX06Vfgh0YXceONOtlSsHvadqROICeDx8='; -# GRANT ALL PRIVILEGES ON DATABASE authentik TO authentik_user; -# \c authentik -# GRANT ALL ON SCHEMA public TO authentik_user; -# ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO authentik_user; -# ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO authentik_user; -# -# -- Gitea database -# \c postgres -# CREATE DATABASE gitea; -# CREATE USER gitea_user WITH PASSWORD 'cCav64d76NX1zdEEAbVOM9uvao14aY8HojjNdxsSpMM='; -# GRANT ALL PRIVILEGES ON DATABASE gitea TO gitea_user; -# \c gitea -# GRANT ALL ON SCHEMA public TO gitea_user; -# ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO gitea_user; -# ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO gitea_user; -# EOF -# -# 4. Verify deployment: -# docker exec postgres-shared pg_isready -# docker exec postgres-shared psql -U postgres -c '\l' -# -# Database Connection Examples: -# -# From containers on docker-dataplane network: -# Host: postgres-shared -# Port: 5432 -# Database: authentik (or gitea, etc.) -# User: authentik_user (or gitea_user, etc.) -# Password: -# -# From host machine: -# psql -h localhost -U authentik_user -d authentik -# -# Monitoring: -# -# Active connections per database: -# docker exec postgres-shared psql -U postgres -c \ -# "SELECT datname, numbackends FROM pg_stat_database;" -# -# Database sizes: -# docker exec postgres-shared psql -U postgres -c \ -# "SELECT datname, pg_size_pretty(pg_database_size(datname)) FROM pg_database;" -# -# Backup: -# -# All databases: -# docker exec postgres-shared pg_dumpall -U postgres | \ -# gzip > ~/docker-data/postgres-shared/backups/all-$(date +%Y%m%d).sql.gz -# -# Single database: -# docker exec postgres-shared pg_dump -U postgres authentik | \ -# gzip > ~/docker-data/postgres-shared/backups/authentik-$(date +%Y%m%d).sql.gz -# -# Restore: -# gunzip < backup.sql.gz | docker exec -i postgres-shared psql -U postgres -# -# Maintenance: -# -# Vacuum analyze (optimize performance): -# docker exec postgres-shared psql -U postgres -c "VACUUM ANALYZE;" -# -# Reindex (if queries slow): -# docker exec postgres-shared psql -U postgres -d authentik -c "REINDEX DATABASE authentik;" -# -# Resource Usage (expected): -# CPU: ~0.5-1.5 cores (depends on query load) -# RAM: ~500MB-1.5GB (depends on active connections and cache) -# Storage: Grows with data (monitor with: df -h ~/docker-data/postgres-shared) -# -# Applications Using This Database: -# - Authentik (identity provider) -# - Gitea (git hosting) - migrated from dedicated instance -# - Nextcloud (personal cloud storage) - migrated from MariaDB -# - Paperless-ngx (document management) - DB: paperless, User: paperless_user -# - Future applications as needed diff --git a/stacks/qdrant.yml b/stacks/qdrant.yml deleted file mode 100644 index ff0e027..0000000 --- a/stacks/qdrant.yml +++ /dev/null @@ -1,69 +0,0 @@ -version: '3.8' - -# Qdrant Vector Database -# Purpose: Efficient vector storage for Open WebUI RAG (conversation memory & documents) -# Ports: 6333 (HTTP API), 6334 (gRPC) -# GPU: NO - CPU-based vector operations are efficient -# Storage: SSD for vector data (performance-critical) - -services: - qdrant: - image: qdrant/qdrant:latest - container_name: qdrant - restart: unless-stopped - ports: - - "6333:6333" # HTTP API - - "6334:6334" # gRPC API - volumes: - # Vector storage on SSD for performance - - /home/jpmschweitzer/docker-data/qdrant/storage:/qdrant/storage - # Snapshots for backups - - /home/jpmschweitzer/docker-data/qdrant/snapshots:/qdrant/snapshots - environment: - - TZ=Europe/Amsterdam - healthcheck: - test: ["CMD-SHELL", "curl -fSs http://localhost:6333/readyz || exit 1"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 30s - deploy: - resources: - limits: - cpus: '1.0' - memory: 768M - reservations: - memory: 256M - networks: - - docker-dataplane - -networks: - docker-dataplane: - external: true - name: docker-dataplane - -# Qdrant Performance Notes: -# - Optimized for high-dimensional vectors (embeddings) -# - Supports HNSW indexing for fast similarity search -# - Efficient memory usage (~1-2GB for thousands of documents) -# - No GPU required (CPU operations are fast enough) -# -# Storage Estimates: -# - ~1KB per conversation turn (with embedding) -# - 10,000 turns = ~10MB -# - Very efficient for conversation memory -# -# After Deployment: -# 1. Check logs: docker logs qdrant -# 2. Access UI: http://localhost:6333/dashboard -# 3. Verify API: curl http://localhost:6333/collections -# -# Integration with Open WebUI: -# - Set VECTOR_DB=qdrant in Open WebUI -# - Set QDRANT_URL=http://qdrant:6333 -# - Open WebUI will automatically create collections -# -# Collections Created: -# - Documents: User-uploaded files for RAG -# - Conversations: Chat history for memory -# - Web search results: Cached search results diff --git a/stacks/redis-shared.yml b/stacks/redis-shared.yml deleted file mode 100644 index 3225613..0000000 --- a/stacks/redis-shared.yml +++ /dev/null @@ -1,159 +0,0 @@ -version: '3.8' - -# Shared Redis Cache -# Purpose: Centralized cache and session store for all homelab applications -# Port: 6379 -# GPU: No -# Storage: SSD (Redis persistence - AOF and RDB) - -services: - redis-shared: - image: redis:alpine - container_name: redis-shared - restart: unless-stopped - command: > - redis-server - --appendonly yes - --appendfsync everysec - --maxmemory 512mb - --maxmemory-policy allkeys-lru - --save 60 1000 - --save 300 100 - --save 900 1 - --loglevel warning - healthcheck: - test: ["CMD-SHELL", "redis-cli ping | grep PONG"] - start_period: 20s - interval: 30s - retries: 5 - timeout: 3s - ports: - - "6379:6379" - volumes: - - /home/jpmschweitzer/docker-data/redis-shared/data:/data - environment: - TZ: Europe/Amsterdam - deploy: - resources: - limits: - cpus: '0.5' - memory: 512M - reservations: - memory: 128M - networks: - - docker-dataplane - -networks: - docker-dataplane: - external: true - name: docker-dataplane - -# Setup Instructions: -# -# 1. Create directories: -# mkdir -p ~/docker-data/redis-shared/data -# -# 2. Deploy stack: -# docker-compose -f redis-shared.yml up -d -# -# 3. Verify deployment: -# docker exec redis-shared redis-cli ping -# -# Database Allocation: -# -# Redis supports 16 databases (0-15). Assign one per application: -# -# DB 0: Authentik (sessions, cache, message queue) -# DB 1: Tatlock (memory) -# DB 2: Wiki.js -# DB 3: Scheduler -# DB 4: Library Desk -# DB 5: SearXNG -# DB 6: Tatlock (benchmarks) -# DB 7: Nextcloud (file locking, distributed cache, sessions) -# DB 8: Paperless (task queue, cache) -# DB 9-15: Reserved for future applications -# -# Connection Examples: -# -# From containers on docker-dataplane network: -# redis://redis-shared:6379/1 (Authentik, DB 1) -# redis://redis-shared:6379/2 (Gitea, DB 2) -# -# From host machine: -# redis-cli -h localhost -# SELECT 1 (switch to database 1) -# -# Monitoring: -# -# General info: -# docker exec redis-shared redis-cli INFO -# -# Memory usage: -# docker exec redis-shared redis-cli INFO memory -# -# Keyspace (keys per database): -# docker exec redis-shared redis-cli INFO keyspace -# -# Stats: -# docker exec redis-shared redis-cli INFO stats -# -# Per-database keys: -# docker exec redis-shared redis-cli -n 1 DBSIZE (database 1) -# docker exec redis-shared redis-cli -n 2 DBSIZE (database 2) -# -# Backup: -# -# Trigger background save: -# docker exec redis-shared redis-cli BGSAVE -# -# Copy RDB file: -# cp ~/docker-data/redis-shared/data/dump.rdb \ -# ~/backups/redis-$(date +%Y%m%d).rdb -# -# Backup AOF (append-only file): -# cp ~/docker-data/redis-shared/data/appendonly.aof \ -# ~/backups/redis-aof-$(date +%Y%m%d).aof -# -# Restore: -# docker stop redis-shared -# cp backup-dump.rdb ~/docker-data/redis-shared/data/dump.rdb -# docker start redis-shared -# -# Maintenance: -# -# Clear specific database (DANGER - data loss!): -# docker exec redis-shared redis-cli -n 1 FLUSHDB -# -# Clear all databases (DANGER - total data loss!): -# docker exec redis-shared redis-cli FLUSHALL -# -# Rewrite AOF (compact log file): -# docker exec redis-shared redis-cli BGREWRITEAOF -# -# Configuration Details: -# -# Persistence strategy (dual): -# - AOF (Append Only File): Real-time durability, fsync every second -# - RDB Snapshots: Periodic snapshots (every 60s if 1000+ keys changed) -# -# Memory policy: -# - Max memory: 512MB -# - Eviction: allkeys-lru (Least Recently Used eviction when full) -# -# Resource Usage (expected): -# CPU: ~0.1-0.3 cores (low CPU, very efficient) -# RAM: ~100-400MB (depends on data, capped at 512MB) -# Storage: ~50-200MB (AOF + RDB files) -# -# Applications Using This Cache: -# - Authentik (sessions, policies, background tasks) -# - Gitea (sessions, cache, queues) - if migrated -# - Nextcloud (file locking, distributed cache, sessions) -# - Future applications as needed -# -# Performance Tips: -# - Use pipeline commands for bulk operations -# - Set appropriate TTL (Time To Live) on cached keys -# - Monitor memory usage to prevent eviction storms -# - Use database numbers to isolate application data diff --git a/stacks/samba.yml b/stacks/samba.yml deleted file mode 100644 index f602264..0000000 --- a/stacks/samba.yml +++ /dev/null @@ -1,86 +0,0 @@ -version: '3.8' - -# Samba - Network File Sharing (SMB/CIFS) -# Backlog: Application Deployment -# Ports: 139, 445 -# GPU: No -# Storage: HDD (shares from media drive) - -services: - samba: - image: dperson/samba - container_name: samba - restart: unless-stopped - ports: - - "139:139" - - "445:445" - environment: - - TZ=Europe/Amsterdam - - USERID=1000 # Your user ID (run: id -u) - - GROUPID=1000 # Your group ID (run: id -g) - volumes: - # Config on SSD - - /home/jpmschweitzer/docker-data/samba:/share/config - - # Shares from HDD - - /mnt/media/jellyfin:/share/media # Media files (read/write) - - /mnt/media/downloads:/share/downloads # Downloads folder - - /mnt/media/backups:/share/backups:ro # Backups (read-only) - command: > - -s "Media;/share/media;yes;no;yes;all" - -s "Downloads;/share/downloads;yes;no;no;all" - -s "Backups;/share/backups;yes;no;yes;all" - -u "jpmschweitzer;IG3omTybtVW3pVmmBi1D5FjnQ0MnZLUG" - -p - healthcheck: - test: ["CMD-SHELL", "pgrep smbd || exit 1"] - interval: 10m - timeout: 10s - retries: 3 - start_period: 30s - networks: - - docker-dataplane - -networks: - docker-dataplane: - external: true - name: docker-dataplane - -# ⚠️ SECURITY WARNING: -# Change CHANGEME_SAMBA_PASSWORD before deploying! -# -# Share Configuration Format: -# -s "ShareName;/path;browseable;readonly;guest;users" -# -# Current Shares: -# 1. Media - Read/write access to Jellyfin media -# 2. Downloads - Read/write downloads folder (guest: no) -# 3. Backups - Read-only access to backups -# -# Note: Nextcloud files accessible via web interface at https://cloud.schweitz.net -# -# Access from Clients: -# -# Windows: -# 1. Open File Explorer -# 2. Address bar: \\tower-of-joy\Media -# 3. Enter credentials: jpmschweitzer / (your password) -# -# Mac: -# 1. Finder → Go → Connect to Server -# 2. Enter: smb://tower-of-joy/Media -# 3. Enter credentials -# -# Linux: -# 1. Install smbclient: sudo apt install smbclient -# 2. List shares: smbclient -L tower-of-joy -U jpmschweitzer -# 3. Connect: smbclient //tower-of-joy/Media -U jpmschweitzer -# Or mount: sudo mount -t cifs //tower-of-joy/Media /mnt/media -o username=jpmschweitzer -# -# Mobile (iOS/Android): -# Use file manager apps that support SMB (e.g., FE File Explorer, Solid Explorer) -# -# Firewall Configuration: -# If using UFW, allow Samba: -# sudo ufw allow 139/tcp -# sudo ufw allow 445/tcp diff --git a/stacks/scheduler.yml b/stacks/scheduler.yml deleted file mode 100644 index 3025464..0000000 --- a/stacks/scheduler.yml +++ /dev/null @@ -1,88 +0,0 @@ -version: '3.8' - -# The Scheduler -# Purpose: System-wide maintenance orchestration - backups, doc mirroring, cleanup, task automation -# Port: 8090 (API + UI) -# Network: docker-dataplane -# Image: git.schweitz.internal/jpmschweitzer/scheduler (internal registry) - -services: - scheduler: - image: git.schweitz.internal/jpmschweitzer/scheduler:latest - container_name: scheduler - restart: unless-stopped - - ports: - - "8090:8090" - - environment: - - APP_NAME=The Scheduler - - DEBUG=true - - HOST=0.0.0.0 - - PORT=8090 - - LOG_LEVEL=INFO - - POSTGRES_HOST=postgres-shared - - POSTGRES_PORT=5432 - - POSTGRES_DB=scheduler - - POSTGRES_USER=scheduler_user - - POSTGRES_PASSWORD=${SCHEDULER_DB_PASSWORD} - - REDIS_HOST=redis-shared - - REDIS_PORT=6379 - - REDIS_DB=3 - - GITEA_URL=http://gitea:3000 - - GITEA_USER=${GITEA_LIBRARY_USER} - - GITEA_PASSWORD=${GITEA_LIBRARY_PASSWORD} - - GITEA_SSH_HOST=gitea - - GITEA_SSH_PORT=22 - - GITEA_TOKEN=${GITEA_TOKEN} - - BACKUP_RETENTION_DAILY=7 - - BACKUP_RETENTION_WEEKLY=4 - - BACKUP_RETENTION_MONTHLY=12 - - DOCS_MIRROR_PATH=/docs-mirror - - DOCS_CHECK_INTERVAL=21600 - - SCHEDULER_API_KEY=${SCHEDULER_API_KEY} - - GCS_CREDENTIALS_FILE=/secrets/gcs-sa-key.json - - PYTHONPATH=/app - - volumes: - # Data volumes only - source code is baked into image - - /home/jpmschweitzer/docker-data/scheduler/logs:/app/logs - - /home/jpmschweitzer/docker-data/scheduler/task-data:/app/task-data - - /home/jpmschweitzer/docker-data/scheduler/ssh:/root/.ssh:ro - - /mnt/media/library/docs-mirror:/docs-mirror - - /mnt/media/backups/library:/backups - - /var/run/docker.sock:/var/run/docker.sock:ro - - /home/jpmschweitzer/docker-data/postgres-shared:/postgres-data:ro - # For config backups (read-only sources) - - /home/jpmschweitzer/docker-data:/data/docker-data:ro - - /home/jpmschweitzer/.config/code-server:/data/code-server-config:ro - # For config backups (write destination) - - /mnt/media/backups/docker-configs:/backups/docker-configs - # GCS service account credentials - - /home/jpmschweitzer/docker-data/scheduler/secrets:/secrets:ro - - networks: - - docker-dataplane - - deploy: - resources: - limits: - cpus: '0.5' - memory: 1G - reservations: - memory: 256M - - labels: - - "com.centurylinklabs.watchtower.enable=true" - - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8090/health"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 90s - -networks: - docker-dataplane: - external: true - name: docker-dataplane diff --git a/stacks/searxng.yml b/stacks/searxng.yml deleted file mode 100644 index aed529e..0000000 --- a/stacks/searxng.yml +++ /dev/null @@ -1,226 +0,0 @@ -version: '3.8' - -# SearXNG Metasearch Engine -# Purpose: Privacy-focused metasearch aggregating 246+ search engines -# Port: 8087 (HTTP API) -# GPU: NO - Pure CPU application -# Storage: Minimal (config only, no data persistence) -# Integration: Core-AI service tool for web search capability - -services: - searxng: - image: searxng/searxng:latest - container_name: searxng - restart: unless-stopped - ports: - - "8087:8080" # HTTP API (JSON format enabled) - volumes: - # Mount config directory (will be auto-populated on first run) - - /home/jpmschweitzer/docker-data/searxng:/etc/searxng:rw - environment: - - TZ=Europe/Amsterdam - - SEARXNG_BASE_URL=http://searxng:8087/ - # Valkey/Redis configuration for result caching (using shared Redis DB 5) - - SEARXNG_VALKEY_URL=redis://redis-shared:6379/5 - # Enabled output formats (JSON required for API access) - - SEARXNG_SETTINGS_FORMATS=html,json - cap_drop: - - ALL - cap_add: - - CHOWN - - SETGID - - SETUID - logging: - driver: "json-file" - options: - max-size: "10m" - max-file: "3" - deploy: - resources: - limits: - cpus: '1.0' - memory: 512M - reservations: - memory: 128M - networks: - - docker-dataplane - healthcheck: - test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/healthz"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 40s - -networks: - docker-dataplane: - external: true - name: docker-dataplane - -# SearXNG Overview: -# - Metasearch engine: Aggregates results from 246+ search sources -# - Privacy-first: No tracking, no profiling, no data collection -# - Multi-format: HTML (web UI), JSON (API), CSV, RSS -# - Customizable: Enable/disable specific engines per category -# -# Search Categories: -# - general: Web search (Google, Bing, DuckDuckGo, etc.) -# - images: Image search -# - videos: Video search -# - news: News articles -# - map: Geographic/location -# - music: Music/audio -# - it: Programming/technical (StackOverflow, GitHub, docs) -# - science: Academic (arXiv, PubMed, Semantic Scholar) -# - files: File repositories -# - social media: Social platforms -# -# Setup Instructions: -# -# 1. Create configuration directory: -# mkdir -p ~/docker-data/searxng/config -# -# 2. Deploy stack: -# docker-compose -f searxng.yml up -d -# -# Note: First run will auto-generate settings.yml with secret key -# -# 3. Stop container to edit config: -# docker stop searxng -# -# 4. Enable JSON format in settings.yml: -# Edit ~/docker-data/searxng/config/settings.yml -# Find the 'search:' section and set: -# formats: -# - html -# - json -# -# 5. Restart container: -# docker start searxng -# -# 6. Verify deployment: -# # Web UI test: -# curl http://localhost:8087/ -# -# # JSON API test: -# curl "http://localhost:8087/search?q=python&format=json" | jq '.results[0]' -# -# # Health check: -# curl http://localhost:8087/healthz -# -# API Usage: -# -# Basic search: -# GET http://searxng:8080/search?q=query&format=json -# -# With category filter: -# GET http://searxng:8080/search?q=machine+learning&format=json&categories=science -# -# With language: -# GET http://searxng:8080/search?q=query&format=json&language=en -# -# With time range: -# GET http://searxng:8080/search?q=news&format=json&time_range=day -# -# Available categories: -# general, images, videos, news, map, music, it, science, files, social_media -# -# Time ranges: -# day, week, month, year -# -# Response format (JSON): -# { -# "query": "search term", -# "results": [ -# { -# "url": "https://example.com", -# "title": "Page title", -# "content": "Description snippet", -# "engine": "google", -# "score": 1.0 -# } -# ], -# "suggestions": ["related", "searches"], -# "number_of_results": 42 -# } -# -# Integration with Core-AI: -# -# Add to services/core-ai/src/tools/local.py: -# -# @register_tool -# async def web_search(query: str, category: str = "general") -> str: -# """Search the web using SearXNG metasearch engine.""" -# response = await httpx.get( -# "http://searxng:8080/search", -# params={"q": query, "format": "json", "categories": category}, -# timeout=10.0 -# ) -# results = response.json()["results"][:5] -# return "\n\n".join([ -# f"[{r['title']}]({r['url']})\n{r.get('content', '')}" -# for r in results -# ]) -# -# Performance Tuning: -# -# Response times: 2-5 seconds (aggregating multiple sources) -# Redis caching: Reduces duplicate queries (DB 5 on redis-shared) -# Engine selection: Disable slow engines to improve speed -# -# Edit settings.yml to disable slow engines: -# engines: -# - name: slowengine -# disabled: true -# -# Resource Usage (expected): -# CPU: ~0.2-0.5 cores (varies with query load) -# RAM: ~150-300MB (depends on cache size) -# Disk: ~10-50MB (config only, no data storage) -# Network: Variable (depends on upstream engine responses) -# -# Redis Database Allocation: -# DB 5: SearXNG result cache -# -# Security: -# - Dropped all capabilities except essential (CHOWN, SETGID, SETUID) -# - No data persistence (privacy by design) -# - Can run behind Nginx Proxy Manager for HTTPS -# - Optional Tor support (requires additional config) -# -# Monitoring: -# -# View logs: -# docker logs -f searxng -# -# Check engine stats: -# curl http://localhost:8080/stats -# -# Check health: -# docker exec searxng wget -q -O- http://localhost:8080/healthz -# -# Engine Configuration Tips: -# -# To optimize for AI/LLM use cases, consider enabling these engines: -# - General: google, bing, duckduckgo, brave -# - Technical: stackoverflow, github, devdocs, mdn -# - Academic: arxiv, pubmed, semantic_scholar, google_scholar -# - Documentation: readthedocs, man (Linux man pages) -# -# Disable to improve speed: -# - Slow engines (check /stats page) -# - Engines you don't need (social media if not relevant) -# - Engines with frequent timeouts -# -# Advanced Configuration: -# -# Custom engines can be added to settings.yml -# See: https://docs.searxng.org/dev/engines/index.html -# -# Limiter (rate limiting) can be configured to prevent abuse -# See: https://docs.searxng.org/admin/engines/settings.html#limiter -# -# After Deployment: -# 1. Access UI: http://localhost:8080 -# 2. Test JSON API: curl "http://localhost:8080/search?q=test&format=json" -# 3. Review engine stats: http://localhost:8080/stats -# 4. Integrate with core-ai service diff --git a/stacks/tatlock-ui.yml b/stacks/tatlock-ui.yml deleted file mode 100644 index 8e6809a..0000000 --- a/stacks/tatlock-ui.yml +++ /dev/null @@ -1,44 +0,0 @@ -version: '3.8' - -# Tatlock UI - Home Lab Dashboard (Flutter Web) -# Port: 9999 (HTTP) -# GPU: No -# External: home.schweitz.net -# Storage: None (stateless static web app) -# -# Container image built from: git.schweitz.internal/jpmschweitzer/tatlock-ui -# See CONTAINERS.md for port allocation reference - -services: - tatlock-ui: - image: git.schweitz.internal/jpmschweitzer/tatlock-ui:latest - container_name: tatlock-ui - restart: unless-stopped - ports: - - "9999:80" - networks: - - docker-dataplane - labels: - - "com.centurylinklabs.watchtower.enable=true" - healthcheck: - test: ["CMD-SHELL", "wget -q --spider http://localhost:80/ || exit 1"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 10s - deploy: - resources: - limits: - memory: 128M - reservations: - memory: 32M - -networks: - docker-dataplane: - external: true - name: docker-dataplane - -# Access: -# - Internal: http://tower-of-joy:9999 -# - Mesh VPN: http://10.99.0.1:9999 -# - External: https://home.schweitz.net diff --git a/stacks/watchtower.yml b/stacks/watchtower.yml deleted file mode 100644 index d2fcf84..0000000 --- a/stacks/watchtower.yml +++ /dev/null @@ -1,70 +0,0 @@ -version: '3.8' - -# Watchtower - Automatic Container Updates -# Phase 4: Optimization & Security -# Ports: None (runs as background service) -# GPU: No -# Storage: None (reads Docker socket) - -services: - watchtower: - image: containrrr/watchtower:latest - container_name: watchtower - restart: unless-stopped - ports: - - "8070:8080" # HTTP API for triggering updates - volumes: - - /var/run/docker.sock:/var/run/docker.sock - environment: - - WATCHTOWER_CLEANUP=true # Remove old images after update - - WATCHTOWER_SCHEDULE=0 0 4 * * * # Run at 4 AM daily (cron format) - - TZ=Europe/Amsterdam - - # HTTP API for CI/CD triggered updates - - WATCHTOWER_HTTP_API_UPDATE=true - - WATCHTOWER_HTTP_API_TOKEN=${WATCHTOWER_API_TOKEN} - - WATCHTOWER_HTTP_API_PERIODIC_POLLS=true # Allow triggering periodic poll via API - - # Optional: Enable notifications - # - WATCHTOWER_NOTIFICATIONS=shoutrrr - # - WATCHTOWER_NOTIFICATION_URL= # Add notification URL (Discord, Slack, etc.) - - # Optional: Monitor only specific containers - # - WATCHTOWER_LABEL_ENABLE=true # Only update containers with label com.centurylinklabs.watchtower.enable=true - healthcheck: - test: ["CMD-SHELL", "pgrep watchtower || exit 1"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 30s - networks: - - docker-dataplane - -networks: - docker-dataplane: - external: true - name: docker-dataplane - -# Schedule Format (cron): -# - 0 0 4 * * * = Daily at 4 AM -# - 0 0 4 * * SUN = Weekly on Sunday at 4 AM -# - 0 0 */6 * * * = Every 6 hours -# -# Manual Trigger: -# docker exec watchtower watchtower --run-once -# -# HTTP API Trigger (for CI/CD): -# curl -H "Authorization: Bearer $WATCHTOWER_API_TOKEN" http://localhost:8070/v1/update -# -# Exclude Specific Containers: -# Add label to container: com.centurylinklabs.watchtower.enable=false -# -# Monitor Watchtower Activity: -# docker logs watchtower -# -# Security Note: -# Watchtower has full Docker socket access. Review updates in logs. -# Consider excluding critical services and updating them manually. -# -# Environment Variables (set in Portainer): -# - WATCHTOWER_API_TOKEN: Secret token for HTTP API authentication diff --git a/stacks/wiki.yml b/stacks/wiki.yml deleted file mode 100644 index fd06a55..0000000 --- a/stacks/wiki.yml +++ /dev/null @@ -1,97 +0,0 @@ -version: '3.8' - -# Library - Wiki.js (Knowledge Wiki) -# Application Layer -# Port: 3000 (HTTP) -# GPU: No -# Storage: PostgreSQL (shared), SSD (uploads) - -services: - wiki: - image: ghcr.io/requarks/wiki:2 - container_name: wiki - restart: unless-stopped - ports: - - "3000:3000" # HTTP web interface - volumes: - # Uploads and backups on HDD - - /mnt/media/library/wiki:/wiki/data - - /etc/timezone:/etc/timezone:ro - - /etc/localtime:/etc/localtime:ro - environment: - # Database configuration (PostgreSQL shared) - - DB_TYPE=postgres - - DB_HOST=postgres-shared - - DB_PORT=5432 - - DB_NAME=library - - DB_USER=library_user - - DB_PASS=${LIBRARY_DB_PASSWORD} - - # Redis cache configuration (DB 2) - - REDIS_HOST=redis-shared - - REDIS_PORT=6379 - - REDIS_DB=2 - - # Application configuration - - WIKI_ADMIN_EMAIL=admin@schweitz.net - - HA_ACTIVE=false - - TZ=${TZ:-Europe/Amsterdam} - networks: - - docker-dataplane - deploy: - resources: - reservations: - memory: 256M - limits: - memory: 1G - healthcheck: - test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:3000/healthz"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 60s - -networks: - docker-dataplane: - external: true - name: docker-dataplane - -# ⚠️ SECURITY WARNING: -# Set LIBRARY_DB_PASSWORD in environment variables before deploying! -# This should match the password created in PostgreSQL (see DEPLOYMENT.md Phase 1.1) -# -# After Deployment: -# 1. Access http://wiki:3000 -# 2. Complete initial setup wizard: -# - Admin account (use strong password!) -# - Site URL: http://wiki:3000 or https://library.schweitz.net -# - Telemetry: Optional -# 3. Enable API Access: -# - Administration → API Access -# - Generate New Key → Save to .env.library as WIKIJS_API_KEY -# 4. Configure storage: -# - Administration → Storage -# - Enable Git storage (optional, for version control) -# -# Features: -# - Markdown editing with live preview -# - Cross-dossier linking (wikilinks) -# - Full-text search -# - Version history -# - User authentication and authorization -# - API for Front Desk integration -# - Mind map embedding (via Front Desk) -# -# Integration: -# - Front Desk proxies CRUD operations via Wiki.js API -# - Content changes trigger re-indexing in Qdrant -# - Entity extraction updates Neo4j graph -# -# Backups: -# - Database: Managed by Scheduler (daily, 02:00) -# - Content export: Managed by Scheduler (daily, 02:00) -# - Location: /mnt/media/backups/library/wikijs/ -# -# External Access (Optional): -# - Nginx Proxy Manager: library.schweitz.net → library-wiki:3000 -# - SSL: Let's Encrypt via NPM