feat(stack): add Appwrite BaaS stack for dev/testing
Self-contained stack with 25 containers: API server, console, realtime WebSocket server, 10 workers, 3 schedulers, maintenance task, browser, OpenRuntimes executor, plus internal MariaDB 10.11 and Redis 7.4. Exposed on port 8093 via bundled Traefik. Watchtower disabled for deliberate upgrades. Also includes media.yml reformatting and Jellyfin GPU deploy block. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,670 @@
|
||||
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
|
||||
+187
-166
@@ -1,4 +1,4 @@
|
||||
version: '3.8'
|
||||
version: "3.8"
|
||||
|
||||
# Media Stack - Unified Media Management & Streaming
|
||||
# Services: Jellyfin, Sonarr, Radarr, Prowlarr, SABnzbd
|
||||
@@ -8,177 +8,198 @@ version: '3.8'
|
||||
# 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
|
||||
networks:
|
||||
- docker-dataplane
|
||||
# ============================================================
|
||||
# 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
|
||||
# ============================================================
|
||||
# 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
|
||||
# ============================================================
|
||||
# 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
|
||||
# ============================================================
|
||||
# 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
|
||||
# ============================================================
|
||||
# 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
|
||||
docker-dataplane:
|
||||
external: true
|
||||
name: docker-dataplane
|
||||
|
||||
# ============================================================
|
||||
# SETUP GUIDE
|
||||
|
||||
Reference in New Issue
Block a user