services: traefik: image: traefik:v3.2 command: - --configFile=/etc/traefik/traefik.yml ports: - "0.0.0.0:8288:80" - "0.0.0.0:2288:443" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - ./traefik/traefik.yml:/etc/traefik/traefik.yml:ro - ./traefik/dynamic:/etc/traefik/dynamic - ./traefik/letsencrypt:/letsencrypt networks: - portal_net restart: unless-stopped db: image: postgres:16 environment: POSTGRES_DB: ${POSTGRES_DB} POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} volumes: - pg_data:/var/lib/postgresql/data networks: - portal_net restart: unless-stopped api: build: context: ./app command: ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "6"] environment: DATABASE_URL: postgresql+psycopg2://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB} SIGNING_KEY: ${SIGNING_KEY} PUBLIC_HOST: ${PUBLIC_HOST} ADMIN_USERNAME: ${ADMIN_USERNAME} ADMIN_PASSWORD: ${ADMIN_PASSWORD} SESSION_IDLE_SECONDS: ${SESSION_IDLE_SECONDS:-7200} PREWARM_POOL_SIZE: ${PREWARM_POOL_SIZE:-2} UNIVERSAL_POOL_SIZE: ${UNIVERSAL_POOL_SIZE:-0} WEB_POOL_SIZE: ${WEB_POOL_SIZE:-20} WEB_POOL_BUFFER: ${WEB_POOL_BUFFER:-10} MAX_ACTIVE_SERVICES_PER_USER: ${MAX_ACTIVE_SERVICES_PER_USER:-4} LOG_LEVEL: ${LOG_LEVEL:-INFO} GO_USER_LOCK_TIMEOUT_SECONDS: 8 GO_POOL_LOCK_TIMEOUT_SECONDS: 20 POOL_DISPATCH_RETRIES: 6 ENABLE_STARTUP_MAINTENANCE: 0 depends_on: - db volumes: - /var/run/docker.sock:/var/run/docker.sock - ./app/static/service-icons:/app/static/service-icons labels: - traefik.enable=true - traefik.docker.network=portal_net - traefik.http.routers.portal.rule=Host(`${PUBLIC_HOST}`) - traefik.http.routers.portal.entrypoints=websecure - traefik.http.routers.portal.tls=true - traefik.http.routers.portal.tls.certresolver=letsencrypt - traefik.http.routers.portal.priority=1 - traefik.http.services.portal.loadbalancer.server.port=8000 - traefik.http.routers.portal.middlewares=secure-headers@file networks: - portal_net restart: unless-stopped maintenance: build: context: ./app command: [python, maintenance_runner.py] environment: DATABASE_URL: postgresql+psycopg2://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB} SIGNING_KEY: ${SIGNING_KEY} PUBLIC_HOST: ${PUBLIC_HOST} ADMIN_USERNAME: ${ADMIN_USERNAME} ADMIN_PASSWORD: ${ADMIN_PASSWORD} SESSION_IDLE_SECONDS: ${SESSION_IDLE_SECONDS:-7200} PREWARM_POOL_SIZE: ${PREWARM_POOL_SIZE:-2} UNIVERSAL_POOL_SIZE: ${UNIVERSAL_POOL_SIZE:-0} WEB_POOL_SIZE: ${WEB_POOL_SIZE:-20} WEB_POOL_BUFFER: ${WEB_POOL_BUFFER:-10} MAX_ACTIVE_SERVICES_PER_USER: ${MAX_ACTIVE_SERVICES_PER_USER:-4} LOG_LEVEL: ${LOG_LEVEL:-INFO} GO_USER_LOCK_TIMEOUT_SECONDS: 8 GO_POOL_LOCK_TIMEOUT_SECONDS: 20 POOL_DISPATCH_RETRIES: 6 ENABLE_STARTUP_MAINTENANCE: 0 depends_on: - db volumes: - /var/run/docker.sock:/var/run/docker.sock - ./app/static/service-icons:/app/static/service-icons networks: - portal_net restart: unless-stopped kiosk-image: image: portal-kiosk:latest build: context: ./kiosk profiles: ["build-only"] rdp-proxy-image: image: portal-rdp-proxy:latest build: context: ./rdp-proxy profiles: ["build-only"] universal-runtime-image: image: portal-universal-runtime:latest build: context: ./universal-runtime profiles: ["build-only"] networks: portal_net: name: portal_net volumes: pg_data: