services: app: build: context: . container_name: mailhub-app restart: unless-stopped env_file: - .env environment: PORT: 3000 DATA_DIR: /data POSTFIX_LOG_FILE: /data/postfix-logs/mail.log MAIL_ACCESS_BACKEND: dovecot MAILDIR_ROOT: /data/maildir UV_THREADPOOL_SIZE: ${UV_THREADPOOL_SIZE:-2} DOVECOT_AUTH_ENABLED: "true" DOVECOT_AUTH_HOST: 0.0.0.0 DOVECOT_AUTH_PORT: 3001 DOVECOT_AUTH_SECRET_FILE: /run/secrets/dovecot_auth_secret WEBMAIL_SSO_URL: ${WEBMAIL_SSO_URL:-} WEBMAIL_SSO_SECRET_FILE: /data/secrets/webmail_sso_secret WEBMAIL_SSO_TICKET_TTL_SECONDS: ${WEBMAIL_SSO_TICKET_TTL_SECONDS:-60} WEBMAIL_SSO_CREDENTIAL_TTL_SECONDS: ${WEBMAIL_SSO_CREDENTIAL_TTL_SECONDS:-43200} ports: - "127.0.0.1:${APP_PORT:-3025}:3000" - "${SUBMISSION_BIND:-0.0.0.0}:25:25" - "${SUBMISSION_BIND:-0.0.0.0}:465:465" - "${SUBMISSION_BIND:-0.0.0.0}:587:587" - "${SUBMISSION_BIND:-0.0.0.0}:${SUBMISSION_ALT_PORT:-2525}:2525" expose: - "3001" volumes: - ./data:/data - ./certs:/certs:ro secrets: - dovecot_auth_secret networks: - mailhub - dovecot_internal depends_on: postfix: condition: service_started healthcheck: test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000/healthz').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"] interval: 30s timeout: 5s retries: 4 start_period: 120s dovecot: image: dovecot/dovecot:2.4.4 container_name: mailhub-dovecot restart: unless-stopped hostname: ${MAIL_HOSTNAME:-mailhub.local} environment: SUBMISSION_TLS_CERT: ${SUBMISSION_TLS_CERT:-/certs/mailhub.example.com.crt} SUBMISSION_TLS_KEY: ${SUBMISSION_TLS_KEY:-/certs/mailhub.example.com.key} ports: - "${IMAP_BIND:-0.0.0.0}:143:31143" - "${IMAP_BIND:-0.0.0.0}:993:31993" - "${POP3_BIND:-0.0.0.0}:110:31110" - "${POP3_BIND:-0.0.0.0}:995:31995" volumes: - ./data/maildir:/srv/vmail - ./certs:/certs:ro - ./docker/dovecot/auth.conf:/etc/dovecot/conf.d/auth.conf:ro - ./docker/dovecot/ssl.conf:/etc/dovecot/conf.d/ssl.conf:ro - ./docker/dovecot/mailhub.conf:/etc/dovecot/conf.d/zz-mailhub.conf:ro - ./docker/dovecot/auth.lua:/etc/dovecot/auth.lua:ro secrets: - dovecot_auth_secret networks: - dovecot_internal - dovecot_public depends_on: app: condition: service_healthy read_only: true tmpfs: - /tmp:mode=1777 - /run/dovecot:mode=1777 security_opt: - no-new-privileges:true healthcheck: test: ["CMD-SHELL", "test -r /run/secrets/dovecot_auth_secret && test -s /run/secrets/dovecot_auth_secret && test -w /srv/vmail && doveadm service status imap-login pop3-login"] interval: 30s timeout: 5s retries: 3 start_period: 15s postfix: build: context: ./docker/postfix container_name: mailhub-postfix restart: unless-stopped env_file: - .env environment: POSTFIX_LOG_FILE: /var/log/mailhub/mail.log hostname: ${MAIL_HOSTNAME:-mailhub.local} volumes: - ./data/postfix-logs:/var/log/mailhub networks: - mailhub healthcheck: test: ["CMD-SHELL", "postfix status >/dev/null 2>&1 || exit 1"] interval: 30s timeout: 5s retries: 3 start_period: 20s secrets: dovecot_auth_secret: file: ./data/secrets/dovecot_auth_secret networks: mailhub: dovecot_internal: internal: true dovecot_public: