Compare commits

...

57 Commits

Author SHA1 Message Date
ea210c73dd fix(core): mount backrest config directory instead of file
Backrest requires atomic file operations for config updates,
which fail with single-file bind mounts.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 17:25:50 +01:00
c4fd23855b feat(media): add Immich photo/video management service
- Add immich_server, immich_ml, and immich_postgres services
- Use dedicated PostgreSQL with vector extensions (vectorchord + pgvectors)
- Connect to core Redis for job queues
- Configure Traefik routing for immich.media.pivoine.art
- Add backup volumes and plan for Backrest (daily at 12:00)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 17:23:25 +01:00
3cc9db6632 fix(core): remove filestash backup volume reference
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 17:08:28 +01:00
4851ca10f0 fix(media): remove filestash service
- Remove filestash service and volume from media/compose.yaml
- Remove filestash-backup plan from backrest config
- Remove MEDIA_FILESTASH_* environment variables from arty.yml

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 17:01:07 +01:00
c55f41408a fix(ai): litellm config 2025-11-30 23:03:32 +01:00
7bca766247 fix(ai): litellm config 2025-11-30 22:32:49 +01:00
120bf7c385 feat(ai): bge over litellm 2025-11-30 20:12:07 +01:00
35e0f232f9 revert: remove GPU_TAILSCALE_HOST from arty.yml
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 09:58:44 +01:00
cd9256f09c fix: use Tailscale IP for GPU_TAILSCALE_HOST (MagicDNS doesn't work from Docker)
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 09:53:14 +01:00
c9e3a5cc4f fix: add resolver for runtime DNS resolution in nginx
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 09:48:25 +01:00
b2b444fb98 fix: add Tailscale DNS to GPU proxy containers
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 09:47:06 +01:00
dcc29d20f0 fix: traefik labels 2025-11-28 09:39:39 +01:00
19ad30e8c4 revert: tailscale docker sidecar 2025-11-28 09:31:21 +01:00
99e39ee6e6 fix: tailscale sidecar dns 2025-11-28 09:12:52 +01:00
ed83e64727 fix: tailscale sidecar dns 2025-11-28 09:09:10 +01:00
18e6741596 fix: tailscale sidecar dns 2025-11-28 09:07:12 +01:00
c83b77ebdb feat: tailscale sidecar 2025-11-28 08:59:42 +01:00
6568dd10b5 feat: tailscale sidecar 2025-11-28 08:42:40 +01:00
a6e4540e84 feat: tailscale sidecar 2025-11-28 08:41:30 +01:00
dbdf33e78e feat: tailscale sidecar 2025-11-28 08:40:30 +01:00
74f618bcbb feat: tailscale sidecar 2025-11-28 08:36:50 +01:00
0c7fe219f7 feat: tailscale sidecar 2025-11-28 08:32:26 +01:00
6d0a15a969 fix: ai compose tailscale dns 2025-11-28 08:21:23 +01:00
f4dd7c7d9d fix: litellm compose 2025-11-28 08:14:13 +01:00
608b5ba793 fix: nginx audio mime types 2025-11-27 16:45:14 +01:00
2e45252793 fix: nginx proxy timeouts 2025-11-27 15:24:38 +01:00
20ba9952a1 feat: upscale service 2025-11-27 12:13:57 +01:00
69869ec3fb fix: remove vllm embedding 2025-11-27 01:11:43 +01:00
cc270c8539 fix: vllm model ids 2025-11-27 00:49:53 +01:00
8bdcde4b90 fix: supervisor env 2025-11-26 22:58:16 +01:00
2ab43e8fd3 fix: authelia for audiocraft 2025-11-26 22:56:30 +01:00
5d232c7d9b feat: audiocraft 2025-11-26 22:54:10 +01:00
cef233b678 chore: remove qwen 2025-11-26 21:03:43 +01:00
b63ddbffbd fix(ai): correct bge embedding model name to hosted_vllm/openai prefix
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-25 06:44:33 +01:00
d57a1241d2 feat(ai): add bge-large-en-v1.5 embedding model to litellm
- Add BGE embedding model config (port 8002) to litellm-config.yaml
- Add GPU_VLLM_EMBED_URL env var to compose and .env

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-25 06:40:36 +01:00
ef0309838c refactor(ai): remove crawl4ai service, add backrest config to repo
- Remove crawl4ai service from ai/compose.yaml (will use local MCP instead)
- Remove crawl4ai backup volume from core/compose.yaml
- Add core/backrest/config.json (infrastructure as code)
- Change backrest from volume to bind-mounted config
- Update CLAUDE.md and README.md documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-25 06:20:22 +01:00
071a74a996 revert(ai): remove SUPERVISOR_LOGFILE env var from supervisor-ui
Supervisor XML-RPC API v3.0 (Supervisor 4.3.0) only supports 2-parameter
readLog(offset, length) calls, not 3-parameter calls with filename.
The SUPERVISOR_LOGFILE environment variable is not used by the API.

Testing showed:
- Working: server.supervisor.readLog(-4096, 0)
- Failing: server.supervisor.readLog(-4096, 4096, '/path/to/log')

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-23 23:01:10 +01:00
74b3748b23 feat(ai): add SUPERVISOR_LOGFILE env var to supervisor-ui for RunPod logs
Configure supervisor-ui to use correct logfile path (/workspace/logs/supervisord.log)
for RunPod Supervisor instance. Fixes logs page error on https://supervisor.ai.pivoine.art/logs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-23 22:49:33 +01:00
87216ab26a fix: remove healthcheck from supervisor-ui service 2025-11-23 20:38:37 +01:00
9e2b19e7f6 feat: replace nginx supervisor proxy with modern supervisor-ui
- Replaced nginx:alpine proxy with dev.pivoine.art/valknar/supervisor-ui:latest
- Modern Next.js UI with real-time SSE updates, batch operations, and charts
- Changed service port from 80 (nginx) to 3000 (Next.js)
- Removed supervisor-nginx.conf (no longer needed)
- Kept same URL (supervisor.ai.pivoine.art) and Authelia SSO protection
- Added health check for /api/health endpoint
- Service connects to RunPod Supervisor via Tailscale (SUPERVISOR_HOST/PORT)
2025-11-23 20:18:29 +01:00
a80c6b931b fix: update compose.yaml to use new GPU_VLLM URLs 2025-11-23 16:22:54 +01:00
64c02228d8 fix: use EMPTY api_key for vLLM servers 2025-11-23 16:17:27 +01:00
55d9bef18a fix: remove api_key from vLLM config to fix authentication error
vLLM servers don't validate API keys, so LiteLLM shouldn't pass them

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-23 16:16:37 +01:00
7fc945e179 fix: update LiteLLM config for direct vLLM server access
- Replace orchestrator routing with direct vLLM server connections
- Qwen 2.5 7B on port 8000 (GPU_VLLM_QWEN_URL)
- Llama 3.1 8B on port 8001 (GPU_VLLM_LLAMA_URL)
- Simplify architecture by removing orchestrator proxy layer

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-23 16:10:20 +01:00
94ab4ae6dd feat: enable system message support for qwen-2.5-7b 2025-11-23 14:36:34 +01:00
779e76974d fix: use complete URL env var for vLLM API base
- Replace GPU_TAILSCALE_IP interpolation with GPU_VLLM_API_URL
- LiteLLM requires full URL in api_base with os.environ/ syntax

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-23 13:17:37 +01:00
f3f32c163f feat: consolidate GPU IP with single GPU_TAILSCALE_IP variable
- Replace COMFYUI_BACKEND_HOST and SUPERVISOR_BACKEND_HOST with GPU_TAILSCALE_IP
- Update LiteLLM config to use os.environ/GPU_TAILSCALE_IP for vLLM models
- Add GPU_TAILSCALE_IP env var to LiteLLM service
- Configure qwen-2.5-7b and llama-3.1-8b to route through orchestrator

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-23 13:05:33 +01:00
e00e959543 Update backend IPs for ComfyUI and Supervisor proxies
- Remove hardcoded default values from compose.yaml
- Backend IPs now managed via environment variables only

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-23 02:11:19 +01:00
0fd2eacad1 feat: add Supervisor proxy with Authelia SSO
Add nginx reverse proxy service for Supervisor web UI at supervisor.ai.pivoine.art with Authelia authentication. Proxies to RunPod GPU instance via Tailscale (100.121.199.88:9001).

Changes:
- Create supervisor-nginx.conf for nginx proxy configuration
- Add supervisor service to docker-compose with Traefik labels
- Add supervisor.ai.pivoine.art to Authelia protected domains
- Remove deprecated Flux-related files

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-22 13:19:02 +01:00
bf402adb25 Add Llama 3.1 8B model to LiteLLM configuration 2025-11-21 21:30:18 +01:00
ae1c349b55 feat: make ComfyUI backend IP/port configurable via environment variables
- Replace hardcoded IP in comfyui-nginx.conf with env vars
- Add COMFYUI_BACKEND_HOST and COMFYUI_BACKEND_PORT to compose.yaml
- Use envsubst to substitute variables at container startup
- Defaults: 100.121.199.88:8188 (current RunPod Tailscale IP)
2025-11-21 21:24:51 +01:00
66d8c82e47 Remove Flux and MusicGen models from LiteLLM config
ComfyUI now handles Flux image generation directly.
MusicGen is not being used and has been removed.
2025-11-21 21:11:29 +01:00
ea81634ef3 feat: add ComfyUI to Authelia protected domains
- Add comfy.ai.pivoine.art to one_factor authentication policy
- Enables SSO protection for ComfyUI image generation service
2025-11-21 21:05:24 +01:00
25bd020b93 docs: document ComfyUI setup and integration
- Add ComfyUI service to AI stack service list
- Document ComfyUI proxy architecture and configuration
- Include deployment instructions via Ansible
- Explain network topology and access flow
- Add proxy configuration details (nginx, Tailscale, Authelia)
- Document RunPod setup process and model integration
2025-11-21 21:03:35 +01:00
904f7d3c2e feat(ai): add ComfyUI proxy service with Authelia SSO
- Add ComfyUI service to AI stack using nginx:alpine as reverse proxy
- Proxy to RunPod ComfyUI via Tailscale (100.121.199.88:8188)
- Configure Traefik routing for comfy.ai.pivoine.art
- Enable Authelia SSO middleware (net-authelia)
- Support WebSocket connections for real-time updates
- Set appropriate timeouts for image generation (300s)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-21 20:56:20 +01:00
9a964cff3c feat: add Flux image generation function for Open WebUI
- Add flux_image_gen.py manifold function for Flux.1 Schnell
- Auto-mount functions via Docker volume (./functions:/app/backend/data/functions:ro)
- Add comprehensive setup guide in FLUX_SETUP.md
- Update CLAUDE.md with Flux integration documentation
- Infrastructure as code approach - no manual import needed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-21 20:20:33 +01:00
0999e5d29f feat: re-enable Redis caching in LiteLLM now that streaming is fixed 2025-11-21 19:40:57 +01:00
11 changed files with 1001 additions and 277 deletions

View File

@@ -25,7 +25,7 @@ Root `compose.yaml` uses Docker Compose's `include` directive to orchestrate mul
- **kit**: Unified toolkit with Vert file converter and miniPaint image editor (path-routed) - **kit**: Unified toolkit with Vert file converter and miniPaint image editor (path-routed)
- **jelly**: Jellyfin media server with hardware transcoding - **jelly**: Jellyfin media server with hardware transcoding
- **drop**: PairDrop peer-to-peer file sharing - **drop**: PairDrop peer-to-peer file sharing
- **ai**: AI infrastructure with Open WebUI, Crawl4AI, and pgvector (PostgreSQL) - **ai**: AI infrastructure with Open WebUI, ComfyUI proxy, Crawl4AI, and pgvector (PostgreSQL)
- **asciinema**: Terminal recording and sharing platform (PostgreSQL) - **asciinema**: Terminal recording and sharing platform (PostgreSQL)
- **restic**: Backrest backup system with restic backend - **restic**: Backrest backup system with restic backend
- **netdata**: Real-time infrastructure monitoring - **netdata**: Real-time infrastructure monitoring
@@ -451,11 +451,13 @@ AI infrastructure with Open WebUI, Crawl4AI, and dedicated PostgreSQL with pgvec
- User signup enabled - User signup enabled
- Data persisted in `ai_webui_data` volume - Data persisted in `ai_webui_data` volume
- **crawl4ai**: Crawl4AI web scraping service (internal API, no public access) - **comfyui**: ComfyUI reverse proxy exposed at `comfy.ai.pivoine.art:80`
- Optimized web scraper for LLM content preparation - Nginx-based proxy to ComfyUI running on RunPod GPU server
- Internal API on port 11235 (not exposed via Traefik) - Node-based UI for Flux.1 Schnell image generation workflows
- Designed for integration with Open WebUI and n8n workflows - Proxies to RunPod via Tailscale VPN (100.121.199.88:8188)
- Data persisted in `ai_crawl4ai_data` volume - Protected by Authelia SSO authentication
- WebSocket support for real-time updates
- Stateless architecture (no data persistence on VPS)
**Configuration**: **Configuration**:
- **Claude Integration**: Uses Anthropic API with OpenAI-compatible endpoint - **Claude Integration**: Uses Anthropic API with OpenAI-compatible endpoint
@@ -476,11 +478,71 @@ AI infrastructure with Open WebUI, Crawl4AI, and dedicated PostgreSQL with pgvec
4. Use web search feature for current information 4. Use web search feature for current information
5. Integrate with n8n workflows for automation 5. Integrate with n8n workflows for automation
**Flux Image Generation** (`functions/flux_image_gen.py`):
Open WebUI function for generating images via Flux.1 Schnell on RunPod GPU:
- Manifold function adds "Flux.1 Schnell (4-5s)" model to Open WebUI
- Routes requests through LiteLLM → Orchestrator → RunPod Flux
- Generates 1024x1024 images in 4-5 seconds
- Returns images as base64-encoded markdown
- Configuration via Valves (API base, timeout, default size)
- **Automatically loaded via Docker volume mount** (`./functions:/app/backend/data/functions:ro`)
**Deployment**:
- Function file tracked in `ai/functions/` directory
- Automatically available after `pnpm arty up -d ai_webui`
- No manual import required - infrastructure as code
See `ai/FLUX_SETUP.md` for detailed setup instructions and troubleshooting.
**ComfyUI Image Generation**:
ComfyUI provides a professional node-based interface for creating Flux image generation workflows:
**Architecture**:
```
User → Traefik (VPS) → Authelia SSO → ComfyUI Proxy (nginx) → Tailscale → ComfyUI (RunPod:8188) → Flux Model (GPU)
```
**Access**:
1. Navigate to https://comfy.ai.pivoine.art
2. Authenticate via Authelia SSO
3. Create node-based workflows in ComfyUI interface
4. Use Flux.1 Schnell model from HuggingFace cache at `/workspace/ComfyUI/models/huggingface_cache`
**RunPod Setup** (via Ansible):
ComfyUI is installed on RunPod using the Ansible playbook at `/home/valknar/Projects/runpod/playbook.yml`:
- Clone ComfyUI from https://github.com/comfyanonymous/ComfyUI
- Install dependencies from `models/comfyui/requirements.txt`
- Create model directory structure (checkpoints, unet, vae, loras, clip, controlnet)
- Symlink Flux model from HuggingFace cache
- Start service via `models/comfyui/start.sh` on port 8188
**To deploy ComfyUI on RunPod**:
```bash
# Run Ansible playbook with comfyui tag
ssh -p 16186 root@213.173.110.150
cd /workspace/ai
ansible-playbook playbook.yml --tags comfyui --skip-tags always
# Start ComfyUI service
bash models/comfyui/start.sh &
```
**Proxy Configuration**:
The VPS runs an nginx proxy (`ai/comfyui-nginx.conf`) that:
- Listens on port 80 inside container
- Forwards to RunPod via Tailscale (100.121.199.88:8188)
- Supports WebSocket upgrades for real-time updates
- Handles large file uploads (100M limit)
- Uses extended timeouts for long-running generations (300s)
**Note**: ComfyUI runs directly on RunPod GPU server, not in a container. All data is stored on RunPod's `/workspace` volume.
**Integration Points**: **Integration Points**:
- **n8n**: Workflow automation with AI tasks (scraping, RAG ingestion, webhooks) - **n8n**: Workflow automation with AI tasks (scraping, RAG ingestion, webhooks)
- **Mattermost**: Can send AI-generated notifications via webhooks - **Mattermost**: Can send AI-generated notifications via webhooks
- **Crawl4AI**: Internal API for advanced web scraping - **Crawl4AI**: Internal API for advanced web scraping
- **Claude API**: Primary LLM provider via Anthropic - **Claude API**: Primary LLM provider via Anthropic
- **Flux via RunPod**: Image generation through orchestrator (GPU server) or ComfyUI
**Future Enhancements**: **Future Enhancements**:
- GPU server integration (IONOS A10 planned) - GPU server integration (IONOS A10 planned)
@@ -659,7 +721,7 @@ Backrest backup system with restic backend:
- Retention: 7 daily, 4 weekly, 3 monthly - Retention: 7 daily, 4 weekly, 3 monthly
16. **ai-backup** (3 AM daily) 16. **ai-backup** (3 AM daily)
- Paths: `/volumes/ai_postgres_data`, `/volumes/ai_webui_data`, `/volumes/ai_crawl4ai_data` - Paths: `/volumes/ai_postgres_data`, `/volumes/ai_webui_data`
- Retention: 7 daily, 4 weekly, 6 monthly, 2 yearly - Retention: 7 daily, 4 weekly, 6 monthly, 2 yearly
17. **asciinema-backup** (11 AM daily) 17. **asciinema-backup** (11 AM daily)
@@ -670,8 +732,7 @@ Backrest backup system with restic backend:
All Docker volumes are mounted read-only to `/volumes/` with prefixed names (e.g., `backup_core_postgres_data`) to avoid naming conflicts with other compose stacks. All Docker volumes are mounted read-only to `/volumes/` with prefixed names (e.g., `backup_core_postgres_data`) to avoid naming conflicts with other compose stacks.
**Configuration Management**: **Configuration Management**:
- `config.json` template in repository defines all backup plans - `core/backrest/config.json` in repository defines all backup plans (bind-mounted to container)
- On first run, copy config into volume: `docker cp restic/config.json restic_app:/config/config.json`
- Config version must be `4` for Backrest 1.10.1 compatibility - Config version must be `4` for Backrest 1.10.1 compatibility
- Backrest manages auth automatically (username: `valknar`, password set via web UI on first access) - Backrest manages auth automatically (username: `valknar`, password set via web UI on first access)
@@ -709,7 +770,7 @@ Each service uses named volumes prefixed with project name:
- `vault_data`: Vaultwarden password vault (SQLite database) - `vault_data`: Vaultwarden password vault (SQLite database)
- `joplin_data`: Joplin note-taking data - `joplin_data`: Joplin note-taking data
- `jelly_config`: Jellyfin media server configuration - `jelly_config`: Jellyfin media server configuration
- `ai_postgres_data`, `ai_webui_data`, `ai_crawl4ai_data`: AI stack databases and application data - `ai_postgres_data`, `ai_webui_data`: AI stack databases and application data
- `netdata_config`: Netdata monitoring configuration - `netdata_config`: Netdata monitoring configuration
- `restic_data`, `restic_config`, `restic_cache`, `restic_tmp`: Backrest backup system - `restic_data`, `restic_config`, `restic_cache`, `restic_tmp`: Backrest backup system
- `proxy_letsencrypt_data`: SSL certificates - `proxy_letsencrypt_data`: SSL certificates

View File

@@ -406,11 +406,10 @@ THE FALCON (falcon_network)
│ ├─ vaultwarden [vault.pivoine.art] → Password Manager │ ├─ vaultwarden [vault.pivoine.art] → Password Manager
│ └─ tandoor [tandoor.pivoine.art] → Recipe Manager │ └─ tandoor [tandoor.pivoine.art] → Recipe Manager
├─ 🤖 AI STACK (5 services) ├─ 🤖 AI STACK (4 services)
│ ├─ ai_postgres [Internal] → pgvector Database │ ├─ ai_postgres [Internal] → pgvector Database
│ ├─ webui [ai.pivoine.art] → Open WebUI (Claude) │ ├─ webui [ai.pivoine.art] → Open WebUI (Claude)
│ ├─ litellm [llm.ai.pivoine.art] → API Proxy │ ├─ litellm [llm.ai.pivoine.art] → API Proxy
│ ├─ crawl4ai [Internal:11235] → Web Scraper
│ └─ facefusion [facefusion.ai.pivoine.art] → Face AI │ └─ facefusion [facefusion.ai.pivoine.art] → Face AI
├─ 🛡️ NET STACK (4 services) ├─ 🛡️ NET STACK (4 services)
@@ -435,7 +434,7 @@ THE FALCON (falcon_network)
├─ Core: postgres_data, redis_data, backrest_* ├─ Core: postgres_data, redis_data, backrest_*
├─ Sexy: directus_uploads, directus_bundle ├─ Sexy: directus_uploads, directus_bundle
├─ Util: pairdrop_*, joplin_data, linkwarden_*, mattermost_*, vaultwarden_data, tandoor_* ├─ Util: pairdrop_*, joplin_data, linkwarden_*, mattermost_*, vaultwarden_data, tandoor_*
├─ AI: ai_postgres_data, ai_webui_data, ai_crawl4ai_data, facefusion_* ├─ AI: ai_postgres_data, ai_webui_data, facefusion_*
├─ Net: letsencrypt_data, netdata_* ├─ Net: letsencrypt_data, netdata_*
├─ Media: jelly_config, jelly_cache, filestash_data ├─ Media: jelly_config, jelly_cache, filestash_data
└─ Dev: gitea_*, coolify_data, n8n_data, asciinema_data └─ Dev: gitea_*, coolify_data, n8n_data, asciinema_data

View File

@@ -15,7 +15,7 @@ services:
- ai_postgres_data:/var/lib/postgresql/data - ai_postgres_data:/var/lib/postgresql/data
- ./postgres/init:/docker-entrypoint-initdb.d - ./postgres/init:/docker-entrypoint-initdb.d
healthcheck: healthcheck:
test: ['CMD-SHELL', 'pg_isready -U ${AI_DB_USER}'] test: ["CMD-SHELL", "pg_isready -U ${AI_DB_USER}"]
interval: 30s interval: 30s
timeout: 10s timeout: 10s
retries: 3 retries: 3
@@ -66,101 +66,87 @@ services:
volumes: volumes:
- ai_webui_data:/app/backend/data - ai_webui_data:/app/backend/data
- ./functions:/app/backend/data/functions:ro
depends_on: depends_on:
- ai_postgres - ai_postgres
- litellm - litellm
networks: networks:
- compose_network - compose_network
labels: labels:
- 'traefik.enable=${AI_TRAEFIK_ENABLED}' - "traefik.enable=${AI_TRAEFIK_ENABLED}"
# HTTP to HTTPS redirect # HTTP to HTTPS redirect
- 'traefik.http.middlewares.${AI_COMPOSE_PROJECT_NAME}-redirect-web-secure.redirectscheme.scheme=https' - "traefik.http.middlewares.${AI_COMPOSE_PROJECT_NAME}-redirect-web-secure.redirectscheme.scheme=https"
- 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-web.middlewares=${AI_COMPOSE_PROJECT_NAME}-redirect-web-secure' - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-web.middlewares=${AI_COMPOSE_PROJECT_NAME}-redirect-web-secure"
- 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-web.rule=Host(`${AI_TRAEFIK_HOST}`)' - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-web.rule=Host(`${AI_TRAEFIK_HOST}`)"
- 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-web.entrypoints=web' - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-web.entrypoints=web"
# HTTPS router # HTTPS router
- 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-web-secure.rule=Host(`${AI_TRAEFIK_HOST}`)' - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-web-secure.rule=Host(`${AI_TRAEFIK_HOST}`)"
- 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-web-secure.tls.certresolver=resolver' - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-web-secure.tls.certresolver=resolver"
- 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-web-secure.entrypoints=web-secure' - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-web-secure.entrypoints=web-secure"
- 'traefik.http.middlewares.${AI_COMPOSE_PROJECT_NAME}-web-secure-compress.compress=true' - "traefik.http.middlewares.${AI_COMPOSE_PROJECT_NAME}-web-secure-compress.compress=true"
- 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-web-secure.middlewares=${AI_COMPOSE_PROJECT_NAME}-web-secure-compress,security-headers@file' - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-web-secure.middlewares=${AI_COMPOSE_PROJECT_NAME}-web-secure-compress,security-headers@file"
# Service # Service
- 'traefik.http.services.${AI_COMPOSE_PROJECT_NAME}-web-secure.loadbalancer.server.port=8080' - "traefik.http.services.${AI_COMPOSE_PROJECT_NAME}-web-secure.loadbalancer.server.port=8080"
- 'traefik.docker.network=${NETWORK_NAME}' - "traefik.docker.network=${NETWORK_NAME}"
# Watchtower # Watchtower
- 'com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}' - "com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}"
# LiteLLM - Proxy to convert Anthropic API to OpenAI-compatible format # LiteLLM - Proxy to convert Anthropic API to OpenAI-compatible format
litellm: litellm:
image: ghcr.io/berriai/litellm:main-latest image: ghcr.io/berriai/litellm:main-latest
container_name: ${AI_COMPOSE_PROJECT_NAME}_litellm container_name: ${AI_COMPOSE_PROJECT_NAME}_litellm
restart: unless-stopped restart: unless-stopped
dns:
- 100.100.100.100
- 8.8.8.8
environment: environment:
TZ: ${TIMEZONE:-Europe/Berlin} TZ: ${TIMEZONE:-Europe/Berlin}
ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY} ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY}
LITELLM_MASTER_KEY: ${AI_LITELLM_API_KEY} LITELLM_MASTER_KEY: ${AI_LITELLM_API_KEY}
DATABASE_URL: postgresql://${AI_DB_USER}:${AI_DB_PASSWORD}@ai_postgres:5432/litellm DATABASE_URL: postgresql://${AI_DB_USER}:${AI_DB_PASSWORD}@ai_postgres:5432/litellm
GPU_VLLM_LLAMA_URL: ${GPU_VLLM_LLAMA_URL}
GPU_VLLM_BGE_URL: ${GPU_VLLM_BGE_URL}
# LITELLM_DROP_PARAMS: 'true' # DISABLED: Was breaking streaming # LITELLM_DROP_PARAMS: 'true' # DISABLED: Was breaking streaming
NO_DOCS: 'true' NO_DOCS: "true"
NO_REDOC: 'true' NO_REDOC: "true"
# Performance optimizations # Performance optimizations
LITELLM_LOG: 'DEBUG' # Enable detailed logging for debugging streaming issues LITELLM_LOG: "DEBUG" # Enable detailed logging for debugging streaming issues
LITELLM_MODE: 'PRODUCTION' # Production mode for better performance LITELLM_MODE: "PRODUCTION" # Production mode for better performance
volumes: volumes:
- ./litellm-config.yaml:/app/litellm-config.yaml:ro - ./litellm-config.yaml:/app/litellm-config.yaml:ro
command: command:
[ [
'--config', "--config",
'/app/litellm-config.yaml', "/app/litellm-config.yaml",
'--host', "--host",
'0.0.0.0', "0.0.0.0",
'--port', "--port",
'4000' "4000",
] ]
depends_on: depends_on:
- ai_postgres - ai_postgres
networks:
- compose_network
healthcheck: healthcheck:
disable: true disable: true
labels:
- 'traefik.enable=${AI_TRAEFIK_ENABLED}'
# HTTP to HTTPS redirect
- 'traefik.http.middlewares.${AI_COMPOSE_PROJECT_NAME}-litellm-redirect-web-secure.redirectscheme.scheme=https'
- 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web.middlewares=${AI_COMPOSE_PROJECT_NAME}-litellm-redirect-web-secure'
- 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web.rule=Host(`${AI_LITELLM_TRAEFIK_HOST}`)'
- 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web.entrypoints=web'
# HTTPS router
- 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web-secure.rule=Host(`${AI_LITELLM_TRAEFIK_HOST}`)'
- 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web-secure.tls.certresolver=resolver'
- 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web-secure.entrypoints=web-secure'
- 'traefik.http.middlewares.${AI_COMPOSE_PROJECT_NAME}-litellm-web-secure-compress.compress=true'
- 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web-secure.middlewares=${AI_COMPOSE_PROJECT_NAME}-litellm-web-secure-compress,security-headers@file'
# Service
- 'traefik.http.services.${AI_COMPOSE_PROJECT_NAME}-litellm-web-secure.loadbalancer.server.port=4000'
- 'traefik.docker.network=${NETWORK_NAME}'
# Watchtower
- 'com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}'
# Crawl4AI - Web scraping for LLMs (internal API, no public access)
crawl4ai:
image: ${AI_CRAWL4AI_IMAGE:-unclecode/crawl4ai:latest}
container_name: ${AI_COMPOSE_PROJECT_NAME}_crawl4ai
restart: unless-stopped
environment:
TZ: ${TIMEZONE:-Europe/Berlin}
# API configuration
PORT: ${AI_CRAWL4AI_PORT:-11235}
volumes:
- ai_crawl4ai_data:/app/.crawl4ai
networks: networks:
- compose_network - compose_network
labels: labels:
# No Traefik exposure - internal only - "traefik.enable=${AI_TRAEFIK_ENABLED}"
- 'traefik.enable=false' # HTTP to HTTPS redirect
- "traefik.http.middlewares.${AI_COMPOSE_PROJECT_NAME}-litellm-redirect-web-secure.redirectscheme.scheme=https"
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web.middlewares=${AI_COMPOSE_PROJECT_NAME}-litellm-redirect-web-secure"
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web.rule=Host(`${AI_LITELLM_TRAEFIK_HOST}`)"
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web.entrypoints=web"
# HTTPS router
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web-secure.rule=Host(`${AI_LITELLM_TRAEFIK_HOST}`)"
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web-secure.tls.certresolver=resolver"
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web-secure.entrypoints=web-secure"
- "traefik.http.middlewares.${AI_COMPOSE_PROJECT_NAME}-litellm-web-secure-compress.compress=true"
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web-secure.middlewares=${AI_COMPOSE_PROJECT_NAME}-litellm-web-secure-compress,security-headers@file"
# Service
- "traefik.http.services.${AI_COMPOSE_PROJECT_NAME}-litellm-web-secure.loadbalancer.server.port=4000"
- "traefik.docker.network=${NETWORK_NAME}"
# Watchtower # Watchtower
- 'com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}' - "com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}"
# Facefusion - AI face swapping and enhancement # Facefusion - AI face swapping and enhancement
facefusion: facefusion:
build: build:
@@ -170,7 +156,7 @@ services:
container_name: ${AI_COMPOSE_PROJECT_NAME}_facefusion container_name: ${AI_COMPOSE_PROJECT_NAME}_facefusion
restart: unless-stopped restart: unless-stopped
tty: true tty: true
command: ['python', '-u', 'facefusion.py', 'run'] command: ["python", "-u", "facefusion.py", "run"]
environment: environment:
TZ: ${TIMEZONE:-Europe/Berlin} TZ: ${TIMEZONE:-Europe/Berlin}
GRADIO_SERVER_NAME: "0.0.0.0" GRADIO_SERVER_NAME: "0.0.0.0"
@@ -180,30 +166,175 @@ services:
networks: networks:
- compose_network - compose_network
labels: labels:
- 'traefik.enable=${AI_FACEFUSION_TRAEFIK_ENABLED}' - "traefik.enable=${AI_FACEFUSION_TRAEFIK_ENABLED}"
# HTTP to HTTPS redirect # HTTP to HTTPS redirect
- 'traefik.http.middlewares.${AI_COMPOSE_PROJECT_NAME}-facefusion-redirect-web-secure.redirectscheme.scheme=https' - "traefik.http.middlewares.${AI_COMPOSE_PROJECT_NAME}-facefusion-redirect-web-secure.redirectscheme.scheme=https"
- 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-facefusion-web.middlewares=${AI_COMPOSE_PROJECT_NAME}-facefusion-redirect-web-secure' - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-facefusion-web.middlewares=${AI_COMPOSE_PROJECT_NAME}-facefusion-redirect-web-secure"
- 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-facefusion-web.rule=Host(`${AI_FACEFUSION_TRAEFIK_HOST}`)' - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-facefusion-web.rule=Host(`${AI_FACEFUSION_TRAEFIK_HOST}`)"
- 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-facefusion-web.entrypoints=web' - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-facefusion-web.entrypoints=web"
# HTTPS router with Authelia # HTTPS router with Authelia
- 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-facefusion-web-secure.rule=Host(`${AI_FACEFUSION_TRAEFIK_HOST}`)' - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-facefusion-web-secure.rule=Host(`${AI_FACEFUSION_TRAEFIK_HOST}`)"
- 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-facefusion-web-secure.tls.certresolver=resolver' - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-facefusion-web-secure.tls.certresolver=resolver"
- 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-facefusion-web-secure.entrypoints=web-secure' - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-facefusion-web-secure.entrypoints=web-secure"
- 'traefik.http.middlewares.${AI_COMPOSE_PROJECT_NAME}-facefusion-web-secure-compress.compress=true' - "traefik.http.middlewares.${AI_COMPOSE_PROJECT_NAME}-facefusion-web-secure-compress.compress=true"
- 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-facefusion-web-secure.middlewares=${AI_COMPOSE_PROJECT_NAME}-facefusion-web-secure-compress,net-authelia,security-headers@file' - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-facefusion-web-secure.middlewares=${AI_COMPOSE_PROJECT_NAME}-facefusion-web-secure-compress,net-authelia,security-headers@file"
# Service # Service
- 'traefik.http.services.${AI_COMPOSE_PROJECT_NAME}-facefusion-web-secure.loadbalancer.server.port=7860' - "traefik.http.services.${AI_COMPOSE_PROJECT_NAME}-facefusion-web-secure.loadbalancer.server.port=7860"
- 'traefik.docker.network=${NETWORK_NAME}' - "traefik.docker.network=${NETWORK_NAME}"
# Watchtower - disabled for custom local image # Watchtower - disabled for custom local image
- 'com.centurylinklabs.watchtower.enable=false' - "com.centurylinklabs.watchtower.enable=false"
# ComfyUI - Node-based UI for Flux image generation (proxies to RunPod GPU)
comfyui:
image: nginx:alpine
container_name: ${AI_COMPOSE_PROJECT_NAME}_comfyui
restart: unless-stopped
dns:
- 100.100.100.100
- 8.8.8.8
environment:
TZ: ${TIMEZONE:-Europe/Berlin}
GPU_SERVICE_HOST: ${GPU_TAILSCALE_HOST:-runpod-ai-orchestrator}
GPU_SERVICE_PORT: ${COMFYUI_BACKEND_PORT:-8188}
volumes:
- ./nginx.conf.template:/etc/nginx/nginx.conf.template:ro
command: /bin/sh -c "envsubst '$${GPU_SERVICE_HOST},$${GPU_SERVICE_PORT}' < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf && exec nginx -g 'daemon off;'"
networks:
- compose_network
labels:
- "traefik.enable=${AI_COMFYUI_TRAEFIK_ENABLED:-true}"
# HTTP to HTTPS redirect
- "traefik.http.middlewares.${AI_COMPOSE_PROJECT_NAME}-comfyui-redirect-web-secure.redirectscheme.scheme=https"
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-comfyui-web.middlewares=${AI_COMPOSE_PROJECT_NAME}-comfyui-redirect-web-secure"
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-comfyui-web.rule=Host(`${AI_COMFYUI_TRAEFIK_HOST:-comfy.ai.pivoine.art}`)"
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-comfyui-web.entrypoints=web"
# HTTPS router with Authelia SSO
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-comfyui-web-secure.rule=Host(`${AI_COMFYUI_TRAEFIK_HOST:-comfy.ai.pivoine.art}`)"
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-comfyui-web-secure.tls.certresolver=resolver"
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-comfyui-web-secure.entrypoints=web-secure"
- "traefik.http.middlewares.${AI_COMPOSE_PROJECT_NAME}-comfyui-web-secure-compress.compress=true"
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-comfyui-web-secure.middlewares=${AI_COMPOSE_PROJECT_NAME}-comfyui-web-secure-compress,net-authelia,security-headers@file"
# Service
- "traefik.http.services.${AI_COMPOSE_PROJECT_NAME}-comfyui-web-secure.loadbalancer.server.port=80"
- "traefik.docker.network=${NETWORK_NAME}"
# Watchtower
- "com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}"
audiocraft:
image: nginx:alpine
container_name: ${AI_COMPOSE_PROJECT_NAME}_audiocraft
restart: unless-stopped
dns:
- 100.100.100.100
- 8.8.8.8
environment:
TZ: ${TIMEZONE:-Europe/Berlin}
GPU_SERVICE_HOST: ${GPU_TAILSCALE_HOST:-runpod-ai-orchestrator}
GPU_SERVICE_PORT: ${AUDIOCRAFT_BACKEND_PORT:-7860}
volumes:
- ./nginx.conf.template:/etc/nginx/nginx.conf.template:ro
command: /bin/sh -c "envsubst '$${GPU_SERVICE_HOST},$${GPU_SERVICE_PORT}' < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf && exec nginx -g 'daemon off;'"
networks:
- compose_network
labels:
- "traefik.enable=${AI_AUDIOCRAFT_TRAEFIK_ENABLED:-true}"
# HTTP to HTTPS redirect
- "traefik.http.middlewares.${AI_COMPOSE_PROJECT_NAME}-audiocraft-redirect-web-secure.redirectscheme.scheme=https"
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-audiocraft-web.middlewares=${AI_COMPOSE_PROJECT_NAME}-audiocraft-redirect-web-secure"
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-audiocraft-web.rule=Host(`${AI_AUDIOCRAFT_TRAEFIK_HOST:-audiocraft.ai.pivoine.art}`)"
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-audiocraft-web.entrypoints=web"
# HTTPS router with Authelia SSO
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-audiocraft-web-secure.rule=Host(`${AI_AUDIOCRAFT_TRAEFIK_HOST:-audiocraft.ai.pivoine.art}`)"
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-audiocraft-web-secure.tls.certresolver=resolver"
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-audiocraft-web-secure.entrypoints=web-secure"
- "traefik.http.middlewares.${AI_COMPOSE_PROJECT_NAME}-audiocraft-web-secure-compress.compress=true"
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-audiocraft-web-secure.middlewares=${AI_COMPOSE_PROJECT_NAME}-audiocraft-web-secure-compress,net-authelia,security-headers@file"
# Service
- "traefik.http.services.${AI_COMPOSE_PROJECT_NAME}-audiocraft-web-secure.loadbalancer.server.port=80"
- "traefik.docker.network=${NETWORK_NAME}"
# Watchtower
- "com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}"
upscale:
image: nginx:alpine
container_name: ${AI_COMPOSE_PROJECT_NAME}_upscale
restart: unless-stopped
dns:
- 100.100.100.100
- 8.8.8.8
environment:
TZ: ${TIMEZONE:-Europe/Berlin}
GPU_SERVICE_HOST: ${GPU_TAILSCALE_HOST:-runpod-ai-orchestrator}
GPU_SERVICE_PORT: ${UPSCALE_BACKEND_PORT:-8080}
volumes:
- ./nginx.conf.template:/etc/nginx/nginx.conf.template:ro
command: /bin/sh -c "envsubst '$${GPU_SERVICE_HOST},$${GPU_SERVICE_PORT}' < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf && exec nginx -g 'daemon off;'"
networks:
- compose_network
labels:
- "traefik.enable=${AI_UPSCALE_TRAEFIK_ENABLED:-true}"
# HTTP to HTTPS redirect
- "traefik.http.middlewares.${AI_COMPOSE_PROJECT_NAME}-upscale-redirect-web-secure.redirectscheme.scheme=https"
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-upscale-web.middlewares=${AI_COMPOSE_PROJECT_NAME}-upscale-redirect-web-secure"
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-upscale-web.rule=Host(`${AI_UPSCALE_TRAEFIK_HOST:-upscale.ai.pivoine.art}`)"
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-upscale-web.entrypoints=web"
# HTTPS router with Authelia SSO
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-upscale-web-secure.rule=Host(`${AI_UPSCALE_TRAEFIK_HOST:-upscale.ai.pivoine.art}`)"
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-upscale-web-secure.tls.certresolver=resolver"
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-upscale-web-secure.entrypoints=web-secure"
- "traefik.http.middlewares.${AI_COMPOSE_PROJECT_NAME}-upscale-web-secure-compress.compress=true"
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-upscale-web-secure.middlewares=${AI_COMPOSE_PROJECT_NAME}-upscale-web-secure-compress,net-authelia,security-headers@file"
# Service
- "traefik.http.services.${AI_COMPOSE_PROJECT_NAME}-upscale-web-secure.loadbalancer.server.port=80"
- "traefik.docker.network=${NETWORK_NAME}"
# Watchtower
- "com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}"
# Supervisor UI - Modern web interface for RunPod process management
supervisor:
image: dev.pivoine.art/valknar/supervisor-ui:latest
container_name: ${AI_COMPOSE_PROJECT_NAME}_supervisor_ui
restart: unless-stopped
dns:
- 100.100.100.100
- 8.8.8.8
environment:
TZ: ${TIMEZONE:-Europe/Berlin}
NODE_ENV: production
# Connect to RunPod Supervisor via Tailscale (host Tailscale provides DNS)
SUPERVISOR_HOST: ${GPU_TAILSCALE_HOST:-runpod-ai-orchestrator}
SUPERVISOR_PORT: ${SUPERVISOR_BACKEND_PORT:-9001}
# No auth needed - Supervisor has auth disabled (protected by Authelia)
networks:
- compose_network
labels:
- "traefik.enable=${AI_SUPERVISOR_TRAEFIK_ENABLED:-true}"
# HTTP to HTTPS redirect
- "traefik.http.middlewares.${AI_COMPOSE_PROJECT_NAME}-supervisor-redirect-web-secure.redirectscheme.scheme=https"
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-supervisor-web.middlewares=${AI_COMPOSE_PROJECT_NAME}-supervisor-redirect-web-secure"
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-supervisor-web.rule=Host(`${AI_SUPERVISOR_TRAEFIK_HOST:-supervisor.ai.pivoine.art}`)"
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-supervisor-web.entrypoints=web"
# HTTPS router with Authelia SSO
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-supervisor-web-secure.rule=Host(`${AI_SUPERVISOR_TRAEFIK_HOST:-supervisor.ai.pivoine.art}`)"
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-supervisor-web-secure.tls.certresolver=resolver"
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-supervisor-web-secure.entrypoints=web-secure"
- "traefik.http.middlewares.${AI_COMPOSE_PROJECT_NAME}-supervisor-web-secure-compress.compress=true"
- "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-supervisor-web-secure.middlewares=${AI_COMPOSE_PROJECT_NAME}-supervisor-web-secure-compress,net-authelia,security-headers@file"
# Service (port 3000 for Next.js app)
- "traefik.http.services.${AI_COMPOSE_PROJECT_NAME}-supervisor-web-secure.loadbalancer.server.port=3000"
- "traefik.docker.network=${NETWORK_NAME}"
# Watchtower
- "com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}"
volumes: volumes:
ai_postgres_data: ai_postgres_data:
name: ${AI_COMPOSE_PROJECT_NAME}_postgres_data name: ${AI_COMPOSE_PROJECT_NAME}_postgres_data
ai_webui_data: ai_webui_data:
name: ${AI_COMPOSE_PROJECT_NAME}_webui_data name: ${AI_COMPOSE_PROJECT_NAME}_webui_data
ai_crawl4ai_data:
name: ${AI_COMPOSE_PROJECT_NAME}_crawl4ai_data
ai_facefusion_data: ai_facefusion_data:
name: ${AI_COMPOSE_PROJECT_NAME}_facefusion_data name: ${AI_COMPOSE_PROJECT_NAME}_facefusion_data
networks:
compose_network:
name: ${NETWORK_NAME}
external: true

View File

@@ -25,65 +25,56 @@ model_list:
api_key: os.environ/ANTHROPIC_API_KEY api_key: os.environ/ANTHROPIC_API_KEY
# =========================================================================== # ===========================================================================
# SELF-HOSTED MODELS VIA ORCHESTRATOR (GPU Server via Tailscale VPN) # SELF-HOSTED MODELS - DIRECT vLLM SERVERS (GPU Server via Tailscale VPN)
# =========================================================================== # ===========================================================================
# All requests route through orchestrator (port 9000) which manages model loading # Direct connections to dedicated vLLM servers (no orchestrator)
# Text Generation # Text Generation - Llama 3.1 8B (Port 8001)
- model_name: qwen-2.5-7b - model_name: llama-3.1-8b
litellm_params: litellm_params:
model: hosted_vllm/openai/qwen-2.5-7b # hosted_vllm/openai/ for vLLM via orchestrator model: hosted_vllm/meta-llama/Llama-3.1-8B-Instruct # hosted_vllm/openai/ prefix for proper streaming
api_base: http://100.121.199.88:9000/v1 # RunPod GPU via Tailscale api_base: os.environ/GPU_VLLM_LLAMA_URL # Direct to vLLM Llama server
api_key: dummy api_key: "EMPTY" # vLLM doesn't validate API keys
rpm: 1000 rpm: 1000
tpm: 100000 tpm: 100000
timeout: 600 # 10 minutes for generation timeout: 600 # 10 minutes for generation
stream_timeout: 600 stream_timeout: 600
supports_system_messages: false # vLLM handles system messages differently supports_system_messages: true # Llama supports system messages
stream: true # Enable streaming by default stream: true # Enable streaming by default
# Image Generation # Embeddings - BGE Large (Port 8002)
- model_name: flux-schnell - model_name: bge-large-en
litellm_params: litellm_params:
model: openai/dall-e-3 # OpenAI-compatible mapping model: openai/BAAI/bge-large-en-v1.5
api_base: http://100.121.199.88:9000/v1 # RunPod GPU via Tailscale api_base: os.environ/GPU_VLLM_BGE_URL
api_key: dummy api_key: "EMPTY"
rpm: 100 rpm: 1000
max_parallel_requests: 3 tpm: 500000
# Music Generation
- model_name: musicgen-medium
litellm_params:
model: openai/musicgen-medium
api_base: http://100.121.199.88:9000/v1 # RunPod GPU via Tailscale
api_key: dummy
rpm: 50
max_parallel_requests: 1
litellm_settings: litellm_settings:
drop_params: false # DISABLED: Was breaking streaming drop_params: false # DISABLED: Was breaking streaming
set_verbose: true # Enable verbose logging for debugging streaming issues set_verbose: true # Enable verbose logging for debugging streaming issues
# Disable caching - it breaks streaming responses # Enable caching now that streaming is fixed
cache: false cache: true
# cache_params: cache_params:
# type: redis type: redis
# host: redis host: core_redis
# port: 6379 port: 6379
# ttl: 3600 # Cache for 1 hour ttl: 3600 # Cache for 1 hour
# Force strip specific parameters globally # Force strip specific parameters globally
allowed_fails: 0 allowed_fails: 0
# Modify params before sending to provider # Modify params before sending to provider
modify_params: false # DISABLED: Was breaking streaming modify_params: false # DISABLED: Was breaking streaming
# Enable success and failure logging but minimize overhead # Enable success and failure logging but minimize overhead
success_callback: [] # Disable all success callbacks to reduce DB writes success_callback: [] # Disable all success callbacks to reduce DB writes
failure_callback: [] # Disable all failure callbacks failure_callback: [] # Disable all failure callbacks
router_settings: router_settings:
allowed_fails: 0 allowed_fails: 0
# Drop unsupported parameters # Drop unsupported parameters
default_litellm_params: default_litellm_params:
drop_params: false # DISABLED: Was breaking streaming drop_params: false # DISABLED: Was breaking streaming
general_settings: general_settings:
disable_responses_id_security: true disable_responses_id_security: true

60
ai/nginx.conf.template Normal file
View File

@@ -0,0 +1,60 @@
events {
worker_connections 1024;
}
http {
# MIME types
include /etc/nginx/mime.types;
default_type application/octet-stream;
# DNS resolver for Tailscale MagicDNS
resolver 100.100.100.100 8.8.8.8 valid=30s;
resolver_timeout 5s;
# Proxy settings
proxy_http_version 1.1;
proxy_buffering off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket support
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Timeouts for long-running audio/image generation
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
send_timeout 600;
server {
listen 80;
server_name _;
# Increase client body size for image uploads
client_max_body_size 100M;
location / {
# Proxy to service on RunPod via Tailscale
# Use variable to force runtime DNS resolution (not startup)
set $backend http://${GPU_SERVICE_HOST}:${GPU_SERVICE_PORT};
proxy_pass $backend;
# WebSocket upgrade
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Proxy headers
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Disable buffering for real-time updates
proxy_buffering off;
}
}
}

View File

@@ -78,16 +78,20 @@ envs:
UTIL_JOPLIN_DB_NAME: joplin UTIL_JOPLIN_DB_NAME: joplin
# PairDrop # PairDrop
UTIL_DROP_TRAEFIK_HOST: drop.pivoine.art UTIL_DROP_TRAEFIK_HOST: drop.pivoine.art
# Media Stack (Jellyfin, Filestash, Pinchflat) # Media Stack (Jellyfin, Pinchflat)
MEDIA_TRAEFIK_ENABLED: true MEDIA_TRAEFIK_ENABLED: true
MEDIA_COMPOSE_PROJECT_NAME: media MEDIA_COMPOSE_PROJECT_NAME: media
MEDIA_JELLYFIN_IMAGE: jellyfin/jellyfin:latest MEDIA_JELLYFIN_IMAGE: jellyfin/jellyfin:latest
MEDIA_JELLYFIN_TRAEFIK_HOST: jellyfin.media.pivoine.art MEDIA_JELLYFIN_TRAEFIK_HOST: jellyfin.media.pivoine.art
MEDIA_FILESTASH_IMAGE: machines/filestash:latest
MEDIA_FILESTASH_TRAEFIK_HOST: filestash.media.pivoine.art
MEDIA_FILESTASH_CANARY: true
MEDIA_PINCHFLAT_IMAGE: ghcr.io/kieraneglin/pinchflat:latest MEDIA_PINCHFLAT_IMAGE: ghcr.io/kieraneglin/pinchflat:latest
MEDIA_PINCHFLAT_TRAEFIK_HOST: pinchflat.media.pivoine.art MEDIA_PINCHFLAT_TRAEFIK_HOST: pinchflat.media.pivoine.art
# Immich - Photo and video management
MEDIA_IMMICH_SERVER_IMAGE: ghcr.io/immich-app/immich-server:release
MEDIA_IMMICH_ML_IMAGE: ghcr.io/immich-app/immich-machine-learning:release
MEDIA_IMMICH_POSTGRES_IMAGE: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0
MEDIA_IMMICH_TRAEFIK_HOST: immich.media.pivoine.art
MEDIA_IMMICH_DB_NAME: immich
MEDIA_IMMICH_DB_USER: immich
# Dev (Gitea + Coolify) # Dev (Gitea + Coolify)
DEV_TRAEFIK_ENABLED: true DEV_TRAEFIK_ENABLED: true
DEV_COMPOSE_PROJECT_NAME: dev DEV_COMPOSE_PROJECT_NAME: dev
@@ -135,7 +139,6 @@ envs:
AI_COMPOSE_PROJECT_NAME: ai AI_COMPOSE_PROJECT_NAME: ai
AI_POSTGRES_IMAGE: pgvector/pgvector:pg16 AI_POSTGRES_IMAGE: pgvector/pgvector:pg16
AI_WEBUI_IMAGE: ghcr.io/open-webui/open-webui:main AI_WEBUI_IMAGE: ghcr.io/open-webui/open-webui:main
AI_CRAWL4AI_IMAGE: unclecode/crawl4ai:latest
AI_FACEFUSION_IMAGE: facefusion/facefusion:3.5.0-cpu AI_FACEFUSION_IMAGE: facefusion/facefusion:3.5.0-cpu
AI_FACEFUSION_TRAEFIK_ENABLED: true AI_FACEFUSION_TRAEFIK_ENABLED: true
AI_FACEFUSION_TRAEFIK_HOST: facefusion.ai.pivoine.art AI_FACEFUSION_TRAEFIK_HOST: facefusion.ai.pivoine.art
@@ -263,3 +266,57 @@ scripts:
docker restart sexy_api && docker restart sexy_api &&
echo "✓ Directus API restarted" echo "✓ Directus API restarted"
net/create: docker network create "$NETWORK_NAME" net/create: docker network create "$NETWORK_NAME"
# Setup iptables NAT for Docker containers to reach Tailscale network
# Requires Tailscale installed on host: curl -fsSL https://tailscale.com/install.sh | sh
tailscale/setup: |
echo "Setting up iptables for Docker-to-Tailscale routing..."
# Enable IP forwarding
sudo sysctl -w net.ipv4.ip_forward=1
grep -q "net.ipv4.ip_forward=1" /etc/sysctl.conf || echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.conf
# Get Docker network CIDR
DOCKER_CIDR=$(docker network inspect ${NETWORK_NAME} --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}' 2>/dev/null || echo "172.18.0.0/16")
echo "Docker network CIDR: $DOCKER_CIDR"
# Add NAT rule (check if already exists)
if ! sudo iptables -t nat -C POSTROUTING -s "$DOCKER_CIDR" -o tailscale0 -j MASQUERADE 2>/dev/null; then
sudo iptables -t nat -A POSTROUTING -s "$DOCKER_CIDR" -o tailscale0 -j MASQUERADE
echo "✓ iptables NAT rule added"
else
echo "✓ iptables NAT rule already exists"
fi
# Persist rules
sudo netfilter-persistent save 2>/dev/null || echo "Install iptables-persistent to persist rules: sudo apt install iptables-persistent"
echo "✓ Tailscale routing configured"
# Install and configure Tailscale on host with persistent state
tailscale/install: |
echo "Installing Tailscale..."
# Install Tailscale if not present
if ! command -v tailscale &> /dev/null; then
curl -fsSL https://tailscale.com/install.sh | sh
else
echo "✓ Tailscale already installed"
fi
# Create state directory for persistence
TAILSCALE_STATE="/var/lib/tailscale"
sudo mkdir -p "$TAILSCALE_STATE"
# Start and enable tailscaled service
sudo systemctl enable --now tailscaled
# Connect to Tailscale network
echo "Connecting to Tailscale..."
sudo tailscale up --authkey="$TAILSCALE_AUTHKEY" --hostname=vps
# Show status
echo ""
tailscale status
echo ""
echo "✓ Tailscale installed and connected"
echo " Run 'arty tailscale/setup' to configure iptables routing for Docker"

370
core/backrest/config.json Normal file
View File

@@ -0,0 +1,370 @@
{
"modno": 1,
"version": 4,
"instance": "falcon",
"repos": [
{
"id": "hidrive-backup",
"uri": "/repos",
"guid": "df03886ea215b0a3ff9730190d906d7034032bf0f1906ed4ad00f2c4f1748215",
"password": "falcon-backup-2025",
"prunePolicy": {
"schedule": {
"cron": "0 2 * * 0"
}
},
"checkPolicy": {
"schedule": {
"cron": "0 3 * * 0"
}
},
"autoUnlock": true
}
],
"plans": [
{
"id": "ai-backup",
"repo": "hidrive-backup",
"paths": [
"/volumes/ai_postgres_data",
"/volumes/ai_webui_data"
],
"schedule": {
"cron": "0 3 * * *"
},
"retention": {
"policyTimeBucketed": {
"daily": 7,
"weekly": 4,
"monthly": 6,
"yearly": 2
}
}
},
{
"id": "asciinema-backup",
"repo": "hidrive-backup",
"paths": [
"/volumes/asciinema_data"
],
"schedule": {
"cron": "0 11 * * *"
},
"retention": {
"policyTimeBucketed": {
"daily": 7,
"weekly": 4,
"monthly": 6,
"yearly": 2
}
}
},
{
"id": "coolify-backup",
"repo": "hidrive-backup",
"paths": [
"/volumes/dev_coolify_data"
],
"schedule": {
"cron": "0 0 * * *"
},
"retention": {
"policyTimeBucketed": {
"daily": 7,
"weekly": 4,
"monthly": 6,
"yearly": 2
}
}
},
{
"id": "directus-bundle-backup",
"repo": "hidrive-backup",
"paths": [
"/volumes/directus_bundle"
],
"schedule": {
"cron": "0 4 * * *"
},
"retention": {
"policyTimeBucketed": {
"daily": 7,
"weekly": 4,
"monthly": 3
}
}
},
{
"id": "directus-uploads-backup",
"repo": "hidrive-backup",
"paths": [
"/volumes/directus_uploads"
],
"schedule": {
"cron": "0 4 * * *"
},
"retention": {
"policyTimeBucketed": {
"daily": 7,
"weekly": 4,
"monthly": 6,
"yearly": 2
}
}
},
{
"id": "gitea-backup",
"repo": "hidrive-backup",
"paths": [
"/volumes/dev_gitea_config",
"/volumes/dev_gitea_data",
"/volumes/dev_gitea_runner_data"
],
"schedule": {
"cron": "0 11 * * *"
},
"retention": {
"policyTimeBucketed": {
"daily": 7,
"weekly": 4,
"monthly": 6,
"yearly": 2
}
}
},
{
"id": "jellyfin-backup",
"repo": "hidrive-backup",
"paths": [
"/volumes/jelly_config"
],
"schedule": {
"cron": "0 9 * * *"
},
"retention": {
"policyTimeBucketed": {
"daily": 7,
"weekly": 4,
"monthly": 6,
"yearly": 2
}
}
},
{
"id": "joplin-backup",
"repo": "hidrive-backup",
"paths": [
"/volumes/joplin_data"
],
"schedule": {
"cron": "0 2 * * *"
},
"retention": {
"policyTimeBucketed": {
"daily": 7,
"weekly": 4,
"monthly": 6,
"yearly": 2
}
}
},
{
"id": "letsencrypt-backup",
"repo": "hidrive-backup",
"paths": [
"/volumes/letsencrypt_data"
],
"schedule": {
"cron": "0 8 * * *"
},
"retention": {
"policyTimeBucketed": {
"daily": 7,
"weekly": 4,
"monthly": 12,
"yearly": 3
}
}
},
{
"id": "linkwarden-backup",
"repo": "hidrive-backup",
"paths": [
"/volumes/linkwarden_data",
"/volumes/linkwarden_meili_data"
],
"schedule": {
"cron": "0 7 * * *"
},
"retention": {
"policyTimeBucketed": {
"daily": 7,
"weekly": 4,
"monthly": 6
}
}
},
{
"id": "mattermost-backup",
"repo": "hidrive-backup",
"paths": [
"/volumes/mattermost_config",
"/volumes/mattermost_data",
"/volumes/mattermost_plugins"
],
"schedule": {
"cron": "0 5 * * *"
},
"retention": {
"policyTimeBucketed": {
"daily": 7,
"weekly": 4,
"monthly": 6,
"yearly": 2
}
}
},
{
"id": "n8n-backup",
"repo": "hidrive-backup",
"paths": [
"/volumes/n8n_data"
],
"schedule": {
"cron": "0 6 * * *"
},
"retention": {
"policyTimeBucketed": {
"daily": 7,
"weekly": 4,
"monthly": 6
}
}
},
{
"id": "netdata-backup",
"repo": "hidrive-backup",
"paths": [
"/volumes/netdata_config"
],
"schedule": {
"cron": "0 10 * * *"
},
"retention": {
"policyTimeBucketed": {
"daily": 7,
"weekly": 4,
"monthly": 3
}
}
},
{
"id": "postgres-backup",
"repo": "hidrive-backup",
"paths": [
"/volumes/core_postgres_data"
],
"schedule": {
"cron": "0 2 * * *"
},
"retention": {
"policyTimeBucketed": {
"daily": 7,
"weekly": 4,
"monthly": 6,
"yearly": 2
}
}
},
{
"id": "redis-backup",
"repo": "hidrive-backup",
"paths": [
"/volumes/core_redis_data"
],
"schedule": {
"cron": "0 3 * * *"
},
"retention": {
"policyTimeBucketed": {
"daily": 7,
"weekly": 4,
"monthly": 3
}
}
},
{
"id": "scrapy-backup",
"repo": "hidrive-backup",
"paths": [
"/volumes/scrapy_code",
"/volumes/scrapyd_data"
],
"schedule": {
"cron": "0 6 * * *"
},
"retention": {
"policyTimeBucketed": {
"daily": 7,
"weekly": 4,
"monthly": 3
}
}
},
{
"id": "tandoor-backup",
"repo": "hidrive-backup",
"paths": [
"/volumes/tandoor_mediafiles",
"/volumes/tandoor_staticfiles"
],
"schedule": {
"cron": "0 5 * * *"
},
"retention": {
"policyTimeBucketed": {
"daily": 7,
"weekly": 4,
"monthly": 6
}
}
},
{
"id": "vaultwarden-backup",
"repo": "hidrive-backup",
"paths": [
"/volumes/vaultwarden_data"
],
"schedule": {
"cron": "0 8 * * *"
},
"retention": {
"policyTimeBucketed": {
"daily": 7,
"weekly": 4,
"monthly": 12,
"yearly": 3
}
}
},
{
"id": "immich-backup",
"repo": "hidrive-backup",
"paths": [
"/volumes/immich_postgres_data",
"/volumes/immich_upload"
],
"schedule": {
"cron": "0 12 * * *"
},
"retention": {
"policyTimeBucketed": {
"daily": 7,
"weekly": 4,
"monthly": 6,
"yearly": 2
}
}
}
]
}

View File

@@ -56,7 +56,7 @@ services:
volumes: volumes:
# Backrest application data # Backrest application data
- backrest_data:/data - backrest_data:/data
- backrest_config:/config - ./backrest:/config
- backrest_cache:/cache - backrest_cache:/cache
- backrest_tmp:/tmp - backrest_tmp:/tmp
@@ -74,7 +74,6 @@ services:
- backup_util_tandoor_staticfiles:/volumes/tandoor_staticfiles:ro - backup_util_tandoor_staticfiles:/volumes/tandoor_staticfiles:ro
- backup_util_tandoor_mediafiles:/volumes/tandoor_mediafiles:ro - backup_util_tandoor_mediafiles:/volumes/tandoor_mediafiles:ro
- backup_n8n_data:/volumes/n8n_data:ro - backup_n8n_data:/volumes/n8n_data:ro
- backup_filestash_data:/volumes/filestash_data:ro
- backup_util_linkwarden_data:/volumes/linkwarden_data:ro - backup_util_linkwarden_data:/volumes/linkwarden_data:ro
- backup_util_linkwarden_meili_data:/volumes/linkwarden_meili_data:ro - backup_util_linkwarden_meili_data:/volumes/linkwarden_meili_data:ro
- backup_letsencrypt_data:/volumes/letsencrypt_data:ro - backup_letsencrypt_data:/volumes/letsencrypt_data:ro
@@ -84,12 +83,13 @@ services:
- backup_netdata_config:/volumes/netdata_config:ro - backup_netdata_config:/volumes/netdata_config:ro
- backup_ai_postgres_data:/volumes/ai_postgres_data:ro - backup_ai_postgres_data:/volumes/ai_postgres_data:ro
- backup_ai_webui_data:/volumes/ai_webui_data:ro - backup_ai_webui_data:/volumes/ai_webui_data:ro
- backup_ai_crawl4ai_data:/volumes/ai_crawl4ai_data:ro
- backup_asciinema_data:/volumes/asciinema_data:ro - backup_asciinema_data:/volumes/asciinema_data:ro
- backup_dev_gitea_data:/volumes/dev_gitea_data:ro - backup_dev_gitea_data:/volumes/dev_gitea_data:ro
- backup_dev_gitea_config:/volumes/dev_gitea_config:ro - backup_dev_gitea_config:/volumes/dev_gitea_config:ro
- backup_dev_gitea_runner_data:/volumes/dev_gitea_runner_data:ro - backup_dev_gitea_runner_data:/volumes/dev_gitea_runner_data:ro
- backup_dev_coolify_data:/volumes/dev_coolify_data:ro - backup_dev_coolify_data:/volumes/dev_coolify_data:ro
- backup_media_immich_postgres_data:/volumes/immich_postgres_data:ro
- backup_media_immich_upload:/volumes/immich_upload:ro
environment: environment:
TZ: ${TIMEZONE:-Europe/Berlin} TZ: ${TIMEZONE:-Europe/Berlin}
@@ -124,8 +124,6 @@ volumes:
name: ${CORE_COMPOSE_PROJECT_NAME}_redis_data name: ${CORE_COMPOSE_PROJECT_NAME}_redis_data
backrest_data: backrest_data:
name: ${CORE_COMPOSE_PROJECT_NAME}_backrest_data name: ${CORE_COMPOSE_PROJECT_NAME}_backrest_data
backrest_config:
name: ${CORE_COMPOSE_PROJECT_NAME}_backrest_config
backrest_cache: backrest_cache:
name: ${CORE_COMPOSE_PROJECT_NAME}_backrest_cache name: ${CORE_COMPOSE_PROJECT_NAME}_backrest_cache
backrest_tmp: backrest_tmp:
@@ -162,9 +160,6 @@ volumes:
backup_n8n_data: backup_n8n_data:
name: dev_n8n_data name: dev_n8n_data
external: true external: true
backup_filestash_data:
name: stash_filestash_data
external: true
backup_util_linkwarden_data: backup_util_linkwarden_data:
name: util_linkwarden_data name: util_linkwarden_data
external: true external: true
@@ -192,9 +187,6 @@ volumes:
backup_ai_webui_data: backup_ai_webui_data:
name: ai_webui_data name: ai_webui_data
external: true external: true
backup_ai_crawl4ai_data:
name: ai_crawl4ai_data
external: true
backup_asciinema_data: backup_asciinema_data:
name: dev_asciinema_data name: dev_asciinema_data
external: true external: true
@@ -210,3 +202,9 @@ volumes:
backup_dev_coolify_data: backup_dev_coolify_data:
name: dev_coolify_data name: dev_coolify_data
external: true external: true
backup_media_immich_postgres_data:
name: media_immich_postgres_data
external: true
backup_media_immich_upload:
name: media_immich_upload
external: true

View File

@@ -33,36 +33,6 @@ services:
# Watchtower # Watchtower
- 'com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}' - 'com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}'
# Filestash - Web-based file manager
filestash:
image: ${MEDIA_FILESTASH_IMAGE:-machines/filestash:latest}
container_name: ${MEDIA_COMPOSE_PROJECT_NAME}_filestash
restart: unless-stopped
volumes:
- filestash_data:/app/data/state/
tmpfs:
- /tmp:exec
environment:
TZ: ${TIMEZONE:-Europe/Berlin}
APPLICATION_URL: ${MEDIA_FILESTASH_TRAEFIK_HOST}
CANARY: ${MEDIA_FILESTASH_CANARY:-true}
networks:
- compose_network
labels:
- 'traefik.enable=${MEDIA_TRAEFIK_ENABLED}'
- 'traefik.http.middlewares.${MEDIA_COMPOSE_PROJECT_NAME}-filestash-redirect-web-secure.redirectscheme.scheme=https'
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-filestash-web.middlewares=${MEDIA_COMPOSE_PROJECT_NAME}-filestash-redirect-web-secure'
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-filestash-web.rule=Host(`${MEDIA_FILESTASH_TRAEFIK_HOST}`)'
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-filestash-web.entrypoints=web'
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-filestash-web-secure.rule=Host(`${MEDIA_FILESTASH_TRAEFIK_HOST}`)'
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-filestash-web-secure.tls.certresolver=resolver'
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-filestash-web-secure.entrypoints=web-secure'
- 'traefik.http.middlewares.${MEDIA_COMPOSE_PROJECT_NAME}-filestash-web-secure-compress.compress=true'
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-filestash-web-secure.middlewares=${MEDIA_COMPOSE_PROJECT_NAME}-filestash-web-secure-compress'
- 'traefik.http.services.${MEDIA_COMPOSE_PROJECT_NAME}-filestash-web-secure.loadbalancer.server.port=8334'
- 'traefik.docker.network=${NETWORK_NAME}'
- 'com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}'
# Pinchflat - YouTube download manager # Pinchflat - YouTube download manager
pinchflat: pinchflat:
image: ${MEDIA_PINCHFLAT_IMAGE:-ghcr.io/kieraneglin/pinchflat:latest} image: ${MEDIA_PINCHFLAT_IMAGE:-ghcr.io/kieraneglin/pinchflat:latest}
@@ -95,15 +65,98 @@ services:
# Watchtower # Watchtower
- 'com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}' - 'com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}'
# Immich PostgreSQL - Dedicated database with vector extensions
immich_postgres:
image: ${MEDIA_IMMICH_POSTGRES_IMAGE:-ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0}
container_name: ${MEDIA_COMPOSE_PROJECT_NAME}_immich_postgres
restart: unless-stopped
environment:
TZ: ${TIMEZONE:-Europe/Berlin}
POSTGRES_USER: ${MEDIA_IMMICH_DB_USER:-immich}
POSTGRES_PASSWORD: ${MEDIA_IMMICH_DB_PASSWORD}
POSTGRES_DB: ${MEDIA_IMMICH_DB_NAME:-immich}
POSTGRES_INITDB_ARGS: --data-checksums
volumes:
- immich_postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${MEDIA_IMMICH_DB_USER:-immich} -d ${MEDIA_IMMICH_DB_NAME:-immich}"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
networks:
- compose_network
# Immich Server - Main application
immich_server:
image: ${MEDIA_IMMICH_SERVER_IMAGE:-ghcr.io/immich-app/immich-server:release}
container_name: ${MEDIA_COMPOSE_PROJECT_NAME}_immich_server
restart: unless-stopped
environment:
TZ: ${TIMEZONE:-Europe/Berlin}
DB_HOSTNAME: ${MEDIA_COMPOSE_PROJECT_NAME}_immich_postgres
DB_PORT: 5432
DB_USERNAME: ${MEDIA_IMMICH_DB_USER:-immich}
DB_PASSWORD: ${MEDIA_IMMICH_DB_PASSWORD}
DB_DATABASE_NAME: ${MEDIA_IMMICH_DB_NAME:-immich}
REDIS_HOSTNAME: ${CORE_COMPOSE_PROJECT_NAME}_redis
REDIS_PORT: ${CORE_REDIS_PORT:-6379}
IMMICH_MACHINE_LEARNING_URL: http://${MEDIA_COMPOSE_PROJECT_NAME}_immich_ml:3003
volumes:
- immich_upload:/usr/src/app/upload
- /etc/localtime:/etc/localtime:ro
depends_on:
immich_postgres:
condition: service_healthy
networks:
- compose_network
labels:
- 'traefik.enable=${MEDIA_TRAEFIK_ENABLED}'
# HTTP to HTTPS redirect
- 'traefik.http.middlewares.${MEDIA_COMPOSE_PROJECT_NAME}-immich-redirect-web-secure.redirectscheme.scheme=https'
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-immich-web.middlewares=${MEDIA_COMPOSE_PROJECT_NAME}-immich-redirect-web-secure'
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-immich-web.rule=Host(`${MEDIA_IMMICH_TRAEFIK_HOST}`)'
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-immich-web.entrypoints=web'
# HTTPS router
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-immich-web-secure.rule=Host(`${MEDIA_IMMICH_TRAEFIK_HOST}`)'
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-immich-web-secure.tls.certresolver=resolver'
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-immich-web-secure.entrypoints=web-secure'
- 'traefik.http.middlewares.${MEDIA_COMPOSE_PROJECT_NAME}-immich-web-secure-compress.compress=true'
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-immich-web-secure.middlewares=${MEDIA_COMPOSE_PROJECT_NAME}-immich-web-secure-compress,security-headers@file'
# Service
- 'traefik.http.services.${MEDIA_COMPOSE_PROJECT_NAME}-immich-web-secure.loadbalancer.server.port=2283'
- 'traefik.docker.network=${NETWORK_NAME}'
# Watchtower
- 'com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}'
# Immich Machine Learning - AI inference for faces, search, etc.
immich_ml:
image: ${MEDIA_IMMICH_ML_IMAGE:-ghcr.io/immich-app/immich-machine-learning:release}
container_name: ${MEDIA_COMPOSE_PROJECT_NAME}_immich_ml
restart: unless-stopped
environment:
TZ: ${TIMEZONE:-Europe/Berlin}
volumes:
- immich_model_cache:/cache
networks:
- compose_network
labels:
# Watchtower
- 'com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}'
volumes: volumes:
jellyfin_config: jellyfin_config:
name: ${MEDIA_COMPOSE_PROJECT_NAME}_jellyfin_config name: ${MEDIA_COMPOSE_PROJECT_NAME}_jellyfin_config
jellyfin_cache: jellyfin_cache:
name: ${MEDIA_COMPOSE_PROJECT_NAME}_jellyfin_cache name: ${MEDIA_COMPOSE_PROJECT_NAME}_jellyfin_cache
filestash_data:
name: ${MEDIA_COMPOSE_PROJECT_NAME}_filestash_data
pinchflat_config: pinchflat_config:
name: ${MEDIA_COMPOSE_PROJECT_NAME}_pinchflat_config name: ${MEDIA_COMPOSE_PROJECT_NAME}_pinchflat_config
immich_postgres_data:
name: ${MEDIA_COMPOSE_PROJECT_NAME}_immich_postgres_data
immich_upload:
name: ${MEDIA_COMPOSE_PROJECT_NAME}_immich_upload
immich_model_cache:
name: ${MEDIA_COMPOSE_PROJECT_NAME}_immich_model_cache
networks: networks:
compose_network: compose_network:

View File

@@ -74,23 +74,26 @@ access_control:
- "admin.asciinema.dev.pivoine.art" - "admin.asciinema.dev.pivoine.art"
- "facefusion.ai.pivoine.art" - "facefusion.ai.pivoine.art"
- "pinchflat.media.pivoine.art" - "pinchflat.media.pivoine.art"
- "comfy.ai.pivoine.art"
- "supervisor.ai.pivoine.art"
- "audiocraft.ai.pivoine.art"
- "upscale.ai.pivoine.art"
policy: one_factor policy: one_factor
# session secret set via environment variable: AUTHELIA_SESSION_SECRET # session secret set via environment variable: AUTHELIA_SESSION_SECRET
session: session:
name: 'authelia_session' name: "authelia_session"
same_site: 'lax' same_site: "lax"
expiration: '1h' expiration: "1h"
inactivity: '5m' inactivity: "15m"
remember_me: '1M' remember_me: "1M"
cookies: cookies:
- domain: 'pivoine.art' - domain: "pivoine.art"
authelia_url: 'https://auth.pivoine.art' authelia_url: "https://auth.pivoine.art"
same_site: 'lax' same_site: "lax"
expiration: '1h' expiration: "1h"
inactivity: '5m' inactivity: "5m"
remember_me: '1M' remember_me: "1M"
regulation: regulation:
max_retries: 3 max_retries: 3

View File

@@ -6,49 +6,49 @@ services:
restart: unless-stopped restart: unless-stopped
command: command:
# API & Dashboard # API & Dashboard
- '--api.dashboard=true' - "--api.dashboard=true"
- '--api.insecure=false' - "--api.insecure=false"
# Ping endpoint for healthcheck # Ping endpoint for healthcheck
- '--ping=true' - "--ping=true"
# Experimental plugins # Experimental plugins
- '--experimental.plugins.sablier.modulename=github.com/acouvreur/sablier' - "--experimental.plugins.sablier.modulename=github.com/acouvreur/sablier"
- '--experimental.plugins.sablier.version=v1.8.0' - "--experimental.plugins.sablier.version=v1.8.0"
# Logging # Logging
- '--log.level=${NET_PROXY_LOG_LEVEL:-INFO}' - "--log.level=${NET_PROXY_LOG_LEVEL:-INFO}"
- '--accesslog=true' - "--accesslog=true"
# Global # Global
- '--global.sendAnonymousUsage=false' - "--global.sendAnonymousUsage=false"
- '--global.checkNewVersion=true' - "--global.checkNewVersion=true"
# Docker Provider # Docker Provider
- '--providers.docker=true' - "--providers.docker=true"
- '--providers.docker.exposedbydefault=false' - "--providers.docker.exposedbydefault=false"
- '--providers.docker.network=${NETWORK_NAME}' - "--providers.docker.network=${NETWORK_NAME}"
# File Provider for dynamic configuration # File Provider for dynamic configuration
- '--providers.file.directory=/etc/traefik/dynamic' - "--providers.file.directory=/etc/traefik/dynamic"
- '--providers.file.watch=true' - "--providers.file.watch=true"
# Entrypoints # Entrypoints
- '--entrypoints.web.address=:${NET_PROXY_PORT_HTTP:-80}' - "--entrypoints.web.address=:${NET_PROXY_PORT_HTTP:-80}"
- '--entrypoints.web-secure.address=:${NET_PROXY_PORT_HTTPS:-443}' - "--entrypoints.web-secure.address=:${NET_PROXY_PORT_HTTPS:-443}"
# Global HTTP to HTTPS redirect # Global HTTP to HTTPS redirect
- '--entrypoints.web.http.redirections.entryPoint.to=web-secure' - "--entrypoints.web.http.redirections.entryPoint.to=web-secure"
- '--entrypoints.web.http.redirections.entryPoint.scheme=https' - "--entrypoints.web.http.redirections.entryPoint.scheme=https"
- '--entrypoints.web.http.redirections.entryPoint.permanent=true' - "--entrypoints.web.http.redirections.entryPoint.permanent=true"
# Security Headers (applied globally) # Security Headers (applied globally)
- '--entrypoints.web-secure.http.middlewares=security-headers@file' - "--entrypoints.web-secure.http.middlewares=security-headers@file"
# Let's Encrypt # Let's Encrypt
- '--certificatesresolvers.resolver.acme.tlschallenge=true' - "--certificatesresolvers.resolver.acme.tlschallenge=true"
- '--certificatesresolvers.resolver.acme.email=${ADMIN_EMAIL}' - "--certificatesresolvers.resolver.acme.email=${ADMIN_EMAIL}"
- '--certificatesresolvers.resolver.acme.storage=/letsencrypt/acme.json' - "--certificatesresolvers.resolver.acme.storage=/letsencrypt/acme.json"
healthcheck: healthcheck:
test: ["CMD", "traefik", "healthcheck", "--ping"] test: ["CMD", "traefik", "healthcheck", "--ping"]
@@ -74,20 +74,20 @@ services:
- ./dynamic:/etc/traefik/dynamic:ro - ./dynamic:/etc/traefik/dynamic:ro
labels: labels:
- 'traefik.enable=true' - "traefik.enable=true"
# HTTP to HTTPS redirect # HTTP to HTTPS redirect
- 'traefik.http.middlewares.${NET_COMPOSE_PROJECT_NAME}-traefik-redirect-web-secure.redirectscheme.scheme=https' - "traefik.http.middlewares.${NET_COMPOSE_PROJECT_NAME}-traefik-redirect-web-secure.redirectscheme.scheme=https"
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-traefik-web.middlewares=${NET_COMPOSE_PROJECT_NAME}-traefik-redirect-web-secure' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-traefik-web.middlewares=${NET_COMPOSE_PROJECT_NAME}-traefik-redirect-web-secure"
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-traefik-web.rule=Host(`${NET_PROXY_TRAEFIK_HOST}`)' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-traefik-web.rule=Host(`${NET_PROXY_TRAEFIK_HOST}`)"
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-traefik-web.entrypoints=web' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-traefik-web.entrypoints=web"
# HTTPS router with auth # HTTPS router with auth
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-traefik-web-secure.rule=Host(`${NET_PROXY_TRAEFIK_HOST}`)' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-traefik-web-secure.rule=Host(`${NET_PROXY_TRAEFIK_HOST}`)"
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-traefik-web-secure.tls.certresolver=resolver' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-traefik-web-secure.tls.certresolver=resolver"
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-traefik-web-secure.entrypoints=web-secure' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-traefik-web-secure.entrypoints=web-secure"
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-traefik-web-secure.service=api@internal' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-traefik-web-secure.service=api@internal"
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-traefik-web-secure.middlewares=${NET_COMPOSE_PROJECT_NAME}-authelia,security-headers@file' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-traefik-web-secure.middlewares=${NET_COMPOSE_PROJECT_NAME}-authelia,security-headers@file"
- 'traefik.http.services.${NET_COMPOSE_PROJECT_NAME}-traefik-web-secure.loadbalancer.server.port=8080' - "traefik.http.services.${NET_COMPOSE_PROJECT_NAME}-traefik-web-secure.loadbalancer.server.port=8080"
- 'traefik.docker.network=${NETWORK_NAME}' - "traefik.docker.network=${NETWORK_NAME}"
# Netdata - Real-time monitoring # Netdata - Real-time monitoring
netdata: netdata:
@@ -128,23 +128,23 @@ services:
networks: networks:
- compose_network - compose_network
labels: labels:
- 'traefik.enable=${NET_TRAEFIK_ENABLED}' - "traefik.enable=${NET_TRAEFIK_ENABLED}"
# HTTP to HTTPS redirect # HTTP to HTTPS redirect
- 'traefik.http.middlewares.${NET_COMPOSE_PROJECT_NAME}-netdata-redirect-web-secure.redirectscheme.scheme=https' - "traefik.http.middlewares.${NET_COMPOSE_PROJECT_NAME}-netdata-redirect-web-secure.redirectscheme.scheme=https"
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-netdata-web.middlewares=${NET_COMPOSE_PROJECT_NAME}-netdata-redirect-web-secure' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-netdata-web.middlewares=${NET_COMPOSE_PROJECT_NAME}-netdata-redirect-web-secure"
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-netdata-web.rule=Host(`${NET_NETDATA_TRAEFIK_HOST}`)' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-netdata-web.rule=Host(`${NET_NETDATA_TRAEFIK_HOST}`)"
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-netdata-web.entrypoints=web' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-netdata-web.entrypoints=web"
# HTTPS router # HTTPS router
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-netdata-web-secure.rule=Host(`${NET_NETDATA_TRAEFIK_HOST}`)' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-netdata-web-secure.rule=Host(`${NET_NETDATA_TRAEFIK_HOST}`)"
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-netdata-web-secure.tls.certresolver=resolver' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-netdata-web-secure.tls.certresolver=resolver"
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-netdata-web-secure.entrypoints=web-secure' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-netdata-web-secure.entrypoints=web-secure"
- 'traefik.http.middlewares.${NET_COMPOSE_PROJECT_NAME}-netdata-compress.compress=true' - "traefik.http.middlewares.${NET_COMPOSE_PROJECT_NAME}-netdata-compress.compress=true"
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-netdata-web-secure.middlewares=${NET_COMPOSE_PROJECT_NAME}-netdata-compress,${NET_COMPOSE_PROJECT_NAME}-authelia,security-headers@file' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-netdata-web-secure.middlewares=${NET_COMPOSE_PROJECT_NAME}-netdata-compress,${NET_COMPOSE_PROJECT_NAME}-authelia,security-headers@file"
# Service # Service
- 'traefik.http.services.${NET_COMPOSE_PROJECT_NAME}-netdata.loadbalancer.server.port=19999' - "traefik.http.services.${NET_COMPOSE_PROJECT_NAME}-netdata.loadbalancer.server.port=19999"
- 'traefik.docker.network=${NETWORK_NAME}' - "traefik.docker.network=${NETWORK_NAME}"
# Watchtower # Watchtower
- 'com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}' - "com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}"
# Watchtower - Automatic container updates # Watchtower - Automatic container updates
watchtower: watchtower:
@@ -202,7 +202,8 @@ services:
- compose_network - compose_network
healthcheck: healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:3000/api/heartbeat || exit 1"] test:
["CMD-SHELL", "curl -f http://localhost:3000/api/heartbeat || exit 1"]
interval: 30s interval: 30s
timeout: 10s timeout: 10s
retries: 5 retries: 5
@@ -210,21 +211,21 @@ services:
labels: labels:
# Traefik Configuration # Traefik Configuration
- 'traefik.enable=${NET_TRAEFIK_ENABLED}' - "traefik.enable=${NET_TRAEFIK_ENABLED}"
# HTTP to HTTPS redirect # HTTP to HTTPS redirect
- 'traefik.http.middlewares.${NET_COMPOSE_PROJECT_NAME}-umami-redirect-web-secure.redirectscheme.scheme=https' - "traefik.http.middlewares.${NET_COMPOSE_PROJECT_NAME}-umami-redirect-web-secure.redirectscheme.scheme=https"
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-umami-web.middlewares=${NET_COMPOSE_PROJECT_NAME}-umami-redirect-web-secure' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-umami-web.middlewares=${NET_COMPOSE_PROJECT_NAME}-umami-redirect-web-secure"
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-umami-web.rule=Host(`${NET_TRACK_TRAEFIK_HOST}`)' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-umami-web.rule=Host(`${NET_TRACK_TRAEFIK_HOST}`)"
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-umami-web.entrypoints=web' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-umami-web.entrypoints=web"
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-umami-web-secure.rule=Host(`${NET_TRACK_TRAEFIK_HOST}`)' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-umami-web-secure.rule=Host(`${NET_TRACK_TRAEFIK_HOST}`)"
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-umami-web-secure.tls.certresolver=resolver' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-umami-web-secure.tls.certresolver=resolver"
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-umami-web-secure.entrypoints=web-secure' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-umami-web-secure.entrypoints=web-secure"
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-umami-web-secure.middlewares=security-headers@file' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-umami-web-secure.middlewares=security-headers@file"
- 'traefik.http.services.${NET_COMPOSE_PROJECT_NAME}-umami-web-secure.loadbalancer.server.port=3000' - "traefik.http.services.${NET_COMPOSE_PROJECT_NAME}-umami-web-secure.loadbalancer.server.port=3000"
- 'traefik.docker.network=${NETWORK_NAME}' - "traefik.docker.network=${NETWORK_NAME}"
# Watchtower # Watchtower
- 'com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}' - "com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}"
# Mailpit - SMTP server with web UI # Mailpit - SMTP server with web UI
mailpit: mailpit:
@@ -250,22 +251,22 @@ services:
networks: networks:
- compose_network - compose_network
labels: labels:
- 'traefik.enable=${NET_TRAEFIK_ENABLED}' - "traefik.enable=${NET_TRAEFIK_ENABLED}"
# HTTP to HTTPS redirect # HTTP to HTTPS redirect
- 'traefik.http.middlewares.${NET_COMPOSE_PROJECT_NAME}-mailpit-redirect-web-secure.redirectscheme.scheme=https' - "traefik.http.middlewares.${NET_COMPOSE_PROJECT_NAME}-mailpit-redirect-web-secure.redirectscheme.scheme=https"
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-mailpit-web.middlewares=${NET_COMPOSE_PROJECT_NAME}-mailpit-redirect-web-secure' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-mailpit-web.middlewares=${NET_COMPOSE_PROJECT_NAME}-mailpit-redirect-web-secure"
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-mailpit-web.rule=Host(`${NET_MAILPIT_TRAEFIK_HOST}`)' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-mailpit-web.rule=Host(`${NET_MAILPIT_TRAEFIK_HOST}`)"
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-mailpit-web.entrypoints=web' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-mailpit-web.entrypoints=web"
# HTTPS router with auth # HTTPS router with auth
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-mailpit-web-secure.rule=Host(`${NET_MAILPIT_TRAEFIK_HOST}`)' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-mailpit-web-secure.rule=Host(`${NET_MAILPIT_TRAEFIK_HOST}`)"
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-mailpit-web-secure.tls.certresolver=resolver' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-mailpit-web-secure.tls.certresolver=resolver"
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-mailpit-web-secure.entrypoints=web-secure' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-mailpit-web-secure.entrypoints=web-secure"
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-mailpit-web-secure.middlewares=${NET_COMPOSE_PROJECT_NAME}-authelia,security-headers@file' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-mailpit-web-secure.middlewares=${NET_COMPOSE_PROJECT_NAME}-authelia,security-headers@file"
# Service # Service
- 'traefik.http.services.${NET_COMPOSE_PROJECT_NAME}-mailpit-web-secure.loadbalancer.server.port=8025' - "traefik.http.services.${NET_COMPOSE_PROJECT_NAME}-mailpit-web-secure.loadbalancer.server.port=8025"
- 'traefik.docker.network=${NETWORK_NAME}' - "traefik.docker.network=${NETWORK_NAME}"
# Watchtower # Watchtower
- 'com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}' - "com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}"
# Authelia - SSO and authentication portal # Authelia - SSO and authentication portal
authelia: authelia:
@@ -285,27 +286,27 @@ services:
networks: networks:
- compose_network - compose_network
labels: labels:
- 'traefik.enable=${NET_TRAEFIK_ENABLED}' - "traefik.enable=${NET_TRAEFIK_ENABLED}"
# HTTP to HTTPS redirect # HTTP to HTTPS redirect
- 'traefik.http.middlewares.${NET_COMPOSE_PROJECT_NAME}-authelia-redirect-web-secure.redirectscheme.scheme=https' - "traefik.http.middlewares.${NET_COMPOSE_PROJECT_NAME}-authelia-redirect-web-secure.redirectscheme.scheme=https"
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-authelia-web.middlewares=${NET_COMPOSE_PROJECT_NAME}-authelia-redirect-web-secure' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-authelia-web.middlewares=${NET_COMPOSE_PROJECT_NAME}-authelia-redirect-web-secure"
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-authelia-web.rule=Host(`${NET_AUTHELIA_TRAEFIK_HOST}`)' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-authelia-web.rule=Host(`${NET_AUTHELIA_TRAEFIK_HOST}`)"
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-authelia-web.entrypoints=web' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-authelia-web.entrypoints=web"
# HTTPS router # HTTPS router
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-authelia-web-secure.rule=Host(`${NET_AUTHELIA_TRAEFIK_HOST}`)' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-authelia-web-secure.rule=Host(`${NET_AUTHELIA_TRAEFIK_HOST}`)"
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-authelia-web-secure.tls.certresolver=resolver' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-authelia-web-secure.tls.certresolver=resolver"
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-authelia-web-secure.entrypoints=web-secure' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-authelia-web-secure.entrypoints=web-secure"
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-authelia-web-secure.middlewares=security-headers@file' - "traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-authelia-web-secure.middlewares=security-headers@file"
# Service # Service
- 'traefik.http.services.${NET_COMPOSE_PROJECT_NAME}-authelia-web-secure.loadbalancer.server.port=9091' - "traefik.http.services.${NET_COMPOSE_PROJECT_NAME}-authelia-web-secure.loadbalancer.server.port=9091"
- 'traefik.docker.network=${NETWORK_NAME}' - "traefik.docker.network=${NETWORK_NAME}"
# ForwardAuth middleware for other services # ForwardAuth middleware for other services
- 'traefik.http.middlewares.${NET_COMPOSE_PROJECT_NAME}-authelia.forwardAuth.address=http://net_authelia:9091/api/authz/forward-auth' - "traefik.http.middlewares.${NET_COMPOSE_PROJECT_NAME}-authelia.forwardAuth.address=http://net_authelia:9091/api/authz/forward-auth"
- 'traefik.http.middlewares.${NET_COMPOSE_PROJECT_NAME}-authelia.forwardAuth.trustForwardHeader=true' - "traefik.http.middlewares.${NET_COMPOSE_PROJECT_NAME}-authelia.forwardAuth.trustForwardHeader=true"
- 'traefik.http.middlewares.${NET_COMPOSE_PROJECT_NAME}-authelia.forwardAuth.authResponseHeaders=Remote-User,Remote-Groups,Remote-Name,Remote-Email' - "traefik.http.middlewares.${NET_COMPOSE_PROJECT_NAME}-authelia.forwardAuth.authResponseHeaders=Remote-User,Remote-Groups,Remote-Name,Remote-Email"
- 'traefik.http.middlewares.${NET_COMPOSE_PROJECT_NAME}-authelia.forwardAuth.authResponseHeadersRegex=^Remote-' - "traefik.http.middlewares.${NET_COMPOSE_PROJECT_NAME}-authelia.forwardAuth.authResponseHeadersRegex=^Remote-"
# Watchtower # Watchtower
- 'com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}' - "com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}"
volumes: volumes:
letsencrypt_data: letsencrypt_data: