name: pneumatic services: postgres: image: postgres:15-bookworm container_name: pneumatic-postgres environment: PGDATA: ${PGDATA:-var/lib/postgresql/data} POSTGRES_HOST: ${POSTGRES_HOST:-postgres} POSTGRES_USER: ${POSTGRES_USER:-postgres_user} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres_password} POSTGRES_DB: ${POSTGRES_DB:-postgres_db} POSTGRES_REPLICA_HOST: ${POSTGRES_REPLICA_HOST:-postgres} POSTGRES_REPLICA_USER: ${POSTGRES_REPLICA_USER:-postgres_user} POSTGRES_REPLICA_PASSWORD: ${POSTGRES_REPLICA_PASSWORD:-postgres_password} POSTGRES_REPLICA_DB: ${POSTGRES_REPLICA_DB:-postgres_db} FILE_POSTGRES_USER: ${FILE_POSTGRES_USER:-pneumatic} FILE_POSTGRES_PASSWORD: ${FILE_POSTGRES_PASSWORD:-pneumatic} FILE_POSTGRES_DB: ${FILE_POSTGRES_DB:-pneumatic} volumes: - ./postgres/data:/var/lib/postgresql/data:rw - ./postgres/backups:/backups:rw - ./scripts/postgres-init:/docker-entrypoint-initdb.d:ro expose: - 5432 healthcheck: test: ["CMD", "pg_isready", "-h", "localhost"] interval: 5s start_period: 60s networks: - pneumatic restart: always redis: image: redis:8.10.2-trixie container_name: pneumatic-redis mem_limit: 512m memswap_limit: 512m # swap disabled command: > --loglevel warning --databases 16 --dbfilename dump.rdb --dir /data --requirepass ${REDIS_PASSWORD:-redis_password} --save "20 1 300 100 60 10000" --stop-writes-on-bgsave-error yes --maxmemory 256mb --maxmemory-policy volatile-lru --maxmemory-samples 10 --maxmemory-clients 10% --lazyfree-lazy-eviction yes --lazyfree-lazy-expire yes --lazyfree-lazy-server-del yes --activedefrag yes --timeout 300 --tcp-keepalive 300 volumes: - ./redis/data:/data expose: - 6379 networks: - pneumatic restart: always rabbitmq: container_name: pneumatic-rabbitmq image: rabbitmq:3.13-management expose: - 5672 # broker url ports: - 15672:15672 # admin panel volumes: - ./rabbitmq/data/:/var/lib/rabbitmq/ - ./rabbitmq/log/:/var/log/rabbitmq environment: RABBITMQ_DEFAULT_USER: ${RABBITMQ_USER:-rabbitmq_user} RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASSWORD:-rabbitmq_password} networks: - pneumatic backend: build: context: ./backend image: pneumaticworkflow/pneumatic-backend:${TAG:-src} container_name: pneumatic-backend command: > sh -c "python manage.py migrate && python manage.py collectstatic --no-input && python manage.py init_periodic_tasks && python manage.py compilemessages && gunicorn src.asgi:application --workers ${WORKERS_COUNT:-2} -k uvicorn.workers.UvicornWorker --worker-tmp-dir /dev/shm --bind unix:/tmp/gunicorn/pneumatic-backend.sock --timeout 200" environment: BACKEND_URL: ${BACKEND_URL:-http://localhost:8001} FRONTEND_URL: ${FRONTEND_URL:-http://localhost} FORMS_URL: ${FORMS_URL:-http://localhost/forms} ENVIRONMENT: ${ENVIRONMENT:-Production} RELEASE: ${RELEASE:-1.0.0} LANGUAGE_CODE: ${LANGUAGE_CODE:-en} # Allowed values: en, fr, de, es, ru CAPTCHA: ${CAPTCHA:-no} RECAPTCHA_SITE_KEY: ${RECAPTCHA_SITE_KEY:-} RECAPTCHA_SECRET_KEY: ${RECAPTCHA_SECRET_KEY:-} RECAPTCHA_TESTING: ${RECAPTCHA_TESTING:-no} ANALYTICS: ${ANALYTICS:-no} ANALYTICS_DEBUG: ${ANALYTICS_DEBUG:-no} ANALYTICS_WRITE_KEY: ${ANALYTICS_WRITE_KEY:-} BILLING: ${BILLING:-no} STRIPE_SECRET_KEY: ${STRIPE_SECRET_KEY:-} STRIPE_WEBHOOK_SECRET: ${STRIPE_WEBHOOK_SECRET:-} STRIPE_WEBHOOK_IP_WHITELIST: ${STRIPE_WEBHOOK_IP_WHITELIST:-} SIGNUP: ${SIGNUP:-yes} MS_AUTH: ${MS_AUTH:-no} MS_CLIENT_ID: ${MS_CLIENT_ID:-} MS_CLIENT_SECRET: ${MS_CLIENT_SECRET:-} MS_AUTHORITY: ${MS_AUTHORITY:-} GOOGLE_AUTH: ${GOOGLE_AUTH:-no} GOOGLE_OAUTH2_CLIENT_ID: ${GOOGLE_OAUTH2_CLIENT_ID:-} GOOGLE_OAUTH2_CLIENT_SECRET: ${GOOGLE_OAUTH2_CLIENT_SECRET:-} GOOGLE_OAUTH2_REDIRECT_URI: ${GOOGLE_OAUTH2_REDIRECT_URI:-} SSO_AUTH: ${SSO_AUTH:-no} SSO_PROVIDER: ${SSO_PROVIDER:-} # Allowed values: okta, auth0 OKTA_DOMAIN: ${OKTA_DOMAIN:-} OKTA_CLIENT_ID: ${OKTA_CLIENT_ID:-} OKTA_CLIENT_SECRET: ${OKTA_CLIENT_SECRET:-} OKTA_REDIRECT_URI: ${OKTA_REDIRECT_URI:-} AUTH0_CLIENT_ID: ${AUTH0_CLIENT_ID:-} AUTH0_CLIENT_SECRET: ${AUTH0_CLIENT_SECRET:-} AUTH0_DOMAIN: ${AUTH0_DOMAIN:-} AUTH0_REDIRECT_URI: ${AUTH0_REDIRECT_URI:-} EMAIL: ${EMAIL:-no} # Disable send emails EMAIL_PROVIDER: ${EMAIL_PROVIDER:-} CIO_WEBHOOK_API_VERSION: ${CIO_WEBHOOK_API_VERSION:-} CIO_WEBHOOK_API_KEY: ${CIO_WEBHOOK_API_KEY:-} CIO_TRANSACTIONAL_API_KEY: ${CIO_TRANSACTIONAL_API_KEY:-} CIO_TEMPLATE__RESET_PASSWORD: ${CIO_TEMPLATE__RESET_PASSWORD:-} CIO_TEMPLATE__INVITE: ${CIO_TEMPLATE__INVITE:-} CIO_TEMPLATE__USER_DEACTIVATED: ${CIO_TEMPLATE__USER_DEACTIVATED:-} CIO_TEMPLATE__NEW_TASK: ${CIO_TEMPLATE__NEW_TASK:-} CIO_TEMPLATE__TASK_RETURNED: ${CIO_TEMPLATE__TASK_RETURNED:-} CIO_TEMPLATE__ACCOUNT_VERIFICATION: ${CIO_TEMPLATE__ACCOUNT_VERIFICATION:-} CIO_TEMPLATE__WORKFLOWS_DIGEST: ${CIO_TEMPLATE__WORKFLOWS_DIGEST:-} CIO_TEMPLATE__TASKS_DIGEST: ${CIO_TEMPLATE__TASKS_DIGEST:-} CIO_TEMPLATE__USER_TRANSFER: ${CIO_TEMPLATE__USER_TRANSFER:-} CIO_TEMPLATE__UNREAD_NOTIFICATIONS: ${CIO_TEMPLATE__UNREAD_NOTIFICATIONS:-} CIO_TEMPLATE__GUEST_NEW_TASK: ${CIO_TEMPLATE__GUEST_NEW_TASK:-} CIO_TEMPLATE__OVERDUE_TASK: ${CIO_TEMPLATE__OVERDUE_TASK:-} CIO_TEMPLATE__MENTION: ${CIO_TEMPLATE__MENTION:-} CIO_TEMPLATE__TASK_REMINDER: ${CIO_TEMPLATE__TASK_REMINDER:-} CIO_TEMPLATE__COMPLETE_WORKFLOW: ${CIO_TEMPLATE__COMPLETE_WORKFLOW:-} CIO_TEMPLATE__VACATION_DELEGATION: ${CIO_TEMPLATE__VACATION_DELEGATION:-} EMAIL_HOST: ${EMAIL_HOST:-} EMAIL_PORT: ${EMAIL_PORT:-} EMAIL_HOST_USER: ${EMAIL_HOST_USER:-} EMAIL_HOST_PASSWORD: ${EMAIL_HOST_PASSWORD:-} EMAIL_USE_TLS: ${EMAIL_USE_TLS:-} EMAIL_USE_SSL: ${EMAIL_USE_SSL:-} EMAIL_TIMEOUT: ${EMAIL_TIMEOUT:-} AI: ${AI:-no} AI_PROVIDER: ${AI_PROVIDER:-} OPENAI_API_KEY: ${OPENAI_API_KEY:-} OPENAI_API_ORG: ${OPENAI_API_ORG:-} AI_HTTP_PROXY: ${AI_HTTP_PROXY:-} AI_HTTPS_PROXY: ${AI_HTTPS_PROXY:-} PUSH: ${PUSH:-no} PUSH_PROVIDER: ${PUSH_PROVIDER:-} FIREBASE_PUSH_APPLICATION_CREDENTIALS: ${FIREBASE_PUSH_APPLICATION_CREDENTIALS:-/pneumatic_backend/firebase-push.json} SENTRY_DSN: ${SENTRY_DSN:-} POSTGRES_HOST: ${POSTGRES_HOST:-postgres} POSTGRES_USER: ${POSTGRES_USER:-postgres_user} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres_password} POSTGRES_DB: ${POSTGRES_DB:-postgres_db} POSTGRES_REPLICA_HOST: ${POSTGRES_REPLICA_HOST:-postgres} POSTGRES_REPLICA_USER: ${POSTGRES_REPLICA_USER:-postgres_user} POSTGRES_REPLICA_PASSWORD: ${POSTGRES_REPLICA_PASSWORD:-postgres_password} POSTGRES_REPLICA_DB: ${POSTGRES_REPLICA_DB:-postgres_db} DJANGO_DEBUG: ${DJANGO_DEBUG:-no} ADMIN_PATH: ${ADMIN_PATH:-admin} DJANGO_SECRET_KEY: ${DJANGO_SECRET_KEY:-django_secret_django_secret_django_secret} DJANGO_SETTINGS_MODULE: src.settings AUTH_REDIS_URL: "redis://:${REDIS_PASSWORD:-redis_password}@redis:6379/1" CACHE_REDIS_URL: "redis://:${REDIS_PASSWORD:-redis_password}@redis:6379/0" CHANNELS_REDIS_URL: "redis://:${REDIS_PASSWORD:-redis_password}@redis:6379/2" SESSION_REDIS_URL: "redis://:${REDIS_PASSWORD:-redis_password}@redis:6379/3" CELERY_BROKER_URL: "amqp://${RABBITMQ_USER:-rabbitmq_user}:${RABBITMQ_PASSWORD:-rabbitmq_password}@rabbitmq:5672" ALLOWED_HOSTS: "pneumatic-nginx" VERIFICATION_CHECK: no CORS_ORIGIN_ALLOW_ALL: no CORS_ALLOW_CREDENTIALS: yes CORS_ORIGIN_WHITELIST: "http://localhost:8000" ENABLE_LOGGING: ${ENABLE_LOGGING:-no} FILE_SERVICE_URL: ${FILE_SERVICE_URL:-http://localhost/files} # Migration only: direct DB access for sync_files_to_file_service / run_file_migration. # Can be removed after all accounts are migrated to file-service. FILE_POSTGRES_USER: ${FILE_POSTGRES_USER:-pneumatic} FILE_POSTGRES_PASSWORD: ${FILE_POSTGRES_PASSWORD:-pneumatic} FILE_POSTGRES_DB: ${FILE_POSTGRES_DB:-pneumatic} FILE_POSTGRES_HOST: ${FILE_POSTGRES_HOST:-postgres} FILE_POSTGRES_PORT: ${FILE_POSTGRES_PORT:-5432} LOGS_BACKEND: ${LOGS_BACKEND:-} LOGS_REDIS_URL: "redis://:${REDIS_PASSWORD:-redis_password}@redis:6379/4" LOGS_OTLP_ENDPOINT: ${LOGS_OTLP_ENDPOINT:-http://otel-collector:4318} LOGS_STREAM_MAXLEN: ${LOGS_STREAM_MAXLEN:-250000} LOGS_CONSUMER_BATCH_SIZE: ${LOGS_CONSUMER_BATCH_SIZE:-1000} expose: - 8000 volumes: - ./backend:/pneumatic_backend - backend-socket:/tmp/gunicorn - backend-staticfiles:/pneumatic_backend/staticfiles depends_on: - postgres - rabbitmq - redis healthcheck: test: ["CMD", "pg_isready", "-h", postgres] interval: 5s timeout: 5s retries: 10 networks: - pneumatic restart: always celery: build: context: ./backend image: pneumaticworkflow/pneumatic-celery:${TAG:-src} container_name: pneumatic-celery environment: BACKEND_URL: ${BACKEND_URL:-http://localhost:8001} FRONTEND_URL: ${FRONTEND_URL:-http://localhost} FORMS_URL: ${FORMS_URL:-http://localhost/forms} ENVIRONMENT: ${ENVIRONMENT:-Production} RELEASE: ${RELEASE:-1.0.0} LANGUAGE_CODE: ${LANGUAGE_CODE:-en} # Allowed values: en, fr, de, es, ru CAPTCHA: ${CAPTCHA:-no} RECAPTCHA_SITE_KEY: ${RECAPTCHA_SITE_KEY:-} RECAPTCHA_SECRET_KEY: ${RECAPTCHA_SECRET_KEY:-} RECAPTCHA_TESTING: ${RECAPTCHA_TESTING:-no} ANALYTICS: ${ANALYTICS:-no} ANALYTICS_DEBUG: ${ANALYTICS_DEBUG:-no} ANALYTICS_WRITE_KEY: ${ANALYTICS_WRITE_KEY:-} BILLING: ${BILLING:-no} STRIPE_SECRET_KEY: ${STRIPE_SECRET_KEY:-} STRIPE_WEBHOOK_SECRET: ${STRIPE_WEBHOOK_SECRET:-} STRIPE_WEBHOOK_IP_WHITELIST: ${STRIPE_WEBHOOK_IP_WHITELIST:-} SIGNUP: ${SIGNUP:-yes} MS_AUTH: ${MS_AUTH:-no} MS_CLIENT_ID: ${MS_CLIENT_ID:-} MS_CLIENT_SECRET: ${MS_CLIENT_SECRET:-} MS_AUTHORITY: ${MS_AUTHORITY:-} GOOGLE_AUTH: ${GOOGLE_AUTH:-no} GOOGLE_OAUTH2_CLIENT_ID: ${GOOGLE_OAUTH2_CLIENT_ID:-} GOOGLE_OAUTH2_CLIENT_SECRET: ${GOOGLE_OAUTH2_CLIENT_SECRET:-} GOOGLE_OAUTH2_REDIRECT_URI: ${GOOGLE_OAUTH2_REDIRECT_URI:-} SSO_AUTH: ${SSO_AUTH:-no} SSO_PROVIDER: ${SSO_PROVIDER:-} # Allowed values: okta, auth0 OKTA_DOMAIN: ${OKTA_DOMAIN:-} OKTA_CLIENT_ID: ${OKTA_CLIENT_ID:-} OKTA_CLIENT_SECRET: ${OKTA_CLIENT_SECRET:-} OKTA_REDIRECT_URI: ${OKTA_REDIRECT_URI:-} AUTH0_CLIENT_ID: ${AUTH0_CLIENT_ID:-} AUTH0_CLIENT_SECRET: ${AUTH0_CLIENT_SECRET:-} AUTH0_DOMAIN: ${AUTH0_DOMAIN:-} AUTH0_REDIRECT_URI: ${AUTH0_REDIRECT_URI:-} EMAIL: ${EMAIL:-no} # Disable send emails EMAIL_PROVIDER: ${EMAIL_PROVIDER:-} CIO_WEBHOOK_API_VERSION: ${CIO_WEBHOOK_API_VERSION:-} CIO_WEBHOOK_API_KEY: ${CIO_WEBHOOK_API_KEY:-} CIO_TRANSACTIONAL_API_KEY: ${CIO_TRANSACTIONAL_API_KEY:-} CIO_TEMPLATE__RESET_PASSWORD: ${CIO_TEMPLATE__RESET_PASSWORD:-} CIO_TEMPLATE__INVITE: ${CIO_TEMPLATE__INVITE:-} CIO_TEMPLATE__USER_DEACTIVATED: ${CIO_TEMPLATE__USER_DEACTIVATED:-} CIO_TEMPLATE__NEW_TASK: ${CIO_TEMPLATE__NEW_TASK:-} CIO_TEMPLATE__TASK_RETURNED: ${CIO_TEMPLATE__TASK_RETURNED:-} CIO_TEMPLATE__ACCOUNT_VERIFICATION: ${CIO_TEMPLATE__ACCOUNT_VERIFICATION:-} CIO_TEMPLATE__WORKFLOWS_DIGEST: ${CIO_TEMPLATE__WORKFLOWS_DIGEST:-} CIO_TEMPLATE__TASKS_DIGEST: ${CIO_TEMPLATE__TASKS_DIGEST:-} CIO_TEMPLATE__USER_TRANSFER: ${CIO_TEMPLATE__USER_TRANSFER:-} CIO_TEMPLATE__UNREAD_NOTIFICATIONS: ${CIO_TEMPLATE__UNREAD_NOTIFICATIONS:-} CIO_TEMPLATE__GUEST_NEW_TASK: ${CIO_TEMPLATE__GUEST_NEW_TASK:-} CIO_TEMPLATE__OVERDUE_TASK: ${CIO_TEMPLATE__OVERDUE_TASK:-} CIO_TEMPLATE__MENTION: ${CIO_TEMPLATE__MENTION:-} CIO_TEMPLATE__TASK_REMINDER: ${CIO_TEMPLATE__TASK_REMINDER:-} CIO_TEMPLATE__COMPLETE_WORKFLOW: ${CIO_TEMPLATE__COMPLETE_WORKFLOW:-} CIO_TEMPLATE__VACATION_DELEGATION: ${CIO_TEMPLATE__VACATION_DELEGATION:-} EMAIL_HOST: ${EMAIL_HOST:-} EMAIL_PORT: ${EMAIL_PORT:-} EMAIL_HOST_USER: ${EMAIL_HOST_USER:-} EMAIL_HOST_PASSWORD: ${EMAIL_HOST_PASSWORD:-} EMAIL_USE_TLS: ${EMAIL_USE_TLS:-} EMAIL_USE_SSL: ${EMAIL_USE_SSL:-} EMAIL_TIMEOUT: ${EMAIL_TIMEOUT:-} AI: ${AI:-no} AI_PROVIDER: ${AI_PROVIDER:-} OPENAI_API_KEY: ${OPENAI_API_KEY:-} OPENAI_API_ORG: ${OPENAI_API_ORG:-} AI_HTTP_PROXY: ${AI_HTTP_PROXY:-} AI_HTTPS_PROXY: ${AI_HTTPS_PROXY:-} PUSH: ${PUSH:-no} PUSH_PROVIDER: ${PUSH_PROVIDER:-} FIREBASE_PUSH_APPLICATION_CREDENTIALS: ${FIREBASE_PUSH_APPLICATION_CREDENTIALS:-/pneumatic_backend/firebase-push.json} SENTRY_DSN: ${SENTRY_DSN:-} POSTGRES_HOST: ${POSTGRES_HOST:-postgres} POSTGRES_USER: ${POSTGRES_USER:-postgres_user} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres_password} POSTGRES_DB: ${POSTGRES_DB:-postgres_db} POSTGRES_REPLICA_HOST: ${POSTGRES_REPLICA_HOST:-postgres} POSTGRES_REPLICA_USER: ${POSTGRES_REPLICA_USER:-postgres_user} POSTGRES_REPLICA_PASSWORD: ${POSTGRES_REPLICA_PASSWORD:-postgres_password} POSTGRES_REPLICA_DB: ${POSTGRES_REPLICA_DB:-postgres_db} DJANGO_DEBUG: ${DJANGO_DEBUG:-no} ADMIN_PATH: ${ADMIN_PATH:-admin} DJANGO_SECRET_KEY: ${DJANGO_SECRET_KEY:-django_secret_django_secret_django_secret} DJANGO_SETTINGS_MODULE: src.settings AUTH_REDIS_URL: "redis://:${REDIS_PASSWORD:-redis_password}@redis:6379/1" CACHE_REDIS_URL: "redis://:${REDIS_PASSWORD:-redis_password}@redis:6379/0" CHANNELS_REDIS_URL: "redis://:${REDIS_PASSWORD:-redis_password}@redis:6379/2" SESSION_REDIS_URL: "redis://:${REDIS_PASSWORD:-redis_password}@redis:6379/3" CELERY_BROKER_URL: "amqp://${RABBITMQ_USER:-rabbitmq_user}:${RABBITMQ_PASSWORD:-rabbitmq_password}@rabbitmq:5672" ALLOWED_HOSTS: "pneumatic-nginx" VERIFICATION_CHECK: no CORS_ORIGIN_ALLOW_ALL: no CORS_ALLOW_CREDENTIALS: yes CORS_ORIGIN_WHITELIST: "http://localhost:8000" ENABLE_LOGGING: ${ENABLE_LOGGING:-no} FILE_SERVICE_URL: ${FILE_SERVICE_URL:-http://localhost/files} # Migration only: direct DB access for sync_files_to_file_service / run_file_migration. # Can be removed after all accounts are migrated to file-service. FILE_POSTGRES_USER: ${FILE_POSTGRES_USER:-pneumatic} FILE_POSTGRES_PASSWORD: ${FILE_POSTGRES_PASSWORD:-pneumatic} FILE_POSTGRES_DB: ${FILE_POSTGRES_DB:-pneumatic} FILE_POSTGRES_HOST: ${FILE_POSTGRES_HOST:-postgres} FILE_POSTGRES_PORT: ${FILE_POSTGRES_PORT:-5432} LOGS_BACKEND: ${LOGS_BACKEND:-} LOGS_REDIS_URL: "redis://:${REDIS_PASSWORD:-redis_password}@redis:6379/4" LOGS_OTLP_ENDPOINT: ${LOGS_OTLP_ENDPOINT:-http://otel-collector:4318} LOGS_STREAM_MAXLEN: ${LOGS_STREAM_MAXLEN:-250000} LOGS_CONSUMER_BATCH_SIZE: ${LOGS_CONSUMER_BATCH_SIZE:-1000} volumes: - ./backend:/pneumatic_backend command: > sh -c "celery --app src.celery_app:app worker -l warning" depends_on: - postgres - rabbitmq - redis networks: - pneumatic restart: always celery-beat: build: context: ./backend image: pneumaticworkflow/pneumatic-celery-beat:${TAG:-src} container_name: pneumatic-celery-beat environment: BACKEND_URL: ${BACKEND_URL:-http://localhost:8001} FRONTEND_URL: ${FRONTEND_URL:-http://localhost} FORMS_URL: ${FORMS_URL:-http://localhost/forms} ENVIRONMENT: ${ENVIRONMENT:-Production} RELEASE: ${RELEASE:-1.0.0} LANGUAGE_CODE: ${LANGUAGE_CODE:-en} # Allowed values: en, fr, de, es, ru CAPTCHA: ${CAPTCHA:-no} RECAPTCHA_SITE_KEY: ${RECAPTCHA_SITE_KEY:-} RECAPTCHA_SECRET_KEY: ${RECAPTCHA_SECRET_KEY:-} RECAPTCHA_TESTING: ${RECAPTCHA_TESTING:-no} ANALYTICS: ${ANALYTICS:-no} ANALYTICS_DEBUG: ${ANALYTICS_DEBUG:-no} ANALYTICS_WRITE_KEY: ${ANALYTICS_WRITE_KEY:-} BILLING: ${BILLING:-no} STRIPE_SECRET_KEY: ${STRIPE_SECRET_KEY:-} STRIPE_WEBHOOK_SECRET: ${STRIPE_WEBHOOK_SECRET:-} STRIPE_WEBHOOK_IP_WHITELIST: ${STRIPE_WEBHOOK_IP_WHITELIST:-} SIGNUP: ${SIGNUP:-yes} MS_AUTH: ${MS_AUTH:-no} MS_CLIENT_ID: ${MS_CLIENT_ID:-} MS_CLIENT_SECRET: ${MS_CLIENT_SECRET:-} MS_AUTHORITY: ${MS_AUTHORITY:-} GOOGLE_AUTH: ${GOOGLE_AUTH:-no} GOOGLE_OAUTH2_CLIENT_ID: ${GOOGLE_OAUTH2_CLIENT_ID:-} GOOGLE_OAUTH2_CLIENT_SECRET: ${GOOGLE_OAUTH2_CLIENT_SECRET:-} GOOGLE_OAUTH2_REDIRECT_URI: ${GOOGLE_OAUTH2_REDIRECT_URI:-} SSO_AUTH: ${SSO_AUTH:-no} SSO_PROVIDER: ${SSO_PROVIDER:-} # Allowed values: okta, auth0 OKTA_DOMAIN: ${OKTA_DOMAIN:-} OKTA_CLIENT_ID: ${OKTA_CLIENT_ID:-} OKTA_CLIENT_SECRET: ${OKTA_CLIENT_SECRET:-} OKTA_REDIRECT_URI: ${OKTA_REDIRECT_URI:-} AUTH0_CLIENT_ID: ${AUTH0_CLIENT_ID:-} AUTH0_CLIENT_SECRET: ${AUTH0_CLIENT_SECRET:-} AUTH0_DOMAIN: ${AUTH0_DOMAIN:-} AUTH0_REDIRECT_URI: ${AUTH0_REDIRECT_URI:-} EMAIL: ${EMAIL:-no} # Disable send emails EMAIL_PROVIDER: ${EMAIL_PROVIDER:-} CIO_WEBHOOK_API_VERSION: ${CIO_WEBHOOK_API_VERSION:-} CIO_WEBHOOK_API_KEY: ${CIO_WEBHOOK_API_KEY:-} CIO_TRANSACTIONAL_API_KEY: ${CIO_TRANSACTIONAL_API_KEY:-} CIO_TEMPLATE__RESET_PASSWORD: ${CIO_TEMPLATE__RESET_PASSWORD:-} CIO_TEMPLATE__INVITE: ${CIO_TEMPLATE__INVITE:-} CIO_TEMPLATE__USER_DEACTIVATED: ${CIO_TEMPLATE__USER_DEACTIVATED:-} CIO_TEMPLATE__NEW_TASK: ${CIO_TEMPLATE__NEW_TASK:-} CIO_TEMPLATE__TASK_RETURNED: ${CIO_TEMPLATE__TASK_RETURNED:-} CIO_TEMPLATE__ACCOUNT_VERIFICATION: ${CIO_TEMPLATE__ACCOUNT_VERIFICATION:-} CIO_TEMPLATE__WORKFLOWS_DIGEST: ${CIO_TEMPLATE__WORKFLOWS_DIGEST:-} CIO_TEMPLATE__TASKS_DIGEST: ${CIO_TEMPLATE__TASKS_DIGEST:-} CIO_TEMPLATE__USER_TRANSFER: ${CIO_TEMPLATE__USER_TRANSFER:-} CIO_TEMPLATE__UNREAD_NOTIFICATIONS: ${CIO_TEMPLATE__UNREAD_NOTIFICATIONS:-} CIO_TEMPLATE__GUEST_NEW_TASK: ${CIO_TEMPLATE__GUEST_NEW_TASK:-} CIO_TEMPLATE__OVERDUE_TASK: ${CIO_TEMPLATE__OVERDUE_TASK:-} CIO_TEMPLATE__MENTION: ${CIO_TEMPLATE__MENTION:-} CIO_TEMPLATE__TASK_REMINDER: ${CIO_TEMPLATE__TASK_REMINDER:-} CIO_TEMPLATE__COMPLETE_WORKFLOW: ${CIO_TEMPLATE__COMPLETE_WORKFLOW:-} CIO_TEMPLATE__VACATION_DELEGATION: ${CIO_TEMPLATE__VACATION_DELEGATION:-} EMAIL_HOST: ${EMAIL_HOST:-} EMAIL_PORT: ${EMAIL_PORT:-} EMAIL_HOST_USER: ${EMAIL_HOST_USER:-} EMAIL_HOST_PASSWORD: ${EMAIL_HOST_PASSWORD:-} EMAIL_USE_TLS: ${EMAIL_USE_TLS:-} EMAIL_USE_SSL: ${EMAIL_USE_SSL:-} EMAIL_TIMEOUT: ${EMAIL_TIMEOUT:-} AI: ${AI:-no} AI_PROVIDER: ${AI_PROVIDER:-} OPENAI_API_KEY: ${OPENAI_API_KEY:-} OPENAI_API_ORG: ${OPENAI_API_ORG:-} AI_HTTP_PROXY: ${AI_HTTP_PROXY:-} AI_HTTPS_PROXY: ${AI_HTTPS_PROXY:-} PUSH: ${PUSH:-no} PUSH_PROVIDER: ${PUSH_PROVIDER:-} FIREBASE_PUSH_APPLICATION_CREDENTIALS: ${FIREBASE_PUSH_APPLICATION_CREDENTIALS:-/pneumatic_backend/firebase-push.json} SENTRY_DSN: ${SENTRY_DSN:-} POSTGRES_HOST: ${POSTGRES_HOST:-postgres} POSTGRES_USER: ${POSTGRES_USER:-postgres_user} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres_password} POSTGRES_DB: ${POSTGRES_DB:-postgres_db} POSTGRES_REPLICA_HOST: ${POSTGRES_REPLICA_HOST:-postgres} POSTGRES_REPLICA_USER: ${POSTGRES_REPLICA_USER:-postgres_user} POSTGRES_REPLICA_PASSWORD: ${POSTGRES_REPLICA_PASSWORD:-postgres_password} POSTGRES_REPLICA_DB: ${POSTGRES_REPLICA_DB:-postgres_db} DJANGO_DEBUG: ${DJANGO_DEBUG:-no} ADMIN_PATH: ${ADMIN_PATH:-admin} DJANGO_SECRET_KEY: ${DJANGO_SECRET_KEY:-django_secret_django_secret_django_secret} DJANGO_SETTINGS_MODULE: src.settings AUTH_REDIS_URL: "redis://:${REDIS_PASSWORD:-redis_password}@redis:6379/1" CACHE_REDIS_URL: "redis://:${REDIS_PASSWORD:-redis_password}@redis:6379/0" CHANNELS_REDIS_URL: "redis://:${REDIS_PASSWORD:-redis_password}@redis:6379/2" SESSION_REDIS_URL: "redis://:${REDIS_PASSWORD:-redis_password}@redis:6379/3" CELERY_BROKER_URL: "amqp://${RABBITMQ_USER:-rabbitmq_user}:${RABBITMQ_PASSWORD:-rabbitmq_password}@rabbitmq:5672" ALLOWED_HOSTS: "pneumatic-nginx" VERIFICATION_CHECK: no CORS_ORIGIN_ALLOW_ALL: no CORS_ALLOW_CREDENTIALS: yes CORS_ORIGIN_WHITELIST: "http://localhost:8000" ENABLE_LOGGING: ${ENABLE_LOGGING:-no} FILE_SERVICE_URL: ${FILE_SERVICE_URL:-http://localhost/files} # Migration only: direct DB access for sync_files_to_file_service / run_file_migration. # Can be removed after all accounts are migrated to file-service. FILE_POSTGRES_USER: ${FILE_POSTGRES_USER:-pneumatic} FILE_POSTGRES_PASSWORD: ${FILE_POSTGRES_PASSWORD:-pneumatic} FILE_POSTGRES_DB: ${FILE_POSTGRES_DB:-pneumatic} FILE_POSTGRES_HOST: ${FILE_POSTGRES_HOST:-postgres} FILE_POSTGRES_PORT: ${FILE_POSTGRES_PORT:-5432} LOGS_BACKEND: ${LOGS_BACKEND:-} LOGS_REDIS_URL: "redis://:${REDIS_PASSWORD:-redis_password}@redis:6379/4" LOGS_OTLP_ENDPOINT: ${LOGS_OTLP_ENDPOINT:-http://otel-collector:4318} LOGS_STREAM_MAXLEN: ${LOGS_STREAM_MAXLEN:-250000} LOGS_CONSUMER_BATCH_SIZE: ${LOGS_CONSUMER_BATCH_SIZE:-1000} volumes: - ./backend:/pneumatic_backend command: celery --app src.celery_app:app beat -l warning -S django depends_on: - postgres - rabbitmq - redis networks: - pneumatic restart: always frontend: build: context: ./frontend image: pneumaticworkflow/pneumatic-frontend:${TAG:-src} container_name: pneumatic-frontend environment: BACKEND_PRIVATE_URL: ${BACKEND_PRIVATE_URL:-http://pneumatic-nginx/} BACKEND_URL: ${BACKEND_URL:-http://localhost:8001} FRONTEND_URL: ${FRONTEND_URL:-http://localhost} WSS_URL: ${WSS_URL:-ws://localhost:8001/} FORM_DOMAIN: ${FORM_DOMAIN:-} MCS_RUN_ENV: ${MCS_RUN_ENV:-prod} NODE_OPTIONS: ${NODE_OPTIONS:---max-old-space-size=3072} LANGUAGE_CODE: ${LANGUAGE_CODE:-en} # Allowed values: en, fr, de, es, ru CAPTCHA: ${CAPTCHA:-no} ANALYTICS: ${ANALYTICS:-no} ANALYTICS_WRITE_KEY: ${ANALYTICS_WRITE_KEY:-} BILLING: ${BILLING:-no} SIGNUP: ${SIGNUP:-yes} MS_AUTH: ${MS_AUTH:-no} GOOGLE_AUTH: ${GOOGLE_AUTH:-no} SSO_AUTH: ${SSO_AUTH:-no} SSO_PROVIDER: ${SSO_PROVIDER:-} # Allowed values: okta, auth0 AI: ${AI:-no} PUSH: ${PUSH:-no} FILE_SERVICE_URL: ${FILE_SERVICE_URL:-http://localhost/files} RELEASE: ${RELEASE:-1.0.0} SENTRY_DSN: ${SENTRY_FRONTEND_DSN:-} SENTRY_RELEASE: ${RELEASE:-1.0.0} FIREBASE_VAPID_KEY: ${FIREBASE_VAPID_KEY:-} FIREBASE_API_KEY: ${FIREBASE_API_KEY:-} FIREBASE_AUTH_DOMAIN: ${FIREBASE_AUTH_DOMAIN:-} FIREBASE_PROJECT_ID: ${FIREBASE_PROJECT_ID:-} FIREBASE_STORAGE_BUCKET: ${FIREBASE_STORAGE_BUCKET:-} FIREBASE_SENDER_ID: ${FIREBASE_SENDER_ID:-} FIREBASE_APP_ID: ${FIREBASE_APP_ID:-} FIREBASE_MEASUREMENT_ID: ${FIREBASE_MEASUREMENT_ID:-} RECAPTCHA_SITE_KEY: ${RECAPTCHA_SITE_KEY:-} command: pm2-runtime start pm2.json expose: - 8000 networks: - pneumatic restart: always nginx: image: nginx:1.25.4 container_name: pneumatic-nginx environment: SSL: ${SSL:-no} SERVER_ADDRESS: ${SERVER_ADDRESS:-localhost} FORM_DOMAIN: ${FORM_DOMAIN:-} NGINX_ENVSUBST_OUTPUT_DIR: /etc/nginx/ NGINX_ENVSUBST_TEMPLATE_DIR: /etc/nginx/templates volumes: - ./nginx/logs/:/var/log/nginx/ - ./nginx/keys/:/etc/keys/:ro - ./nginx/www/:/var/www/:ro - ${NGINX_CONF_TEMPLATE:-./nginx/templates/}:/etc/nginx/templates - ./nginx/includes/:/etc/nginx/includes/:ro - backend-socket:/tmp/gunicorn/ - backend-staticfiles:/tmp/backend-staticfiles/ ports: - 80:80 - 443:443 - 8001:8001 networks: - pneumatic depends_on: - backend - frontend - file-service restart: always file-service: build: context: ./storage image: pneumaticworkflow/pneumatic-file-service:${TAG:-src} container_name: pneumatic-file-service environment: DEBUG: ${FILE_DEBUG:-false} CONFIG: ${FILE_CONFIG:-Production} WORKERS: ${FILE_WORKERS:-1} PORT: ${FILE_PORT:-8000} FRONTEND_URL: ${FRONTEND_URL:-http://localhost} FORMS_URL: ${FORMS_URL:-http://localhost/forms} POSTGRES_USER: ${FILE_POSTGRES_USER:-pneumatic} POSTGRES_PASSWORD: ${FILE_POSTGRES_PASSWORD:-pneumatic} POSTGRES_DB: ${FILE_POSTGRES_DB:-pneumatic} POSTGRES_HOST: ${FILE_POSTGRES_HOST:-postgres} POSTGRES_PORT: ${FILE_POSTGRES_PORT:-5432} STORAGE_TYPE: ${FILE_STORAGE_TYPE:-local} SEAWEEDFS_S3_ENDPOINT: ${FILE_SEAWEEDFS_S3_ENDPOINT:-http://seaweedfs-filer:8333} SEAWEEDFS_S3_ACCESS_KEY: ${FILE_SEAWEEDFS_S3_ACCESS_KEY:-any-secret-key-will-work} SEAWEEDFS_S3_SECRET_KEY: ${FILE_SEAWEEDFS_S3_SECRET_KEY:-any-secret-key-will-work} SEAWEEDFS_S3_REGION: ${FILE_SEAWEEDFS_S3_REGION:-us-central1} SEAWEEDFS_S3_USE_SSL: ${FILE_SEAWEEDFS_S3_USE_SSL:-false} GCS_S3_ENDPOINT: ${FILE_GCS_S3_ENDPOINT:-} GCS_S3_ACCESS_KEY: ${FILE_GCS_S3_ACCESS_KEY:-} GCS_S3_SECRET_KEY: ${FILE_GCS_S3_SECRET_KEY:-} GCS_S3_REGION: ${FILE_GCS_S3_REGION:-} FASTAPI_BASE_URL: ${FILE_FASTAPI_BASE_URL:-http://localhost/files} BUCKET_PREFIX: ${FILE_BUCKET_PREFIX:-pneumatic-dev-test} MAX_FILE_SIZE: ${FILE_MAX_FILE_SIZE:-104857600} CHUNK_SIZE: ${FILE_CHUNK_SIZE:-1048576} AUTH_REDIS_URL: "redis://:${REDIS_PASSWORD:-redis_password}@redis:6379/1" KEY_PREFIX_REDIS: ${FILE_KEY_PREFIX_REDIS:-:1:} DJANGO_SECRET_KEY: ${DJANGO_SECRET_KEY:-django_secret_django_secret_django_secret} AUTH_TOKEN_ITERATIONS: ${FILE_AUTH_TOKEN_ITERATIONS:-1} BACKEND_PRIVATE_URL: ${BACKEND_PRIVATE_URL:-http://pneumatic-nginx/} SENTRY_DSN: ${SENTRY_FILE_DSN:-} SENTRY_ENVIRONMENT: ${ENVIRONMENT:-Production} LOGS_BACKEND: ${LOGS_BACKEND:-} LOGS_REDIS_URL: "redis://:${REDIS_PASSWORD:-redis_password}@redis:6379/4" LOGS_STREAM_MAXLEN: ${LOGS_STREAM_MAXLEN:-250000} command: > sh -c "poetry run alembic -c src/shared_kernel/database/alembic.ini upgrade head && poetry run uvicorn src.main:app --host 0.0.0.0 --port ${FILE_PORT:-8000} --workers ${FILE_WORKERS:-1}" depends_on: postgres: condition: service_healthy redis: condition: service_started expose: - 8000 networks: - pneumatic restart: always seaweedfs-master: image: chrislusf/seaweedfs:3.95 container_name: pneumatic-seaweedfs-master command: master -ip=seaweedfs-master -port=9333 expose: - 9333 volumes: - ./seaweedfs/master/data:/data:rw networks: - pneumatic seaweedfs-volume: image: chrislusf/seaweedfs:3.95 container_name: pneumatic-seaweedfs-volume command: - volume - -mserver=seaweedfs-master:9333 - -port=8080 - -ip=seaweedfs-volume - -publicUrl=localhost:8080 expose: - 8080 depends_on: - seaweedfs-master volumes: - ./seaweedfs/volume/data:/data:rw networks: - pneumatic seaweedfs-filer: image: chrislusf/seaweedfs:3.95 container_name: pneumatic-seaweedfs-filer command: filer -master="seaweedfs-master:9333" -s3 -s3.port=8333 -s3.config=/etc/seaweedfs/s3.json expose: - 8333 depends_on: - seaweedfs-master - seaweedfs-volume volumes: - ./storage/seaweedfs/filer.toml:/etc/seaweedfs/filer.toml - ./storage/seaweedfs/s3.json:/etc/seaweedfs/s3.json:ro - ./seaweedfs/filer/data:/data:rw networks: - pneumatic # The collector of the audit journal. Off by default: it starts only # with the profile named after LOGS_BACKEND, which start.sh passes on # its own (by hand: COMPOSE_PROFILES, see default.env). The store it # writes into lives on a machine of its own - logging/grafana for Loki # and Grafana, logging/elasticsearch for Elasticsearch and Kibana. otel-collector: image: otel/opentelemetry-collector-contrib:0.160.0 container_name: pneumatic-otel-collector profiles: ["logs-otlp", "logs-elasticsearch"] command: - --config=/etc/otel/base.yaml - --config=/etc/otel/backend-${LOGS_BACKEND:-}.yaml environment: LOGS_OTLP_EXPORT_ENDPOINT: ${LOGS_OTLP_EXPORT_ENDPOINT:-} LOGS_OTLP_EXPORT_AUTH: ${LOGS_OTLP_EXPORT_AUTH:-} LOGS_ELASTICSEARCH_URL: ${LOGS_ELASTICSEARCH_URL:-} LOGS_ELASTICSEARCH_API_KEY: ${LOGS_ELASTICSEARCH_API_KEY:-} LOGS_ELASTICSEARCH_USER: ${LOGS_ELASTICSEARCH_USER:-} LOGS_ELASTICSEARCH_PASSWORD: ${LOGS_ELASTICSEARCH_PASSWORD:-} LOGS_ELASTICSEARCH_CA_FILE: ${LOGS_ELASTICSEARCH_CA_FILE:-/etc/otel/es-ca.crt} LOGS_ELASTICSEARCH_QUEUE_DIR: ${LOGS_ELASTICSEARCH_QUEUE_DIR:-/var/lib/otelcol/queue} LOGS_ELASTICSEARCH_INDEX: ${LOGS_ELASTICSEARCH_INDEX:-logs-pneumatic.events.otel-default} volumes: - ./otel-collector:/etc/otel:ro # The disk queue of LOGS_BACKEND=elasticsearch, see default.env - otel-queue:/var/lib/otelcol expose: - 4318 # OTLP/HTTP receiver - 13133 # health_check extension networks: - pneumatic restart: always networks: pneumatic: volumes: backend-staticfiles: backend-socket: otel-queue: