# Real mail on the internet (v0.2.3 75.6): receives mail for MAIL_DOMAINS and sends it straight to the recipients' # MX servers (port 25), DKIM-signed. From the ready image, like docker-compose.image.yml. # # Needs a VM of its own with a public address (Jambotron 60.8: «Публичный адрес» of the VM, SMTP «да» — port 25 # is closed by default) — the ports below are published on that address: # 25 SMTP: mail from other servers (STARTTLS) and from mail programs # 587 submission: mail programs (STARTTLS + login) — the same SMTP listener as 25 # 465 SMTPS: mail programs, implicit TLS # 993 IMAPS: mail programs, implicit TLS # 80 only for Let's Encrypt (HTTP-01) — service "acme" # The web client + API (service "mail", port 8080) goes through Jambotron's Caddy under ANOTHER name # (webmail.): the name MAIL_HOSTNAME points at the VM's public address, not at Caddy. # # DNS before the first start (the certificate is issued over HTTP on MAIL_HOSTNAME): # A mail. → public address of the VM # PTR public address → mail. (hoster's panel: Reverse DNS) # MX → 10 mail. # TXT → "v=spf1 ip4: -all" # TXT _dmarc. → "v=DMARC1; p=none; rua=mailto:postmaster@" # TXT mail._domainkey. → printed in the "mail" log on the first start (DKIM key, kept in the data volume) # # Mailbox admin@: its password is in the "mail" log on the first start and in /app/data/initial-passwords.txt. # Registration is closed: new mailboxes only by the admin. No catch-all. name: reactive-mail-public services: secrets-init: image: alpine:3.20 restart: "no" command: - sh - -c - | umask 077 for name in DB_PASSWORD JWT_SECRET; do [ -s /secrets/$$name ] || head -c 48 /dev/urandom | base64 | tr -dc 'A-Za-z0-9' > /secrets/$$name done chmod 644 /secrets/DB_PASSWORD /secrets/JWT_SECRET volumes: - secrets:/secrets # Let's Encrypt for MAIL_HOSTNAME: issued on the first start, renewed 30 days before the end (checked twice a day). # The deploy hook copies the pair to /tls (readable by the "mail" user 10001); the server reads it again within # a minute — no restart acme: image: goacme/lego:v5.5.2 restart: unless-stopped ports: - "80:80" entrypoint: - sh - -c - | cat > /usr/local/bin/jb-deploy <<'EOF' #!/bin/sh set -e cp "$$LEGO_HOOK_CERT_PATH" /tls/cert.pem.new cp "$$LEGO_HOOK_CERT_KEY_PATH" /tls/key.pem.new chown 10001 /tls/cert.pem.new /tls/key.pem.new chmod 600 /tls/key.pem.new chmod 644 /tls/cert.pem.new mv /tls/key.pem.new /tls/key.pem mv /tls/cert.pem.new /tls/cert.pem echo "certificate for $$LEGO_HOOK_CERT_DOMAINS copied to /tls" EOF chmod 755 /usr/local/bin/jb-deploy while true; do /lego run --accept-tos --email "$$ACME_EMAIL" --domains "$$MAIL_HOSTNAME" --key-type EC256 \ --http --http.address :80 --path /var/lib/lego --renew-days 30 --no-random-sleep \ --deploy-hook /usr/local/bin/jb-deploy || echo "lego failed — next try in 1 hour" [ -s /tls/cert.pem ] && sleep 43200 || sleep 3600 done environment: MAIL_HOSTNAME: ${MAIL_HOSTNAME:?MAIL_HOSTNAME, e.g. mail.jambocheck.com} ACME_EMAIL: ${ACME_EMAIL:-postmaster@${MAIL_DOMAINS}} LEGO_LOG_FORMAT: text volumes: - lego:/var/lib/lego - tls:/tls healthcheck: test: ["CMD-SHELL", "test -s /tls/cert.pem && test -s /tls/key.pem"] interval: 15s timeout: 5s start_period: 10m retries: 3 mem_limit: 128m db: image: postgres:17 restart: unless-stopped depends_on: secrets-init: condition: service_completed_successfully environment: POSTGRES_DB: mailserver POSTGRES_USER: mail POSTGRES_PASSWORD_FILE: /run/mail-secrets/DB_PASSWORD volumes: - pgdata:/var/lib/postgresql/data - secrets:/run/mail-secrets:ro healthcheck: test: ["CMD-SHELL", "pg_isready -U mail -d mailserver"] interval: 10s timeout: 5s retries: 10 mem_limit: 256m mail: image: gitea.jambotron.com/jambotron_gang/reactive-mail-server:${MAIL_IMAGE_TAG:-latest} pull_policy: always restart: unless-stopped depends_on: db: condition: service_healthy acme: condition: service_healthy # waits for the first certificate ports: - "25:3025" - "587:3025" - "465:3465" - "993:3993" expose: - "8080" # web client + API → webmail. in Jambotron environment: DB_HOST: db MAILSERVER_SECRETS_DIR: /run/mail-secrets MAIL_DOMAINS: ${MAIL_DOMAINS:?MAIL_DOMAINS, e.g. jambocheck.com} MAIL_HOSTNAME: ${MAIL_HOSTNAME:?MAIL_HOSTNAME, e.g. mail.jambocheck.com} MAIL_TLS_CERT: /tls/cert.pem MAIL_TLS_KEY: /tls/key.pem SMTPS_PORT: "3465" IMAPS_PORT: "3993" DKIM_DOMAIN: ${MAIL_DOMAINS} DKIM_SELECTOR: mail DKIM_KEYS_DIR: /app/data/dkim-keys MAIL_CATCH_ALL: "" MAIL_REGISTRATION: "false" OUTBOUND_MODE: mx SMTP_AUTH_WITHOUT_TLS: "false" IMAP_AUTH_WITHOUT_TLS: "false" SPF_ENABLED: "true" DMARC_ENABLED: "true" volumes: - data:/app/data - secrets:/run/mail-secrets:ro - tls:/tls:ro mem_limit: 512m volumes: secrets: pgdata: data: lego: tls: