toppyr-stack/docker-compose.yml

916 lines
30 KiB
YAML
Raw Permalink Normal View History

# =============================================================================
# Collaboration Stack - PRODUCTION
# =============================================================================
# RAM-Limits nach Empfehlung gesetzt (23.03.2026)
# Gesamt-RAM-Bedarf: ~13-14 GB (nach Entwickler-Empfehlungen)
# =============================================================================
services:
# =============================================================================
# TRAEFIK - Reverse Proxy mit Let's Encrypt
# =============================================================================
traefik:
image: traefik:v2.11
container_name: traefik
deploy:
resources:
limits:
memory: 64m
command:
- "--api.dashboard=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
- "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
- "--certificatesresolvers.letsencrypt.acme.email=${ACME_EMAIL}"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
- "--log.level=INFO"
- "--entrypoints.siptls.address=:5061"
ports:
- "80:80"
- "443:443"
- "5061:5061/tcp"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- traefik_letsencrypt:/letsencrypt
networks:
- collaboration
restart: unless-stopped
labels:
- "traefik.enable=true"
- "traefik.http.routers.traefik.rule=Host(`traefik.${DOMAIN}`)"
- "traefik.http.routers.traefik.entrypoints=websecure"
- "traefik.http.routers.traefik.tls.certresolver=letsencrypt"
- "traefik.http.routers.traefik.service=api@internal"
- "traefik.http.routers.traefik.middlewares=traefik-auth"
- "traefik.http.middlewares.traefik-auth.basicauth.users=${TRAEFIK_AUTH}"
# =============================================================================
# KEYCLOAK - Identity Provider (SSO)
# =============================================================================
keycloak-db:
image: postgres:16-alpine
container_name: keycloak-db
deploy:
resources:
limits:
memory: 256m
environment:
POSTGRES_DB: keycloak
POSTGRES_USER: keycloak
POSTGRES_PASSWORD: ${KEYCLOAK_DB_PASSWORD}
volumes:
- keycloak_db_data:/var/lib/postgresql/data
networks:
- collaboration
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U keycloak"]
interval: 10s
timeout: 5s
retries: 5
keycloak:
image: quay.io/keycloak/keycloak:24.0
container_name: keycloak
deploy:
resources:
limits:
memory: 2g
command: start
environment:
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://keycloak-db:5432/keycloak
KC_DB_USERNAME: keycloak
KC_DB_PASSWORD: ${KEYCLOAK_DB_PASSWORD}
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD}
KC_HOSTNAME: sso.${DOMAIN}
KC_PROXY: edge
KC_HTTP_ENABLED: "true"
depends_on:
keycloak-db:
condition: service_healthy
labels:
- "traefik.enable=true"
- "traefik.http.routers.keycloak.rule=Host(`sso.${DOMAIN}`)"
- "traefik.http.routers.keycloak.entrypoints=websecure"
- "traefik.http.routers.keycloak.tls.certresolver=letsencrypt"
- "traefik.http.services.keycloak.loadbalancer.server.port=8080"
networks:
- collaboration
restart: unless-stopped
# =============================================================================
# OPENPROJECT - Projektmanagement
# =============================================================================
openproject:
image: openproject/openproject:14
container_name: openproject
deploy:
resources:
limits:
memory: 4g
environment:
OPENPROJECT_SECRET_KEY_BASE: ${OPENPROJECT_SECRET}
OPENPROJECT_HOST__NAME: project.${DOMAIN}
OPENPROJECT_HTTPS: "true"
OPENPROJECT_DEFAULT__LANGUAGE: de
OPENPROJECT_SEED_ADMIN_USER_PASSWORD: ${OPENPROJECT_ADMIN_PASSWORD}
OPENPROJECT_OPENID__CONNECT_KEYCLOAK_DISPLAY__NAME: "SSO Login"
OPENPROJECT_OPENID__CONNECT_KEYCLOAK_HOST: "sso.${DOMAIN}"
OPENPROJECT_OPENID__CONNECT_KEYCLOAK_IDENTIFIER: "openproject"
OPENPROJECT_OPENID__CONNECT_KEYCLOAK_SECRET: ${OIDC_CLIENT_SECRET}
OPENPROJECT_OPENID__CONNECT_KEYCLOAK_ISSUER: "https://sso.${DOMAIN}/realms/collaboration"
OPENPROJECT_OPENID__CONNECT_KEYCLOAK_AUTHORIZATION__ENDPOINT: "https://sso.${DOMAIN}/realms/collaboration/protocol/openid-connect/auth"
OPENPROJECT_OPENID__CONNECT_KEYCLOAK_TOKEN__ENDPOINT: "https://sso.${DOMAIN}/realms/collaboration/protocol/openid-connect/token"
OPENPROJECT_OPENID__CONNECT_KEYCLOAK_USERINFO__ENDPOINT: "https://sso.${DOMAIN}/realms/collaboration/protocol/openid-connect/userinfo"
volumes:
- openproject_data:/var/openproject/assets
- openproject_pgdata:/var/openproject/pgdata
labels:
- "traefik.enable=true"
- "traefik.http.routers.openproject.rule=Host(`project.${DOMAIN}`)"
- "traefik.http.routers.openproject.entrypoints=websecure"
- "traefik.http.routers.openproject.tls.certresolver=letsencrypt"
- "traefik.http.services.openproject.loadbalancer.server.port=80"
networks:
- collaboration
restart: unless-stopped
# =============================================================================
# XWIKI - Wiki
# =============================================================================
xwiki-db:
image: mysql:8.0
container_name: xwiki-db
deploy:
resources:
limits:
memory: 1g
environment:
MYSQL_ROOT_PASSWORD: ${XWIKI_DB_ROOT_PASSWORD}
MYSQL_DATABASE: xwiki
MYSQL_USER: xwiki
MYSQL_PASSWORD: ${XWIKI_DB_PASSWORD}
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
volumes:
- xwiki_db_data:/var/lib/mysql
networks:
- collaboration
restart: unless-stopped
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
xwiki:
image: xwiki:16-mysql-tomcat
container_name: xwiki
deploy:
resources:
limits:
memory: 4g
environment:
DB_HOST: xwiki-db
DB_USER: xwiki
DB_PASSWORD: ${XWIKI_DB_PASSWORD}
DB_DATABASE: xwiki
JAVA_OPTS: "-Xmx2g -Xms1g"
volumes:
- xwiki_data:/usr/local/xwiki
depends_on:
xwiki-db:
condition: service_healthy
labels:
- "traefik.enable=true"
- "traefik.http.routers.xwiki.rule=Host(`wiki.${DOMAIN}`)"
- "traefik.http.routers.xwiki.entrypoints=websecure"
- "traefik.http.routers.xwiki.tls.certresolver=letsencrypt"
- "traefik.http.services.xwiki.loadbalancer.server.port=8080"
networks:
- collaboration
restart: unless-stopped
# =============================================================================
# NEXTCLOUD - Dateiverwaltung
# =============================================================================
nextcloud-db:
image: postgres:16-alpine
container_name: nextcloud-db
deploy:
resources:
limits:
memory: 256m
environment:
POSTGRES_DB: nextcloud
POSTGRES_USER: nextcloud
POSTGRES_PASSWORD: ${NEXTCLOUD_DB_PASSWORD}
volumes:
- nextcloud_db_data:/var/lib/postgresql/data
networks:
- collaboration
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U nextcloud"]
interval: 10s
timeout: 5s
retries: 5
nextcloud-redis:
image: redis:7-alpine
container_name: nextcloud-redis
deploy:
resources:
limits:
memory: 64m
networks:
- collaboration
restart: unless-stopped
nextcloud:
image: nextcloud:29-apache
container_name: nextcloud
deploy:
resources:
limits:
memory: 512m
environment:
POSTGRES_HOST: nextcloud-db
POSTGRES_DB: nextcloud
POSTGRES_USER: nextcloud
POSTGRES_PASSWORD: ${NEXTCLOUD_DB_PASSWORD}
REDIS_HOST: nextcloud-redis
NEXTCLOUD_ADMIN_USER: admin
NEXTCLOUD_ADMIN_PASSWORD: ${NEXTCLOUD_ADMIN_PASSWORD}
NEXTCLOUD_TRUSTED_DOMAINS: "cloud.${DOMAIN}"
OVERWRITEPROTOCOL: https
OVERWRITEHOST: cloud.${DOMAIN}
volumes:
- nextcloud_data:/var/www/html
depends_on:
nextcloud-db:
condition: service_healthy
nextcloud-redis:
condition: service_started
labels:
- "traefik.enable=true"
- "traefik.http.routers.nextcloud.rule=Host(`cloud.${DOMAIN}`)"
- "traefik.http.routers.nextcloud.entrypoints=websecure"
- "traefik.http.routers.nextcloud.tls.certresolver=letsencrypt"
- "traefik.http.services.nextcloud.loadbalancer.server.port=80"
- "traefik.http.routers.nextcloud.middlewares=nextcloud-caldav"
- "traefik.http.middlewares.nextcloud-caldav.redirectregex.permanent=true"
- "traefik.http.middlewares.nextcloud-caldav.redirectregex.regex=^https://(.*)/.well-known/(card|cal)dav"
- "traefik.http.middlewares.nextcloud-caldav.redirectregex.replacement=https://$${1}/remote.php/dav/"
networks:
- collaboration
restart: unless-stopped
# =============================================================================
# LLDAP - Lightweight LDAP Server
# =============================================================================
lldap:
image: lldap/lldap:stable
container_name: lldap
deploy:
resources:
limits:
memory: 128m
environment:
- UID=1000
- GID=1000
- TZ=Europe/Berlin
- LLDAP_JWT_SECRET=ZldD2dpHj3RssRqBwdgAVmjeSu7UfRamg7PCsrl0iw8=
- LLDAP_LDAP_USER_PASS=coHfqoruajDrXzeze6qA
- LLDAP_LDAP_BASE_DN=dc=toppyr,dc=de
volumes:
- lldap_data:/data
networks:
- collaboration
labels:
- "traefik.enable=true"
- "traefik.http.routers.lldap.rule=Host(`ldap.toppyr.de`)"
- "traefik.http.routers.lldap.entrypoints=websecure"
- "traefik.http.routers.lldap.tls.certresolver=letsencrypt"
- "traefik.http.services.lldap.loadbalancer.server.port=17170"
restart: unless-stopped
# =============================================================================
# POSTIZ - Social Media Scheduler
# =============================================================================
postiz:
image: ghcr.io/gitroomhq/postiz-app:latest
container_name: postiz
deploy:
resources:
limits:
memory: 4g
restart: unless-stopped
environment:
MAIN_URL: "https://postiz.toppyr.de"
FRONTEND_URL: "https://postiz.toppyr.de"
NEXT_PUBLIC_BACKEND_URL: "https://postiz.toppyr.de/api"
JWT_SECRET: "Xk9mL2pQnRs5tUvWxYz1A3bC4dEf6gHi"
DATABASE_URL: "postgresql://postiz:postiz-secret@postiz-db:5432/postiz"
REDIS_URL: "redis://postiz-redis:6379"
BACKEND_INTERNAL_URL: "http://127.0.0.1:3000"
IS_GENERAL: "true"
LINKEDIN_CLIENT_ID: "78tv4ijhgx8c1e"
LINKEDIN_CLIENT_SECRET: "WPL_AP1.C0UAsznfDj7xWWfs.SDHO8g=="
THREADS_APP_ID: "1641922170163889"
THREADS_APP_SECRET: "dea32c2064e54bf3d58b70e51a330ef9"
MASTODON_CLIENT_ID: "2XmVi5O3Bmv8NRwKiJ6Yi4osjSezLUoRce0zBr2xlAQ"
MASTODON_CLIENT_SECRET: "foi-oiFSzVXvPsMHzsj0LUsR8qZe7VxN5uI-VKzR6IQ"
MASTODON_URL: "https://gruene.social"
DISABLE_REGISTRATION: "true"
STORAGE_PROVIDER: "local"
UPLOAD_DIRECTORY: "/uploads"
POSTIZ_GENERIC_OAUTH: "true"
NEXT_PUBLIC_POSTIZ_OAUTH_DISPLAY_NAME: "Toppyr SSO"
POSTIZ_OAUTH_URL: "https://sso.toppyr.de"
POSTIZ_OAUTH_AUTH_URL: "https://sso.toppyr.de/realms/collaboration/protocol/openid-connect/auth"
POSTIZ_OAUTH_TOKEN_URL: "https://sso.toppyr.de/realms/collaboration/protocol/openid-connect/token"
POSTIZ_OAUTH_USERINFO_URL: "https://sso.toppyr.de/realms/collaboration/protocol/openid-connect/userinfo"
POSTIZ_OAUTH_CLIENT_ID: "postiz"
POSTIZ_OAUTH_CLIENT_SECRET: "xU7jKdZVrHHfp4VTligJKURgm7OXzkud"
TEMPORAL_ADDRESS: "temporal:7233"
NEXT_PUBLIC_UPLOAD_DIRECTORY: "/uploads"
volumes:
- postiz_config:/config
- postiz_uploads:/uploads
labels:
- "traefik.enable=true"
- "traefik.http.routers.postiz.rule=Host(`postiz.toppyr.de`)"
- "traefik.http.routers.postiz.entrypoints=websecure"
- "traefik.http.routers.postiz.tls.certresolver=letsencrypt"
- "traefik.http.services.postiz.loadbalancer.server.port=5000"
networks:
- collaboration
depends_on:
postiz-db:
condition: service_healthy
postiz-redis:
condition: service_healthy
postiz-db:
image: postgres:17-alpine
container_name: postiz-db
deploy:
resources:
limits:
memory: 256m
restart: unless-stopped
environment:
POSTGRES_DB: postiz
POSTGRES_USER: postiz
POSTGRES_PASSWORD: postiz-secret
volumes:
- postiz_db:/var/lib/postgresql/data
networks:
- collaboration
healthcheck:
test: ["CMD", "pg_isready", "-U", "postiz"]
interval: 10s
timeout: 5s
retries: 5
postiz-redis:
image: redis:7.2-alpine
container_name: postiz-redis
deploy:
resources:
limits:
memory: 64m
restart: unless-stopped
volumes:
- postiz_redis:/data
networks:
- collaboration
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
# =============================================================================
# LANDING PAGE
# =============================================================================
landing:
image: nginx:alpine
container_name: landing
deploy:
resources:
limits:
memory: 32m
restart: unless-stopped
volumes:
- /opt/collaboration/landing:/usr/share/nginx/html:ro
labels:
- "traefik.enable=true"
- "traefik.http.routers.landing.rule=Host(`toppyr.de`) || Host(`www.toppyr.de`)"
- "traefik.http.routers.landing.entrypoints=websecure"
- "traefik.http.routers.landing.tls.certresolver=letsencrypt"
- "traefik.http.services.landing.loadbalancer.server.port=80"
networks:
- collaboration
# =============================================================================
# GRÜNE ANTRAEGE - Antragstools
# =============================================================================
gruene-antraege:
image: nginx:alpine
container_name: gruene-antraege
deploy:
resources:
limits:
memory: 32m
restart: unless-stopped
volumes:
- /opt/collaboration/gruene-antraege:/usr/share/nginx/html:ro
labels:
- "traefik.enable=true"
- "traefik.http.routers.gruene.middlewares=sso-auth@docker"
- "traefik.http.routers.gruene.rule=Host(`gruen.toppyr.de`)"
- "traefik.http.routers.gruene.entrypoints=websecure"
- "traefik.http.routers.gruene.tls.certresolver=letsencrypt"
- "traefik.http.services.gruene.loadbalancer.server.port=80"
networks:
- collaboration
# =============================================================================
# TEMPORAL STACK - Workflow Engine for Postiz
# =============================================================================
temporal-elasticsearch:
container_name: temporal-elasticsearch
image: elasticsearch:7.17.27
deploy:
resources:
limits:
memory: 1g
restart: unless-stopped
environment:
- cluster.routing.allocation.disk.threshold_enabled=true
- cluster.routing.allocation.disk.watermark.low=512mb
- cluster.routing.allocation.disk.watermark.high=256mb
- cluster.routing.allocation.disk.watermark.flood_stage=128mb
- discovery.type=single-node
- ES_JAVA_OPTS=-Xms512m -Xmx512m
- xpack.security.enabled=false
networks:
- collaboration
expose:
- 9200
volumes:
- temporal_es_data:/usr/share/elasticsearch/data
temporal-postgresql:
container_name: temporal-postgresql
image: postgres:16
deploy:
resources:
limits:
memory: 256m
restart: unless-stopped
environment:
POSTGRES_PASSWORD: temporal
POSTGRES_USER: temporal
networks:
- collaboration
expose:
- 5432
volumes:
- temporal_pg_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "pg_isready", "-U", "temporal"]
interval: 10s
timeout: 5s
retries: 5
temporal:
container_name: temporal
image: temporalio/auto-setup:1.28.1
deploy:
resources:
limits:
memory: 256m
restart: unless-stopped
depends_on:
temporal-postgresql:
condition: service_healthy
temporal-elasticsearch:
condition: service_started
environment:
- DB=postgres12
- DB_PORT=5432
- POSTGRES_USER=temporal
- POSTGRES_PWD=temporal
- POSTGRES_SEEDS=temporal-postgresql
- ENABLE_ES=true
- ES_SEEDS=temporal-elasticsearch
- ES_VERSION=v7
- TEMPORAL_NAMESPACE=default
networks:
- collaboration
expose:
- 7233
# =============================================================================
# TRAEFIK FORWARD AUTH (Keycloak SSO)
# =============================================================================
forward-auth:
image: thomseddon/traefik-forward-auth:2
container_name: forward-auth
deploy:
resources:
limits:
memory: 64m
restart: unless-stopped
environment:
PROVIDERS_OIDC_ISSUER_URL: "https://sso.toppyr.de/realms/collaboration"
PROVIDERS_OIDC_CLIENT_ID: "gruene-antraege"
PROVIDERS_OIDC_CLIENT_SECRET: "gruene-antraege-secret-2026"
SECRET: "forward-auth-secret-random-string-2026"
DEFAULT_PROVIDER: "oidc"
LOG_LEVEL: "debug"
COOKIE_DOMAIN: "toppyr.de"
URL_PATH: "/_oauth"
labels:
- "traefik.enable=true"
- "traefik.http.middlewares.sso-auth.forwardauth.address=http://forward-auth:4181"
- "traefik.http.middlewares.sso-auth.forwardauth.authResponseHeaders=X-Forwarded-User"
- "traefik.http.middlewares.sso-auth.forwardauth.trustForwardHeader=true"
- "traefik.http.services.forward-auth.loadbalancer.server.port=4181"
# =============================================================================
# =============================================================================
# ESPOCRM - CRM System
# =============================================================================
espocrm-db:
image: mariadb:10.11
container_name: espocrm-db
deploy:
resources:
limits:
memory: 512m
environment:
MARIADB_ROOT_PASSWORD: ${ESPOCRM_DB_PASSWORD}
MARIADB_DATABASE: espocrm
MARIADB_USER: espocrm
MARIADB_PASSWORD: ${ESPOCRM_DB_PASSWORD}
volumes:
- espocrm_db_data:/var/lib/mysql
networks:
- collaboration
restart: unless-stopped
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
espocrm:
image: espocrm/espocrm:latest
container_name: espocrm
deploy:
resources:
limits:
memory: 512m
environment:
ESPOCRM_DATABASE_HOST: espocrm-db
ESPOCRM_DATABASE_NAME: espocrm
ESPOCRM_DATABASE_USER: espocrm
ESPOCRM_DATABASE_PASSWORD: ${ESPOCRM_DB_PASSWORD}
ESPOCRM_ADMIN_USERNAME: admin
ESPOCRM_ADMIN_PASSWORD: ${ESPOCRM_ADMIN_PASSWORD}
ESPOCRM_SITE_URL: https://crm.toppyr.de
volumes:
- espocrm_data:/var/www/html
depends_on:
espocrm-db:
condition: service_healthy
labels:
- "traefik.enable=true"
- "traefik.http.routers.espocrm.rule=Host(`crm.toppyr.de`)"
- "traefik.http.routers.espocrm.entrypoints=websecure"
- "traefik.http.routers.espocrm.tls.certresolver=letsencrypt"
- "traefik.http.services.espocrm.loadbalancer.server.port=80"
networks:
- collaboration
restart: unless-stopped
# MAUTIC - Marketing Automation
# =============================================================================
mautic-db:
image: mariadb:10.11
container_name: mautic-db
deploy:
resources:
limits:
memory: 512m
environment:
MARIADB_ROOT_PASSWORD: ${MAUTIC_DB_PASSWORD}
MARIADB_DATABASE: mautic
MARIADB_USER: mautic
MARIADB_PASSWORD: ${MAUTIC_DB_PASSWORD}
volumes:
- mautic_db_data:/var/lib/mysql
networks:
- collaboration
restart: unless-stopped
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
mautic:
image: mautic/mautic:5-apache
container_name: mautic
deploy:
resources:
limits:
memory: 1g
environment:
MAUTIC_DB_HOST: mautic-db
MAUTIC_DB_PORT: 3306
MAUTIC_DB_NAME: mautic
MAUTIC_DB_USER: mautic
MAUTIC_DB_PASSWORD: ${MAUTIC_DB_PASSWORD}
MAUTIC_RUN_CRON_JOBS: "true"
MAUTIC_TRUSTED_PROXIES: "[\"0.0.0.0/0\"]"
volumes:
- mautic_data:/var/www/html
depends_on:
mautic-db:
condition: service_healthy
labels:
- "traefik.enable=true"
#- "traefik.http.routers.mautic.middlewares=sso-auth@docker" # Disabled for native SAML
- "traefik.http.routers.mautic.rule=Host(`marketing.toppyr.de`)"
- "traefik.http.routers.mautic.entrypoints=websecure"
- "traefik.http.routers.mautic.tls.certresolver=letsencrypt"
- "traefik.http.services.mautic.loadbalancer.server.port=80"
networks:
- collaboration
restart: unless-stopped
# =============================================================================
# FREESCOUT - Helpdesk / Ticketsystem
# =============================================================================
freescout-db:
image: mariadb:10.11
container_name: freescout-db
deploy:
resources:
limits:
memory: 256m
environment:
MARIADB_ROOT_PASSWORD: ${FREESCOUT_DB_PASSWORD}
MARIADB_DATABASE: freescout
MARIADB_USER: freescout
MARIADB_PASSWORD: ${FREESCOUT_DB_PASSWORD}
volumes:
- freescout_db_data:/var/lib/mysql
networks:
- collaboration
restart: unless-stopped
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
freescout:
image: tiredofit/freescout:latest
container_name: freescout
deploy:
resources:
limits:
memory: 512m
environment:
CONTAINER_NAME: freescout
DB_HOST: freescout-db
DB_NAME: freescout
DB_USER: freescout
DB_PASS: ${FREESCOUT_DB_PASSWORD}
SITE_URL: https://support.toppyr.de
ADMIN_EMAIL: mail@tobiasroedel.de
ADMIN_PASS: ${FREESCOUT_ADMIN_PASSWORD}
TIMEZONE: Europe/Berlin
ENABLE_SSL_PROXY: "TRUE"
volumes:
- freescout_data:/data
depends_on:
freescout-db:
condition: service_healthy
labels:
- "traefik.enable=true"
- "traefik.http.routers.freescout.rule=Host(`support.toppyr.de`)"
- "traefik.http.routers.freescout.entrypoints=websecure"
- "traefik.http.routers.freescout.tls.certresolver=letsencrypt"
- "traefik.http.services.freescout.loadbalancer.server.port=80"
networks:
- collaboration
restart: unless-stopped
# =============================================================================
# UPTIME KUMA - Monitoring
# =============================================================================
# =============================================================================
# NETDATA PROXY (External service on host)
# =============================================================================
netdata-proxy:
image: alpine/socat
container_name: netdata-proxy
restart: unless-stopped
command: TCP-LISTEN:19999,fork,reuseaddr TCP:host.docker.internal:19999
extra_hosts:
- "host.docker.internal:host-gateway"
labels:
- "traefik.enable=true"
- "traefik.http.routers.netdata.rule=Host(`netdata.toppyr.de`)"
- "traefik.http.routers.netdata.entrypoints=websecure"
- "traefik.http.routers.netdata.tls.certresolver=letsencrypt"
- "traefik.http.services.netdata.loadbalancer.server.port=19999"
- "traefik.http.routers.netdata.middlewares=sso-auth@docker"
networks:
- collaboration
# =============================================================================
# MONITORING DASHBOARD (Static HTML)
# =============================================================================
monitoring-dashboard:
image: nginx:alpine
container_name: monitoring-dashboard
restart: unless-stopped
volumes:
- /var/www/monitoring:/usr/share/nginx/html:ro
labels:
- "traefik.enable=true"
- "traefik.http.routers.monitoring.priority=100"
- "traefik.http.routers.monitoring.rule=Host(`toppyr.de`) || Host(`www.toppyr.de`)"
- "traefik.http.routers.monitoring.entrypoints=websecure"
- "traefik.http.routers.monitoring.tls.certresolver=letsencrypt"
- "traefik.http.routers.monitoring.middlewares="
- "traefik.http.services.monitoring.loadbalancer.server.port=80"
networks:
- collaboration
uptime-kuma:
image: louislam/uptime-kuma:1
container_name: uptime-kuma
deploy:
resources:
limits:
memory: 256m
restart: unless-stopped
volumes:
- uptime_kuma_data:/app/data
- /var/run/docker.sock:/var/run/docker.sock:ro
labels:
- "traefik.enable=true"
- "traefik.http.routers.uptime.rule=Host(`status.toppyr.de`)"
- "traefik.http.routers.uptime.entrypoints=websecure"
- "traefik.http.routers.uptime.tls.certresolver=letsencrypt"
- "traefik.http.services.uptime.loadbalancer.server.port=3001"
networks:
- collaboration
# =============================================================================
# GITEA - Git Repository Server
# =============================================================================
gitea-db:
image: postgres:16-alpine
container_name: gitea-db
deploy:
resources:
limits:
memory: 256m
environment:
POSTGRES_DB: gitea
POSTGRES_USER: gitea
POSTGRES_PASSWORD: ${GITEA_DB_PASSWORD}
volumes:
- gitea_db_data:/var/lib/postgresql/data
networks:
- collaboration
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U gitea"]
interval: 10s
timeout: 5s
retries: 5
gitea:
image: gitea/gitea:1.22
container_name: gitea
deploy:
resources:
limits:
memory: 512m
environment:
- USER_UID=1000
- USER_GID=1000
- GITEA__database__DB_TYPE=postgres
- GITEA__database__HOST=gitea-db:5432
- GITEA__database__NAME=gitea
- GITEA__database__USER=gitea
- GITEA__database__PASSWD=${GITEA_DB_PASSWORD}
- GITEA__server__DOMAIN=repo.${DOMAIN}
- GITEA__server__SSH_DOMAIN=repo.${DOMAIN}
- GITEA__server__ROOT_URL=https://repo.${DOMAIN}/
- GITEA__server__SSH_PORT=2222
- GITEA__server__SSH_LISTEN_PORT=22
- GITEA__service__DISABLE_REGISTRATION=true
- GITEA__openid__ENABLE_OPENID_SIGNIN=false
- GITEA__oauth2_client__ENABLE_AUTO_REGISTRATION=true
- GITEA__oauth2_client__USERNAME=nickname
volumes:
- gitea_data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "2222:22"
depends_on:
gitea-db:
condition: service_healthy
labels:
- "traefik.enable=true"
- "traefik.http.routers.gitea.rule=Host(`repo.${DOMAIN}`)"
- "traefik.http.routers.gitea.entrypoints=websecure"
- "traefik.http.routers.gitea.tls.certresolver=letsencrypt"
- "traefik.http.services.gitea.loadbalancer.server.port=3000"
networks:
- collaboration
restart: unless-stopped
# =============================================================================
# WEBHOOK HANDLER - Gitea → OpenProject Integration
# =============================================================================
webhook-handler:
image: python:3.12-alpine
container_name: webhook-handler
deploy:
resources:
limits:
memory: 64m
environment:
- OPENPROJECT_URL=https://project.${DOMAIN}
- OPENPROJECT_API_KEY=${OPENPROJECT_API_KEY:-}
- GITEA_URL=https://repo.${DOMAIN}
- PORT=9000
volumes:
- /opt/collaboration/webhooks:/app:ro
working_dir: /app
command: python gitea-openproject-webhook.py
networks:
- collaboration
restart: unless-stopped
# =============================================================================
# KAMAILIO - SIP Proxy (Dotty Phone)
# =============================================================================
# TCP-only Proxy, TLS-Terminierung via Traefik auf Port 5061.
# Zwei SIP-Accounts: tobias (Linphone iOS) und dotty (Mac Mini Asterisk).
# Siehe: dotty-phone/README.md
# =============================================================================
kamailio:
image: kamailio/kamailio-ci:5.5.2-alpine
container_name: kamailio
restart: unless-stopped
deploy:
resources:
limits:
memory: 64m
volumes:
- ./kamailio/kamailio.cfg:/etc/kamailio/kamailio.cfg:ro
- ./kamailio/kamailio-local.cfg:/etc/kamailio/kamailio-local.cfg:ro
networks:
- collaboration
labels:
- "traefik.enable=true"
- "traefik.tcp.routers.sip.rule=HostSNI(`sip.toppyr.de`)"
- "traefik.tcp.routers.sip.entrypoints=siptls"
- "traefik.tcp.routers.sip.tls=true"
- "traefik.tcp.routers.sip.tls.certresolver=letsencrypt"
- "traefik.tcp.services.sip.loadbalancer.server.port=5060"
volumes:
gitea_db_data:
gitea_data:
traefik_letsencrypt:
traefik_certs:
keycloak_db_data:
openproject_data:
openproject_pgdata:
xwiki_db_data:
xwiki_data:
nextcloud_db_data:
nextcloud_data:
lldap_data:
postiz_config:
postiz_uploads:
postiz_db:
postiz_redis:
temporal_es_data:
temporal_pg_data:
espocrm_db_data:
espocrm_data:
mautic_db_data:
mautic_data:
freescout_db_data:
freescout_data:
uptime_kuma_data:
networks:
collaboration:
driver: bridge