chore(stacks): remove the deprecated stack copies
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 <noreply@anthropic.com>
This commit is contained in:
+23
-132
@@ -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 <commit>:stacks/<name>.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/<service>/config`
|
||||
- Cache: `/home/jpmschweitzer/docker-data/<service>/cache`
|
||||
- Databases: `/home/jpmschweitzer/docker-data/<service>/db`
|
||||
|
||||
**HDD (Capacity):**
|
||||
- User content: `/mnt/media/<service>/data`
|
||||
- Media files: `/mnt/media/<service>/media`
|
||||
- Backups: `/mnt/media/backups/<service>`
|
||||
|
||||
## 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 <container-name>`
|
||||
3. **Verify GPU** - `docker exec <container> 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/<stack-name>.yml pull
|
||||
|
||||
# Recreate containers with new images
|
||||
docker compose -f stacks/<stack-name>.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 <container-name>`
|
||||
- Port conflicts: `sudo netstat -tulpn | grep <port>`
|
||||
- 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`).
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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: <secure-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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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=<key>
|
||||
#
|
||||
# Verify:
|
||||
# - Health check: curl http://localhost:8085/health
|
||||
# - List users: docker exec headscale headscale users list
|
||||
# - List nodes: docker exec headscale headscale nodes list
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 '<MEDIA_DB_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: <from SABnzbd>
|
||||
# - 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: <from SABnzbd>
|
||||
# - 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
|
||||
@@ -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 && <generate> && 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
|
||||
@@ -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 / <NEO4J_PASSWORD>
|
||||
# 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/
|
||||
@@ -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=<password from shared infrastructure setup>
|
||||
#
|
||||
# 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: <STRONG_PASSWORD>
|
||||
# - Data folder: /var/www/html/data (default)
|
||||
# - Database: PostgreSQL
|
||||
# - Database user: nextcloud_user
|
||||
# - Database password: <FROM_ENV_FILE>
|
||||
# - 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)
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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=<openssl rand -hex 32>
|
||||
# PAPERLESS_SECRET_KEY=<openssl rand -base64 32>
|
||||
# PAPERLESS_ADMIN_PASSWORD=<your-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
|
||||
@@ -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=<from-authentik>
|
||||
# PENPOT_OIDC_CLIENT_SECRET=<from-authentik>
|
||||
#
|
||||
# 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==
|
||||
@@ -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
|
||||
@@ -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: <app-specific-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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user