spring.application.name=mail-server server.port=8080 # behind a proxy (Jambotron Caddy): take scheme/host from X-Forwarded-*, otherwise the browser's own # domain looks like another origin to CORS and every API call with an Origin header gets 403 server.forward-headers-strategy=framework # Secrets: environment variables, or files in a config tree (file name = variable name, e.g. /run/mail-secrets/DB_PASSWORD). # No defaults — the server does not start without them (dev profile: application-dev.properties). spring.config.import=optional:configtree:${MAILSERVER_SECRETS_DIR:/run/mail-secrets}/ # PostgreSQL: R2DBC for the app, JDBC for Flyway migrations spring.r2dbc.url=r2dbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:mailserver} spring.r2dbc.username=${DB_USER:mail} spring.r2dbc.password=${DB_PASSWORD} spring.flyway.enabled=true spring.flyway.url=jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:mailserver} spring.flyway.user=${DB_USER:mail} spring.flyway.password=${DB_PASSWORD} spring.flyway.locations=classpath:db/migration spring.flyway.baseline-on-migrate=true jwt.secret=${JWT_SECRET} # Mailboxes of these domains live here; catch-all: mail to any address without its own mailbox here goes into this one mailbox (mail trap), nothing goes outside mailserver.domains=${MAIL_DOMAINS:localhost} mailserver.catch-all=${MAIL_CATCH_ALL:} mailserver.registration.enabled=${MAIL_REGISTRATION:false} mailserver.admin.email=${MAIL_ADMIN_EMAIL:} mailserver.admin.password=${MAIL_ADMIN_PASSWORD:} # Who manages mailboxes (Settings → Mailboxes), comma separated; empty — the first mailbox (admin.email or admin@) mailserver.admins=${MAIL_ADMINS:} # Generated passwords of created mailboxes (first admin, catch-all) are appended here (mode 600); empty — printed to the log once mailserver.passwords-file=${MAIL_PASSWORDS_FILE:} mailserver.cors.origins=${MAIL_CORS_ORIGINS:} # Attachments mailserver.attachments.dir=${MAIL_ATTACHMENTS_DIR:./attachments} spring.webflux.multipart.max-file-size=10MB spring.webflux.multipart.max-request-size=10MB # SMTP server (incoming mail and submission). AUTH without TLS only in a closed network (docker network, VM) mailserver.smtp.port=${SMTP_PORT:3025} mailserver.smtp.hostname=${MAIL_HOSTNAME:localhost} mailserver.smtp.auth-without-tls=${SMTP_AUTH_WITHOUT_TLS:false} mailserver.smtp.max-message-bytes=26214400 # TLS (75.1): STARTTLS on the SMTP port; implicit TLS on SMTPS_PORT (465) if set. Certificate: MAIL_TLS_CERT + MAIL_TLS_KEY # (PEM, PKCS#8 key), otherwise a self-signed one created once in MAIL_TLS_DIR. MAIL_TLS=false — no TLS mailserver.tls.enabled=${MAIL_TLS:true} mailserver.tls.cert-file=${MAIL_TLS_CERT:} mailserver.tls.key-file=${MAIL_TLS_KEY:} mailserver.tls.dir=${MAIL_TLS_DIR:./tls} # Web client + API over HTTPS with the same certificate (public compose: 8443 → 443); 0 — only plain HTTP (server.port) mailserver.https.port=${HTTPS_PORT:0} mailserver.smtps.port=${SMTPS_PORT:0} # Outbound: off | mx (straight to recipients' MX, port 25) | smarthost (via a provider, e.g. Resend) mailserver.outbound.mode=${OUTBOUND_MODE:off} mailserver.outbound.host=${OUTBOUND_HOST:} mailserver.outbound.port=${OUTBOUND_PORT:587} mailserver.outbound.username=${OUTBOUND_USERNAME:} mailserver.outbound.password=${OUTBOUND_PASSWORD:} mailserver.outbound.tls=${OUTBOUND_TLS:starttls} # IMAP (75.2): STARTTLS on IMAP_PORT, implicit TLS on IMAPS_PORT (993) if set; same certificate as SMTP. # LOGIN only inside TLS unless IMAP_AUTH_WITHOUT_TLS=true (a closed network only) mailserver.imap.enabled=${IMAP_ENABLED:true} mailserver.imap.port=${IMAP_PORT:3143} mailserver.imaps.port=${IMAPS_PORT:0} mailserver.imap.auth-without-tls=${IMAP_AUTH_WITHOUT_TLS:false} # DKIM — signing of our mail; DNS record printed to the log at start mailserver.dkim.enabled=${DKIM_ENABLED:true} mailserver.dkim.domain=${DKIM_DOMAIN:localhost} mailserver.dkim.selector=${DKIM_SELECTOR:mail} mailserver.dkim.keys-dir=${DKIM_KEYS_DIR:./dkim-keys} # SPF / DMARC for incoming mail mailserver.spf.enabled=${SPF_ENABLED:true} mailserver.spf.local-bypass=true mailserver.dmarc.enabled=${DMARC_ENABLED:true} mailserver.dmarc.local-bypass=true # error responses carry the reason ("No such mailbox", "Sending outside is disabled") for the web client server.error.include-message=always management.endpoints.web.exposure.include=health management.endpoint.health.show-details=never logging.level.com.mailserver=INFO # Brute-force protection for SMTP AUTH, IMAP login, web login and password change (one counter for all of them): # MAX_PER_IP wrong passwords from one IP within WINDOW → the IP is blocked for BLOCK (each next block twice as long, # up to MAX_BLOCK); MAX_PER_ACCOUNT wrong passwords to one mailbox from any IPs → the mailbox is blocked for BLOCK, # except for IPs that logged in to it before. TRUSTED_IPS (comma-separated) are never blocked by IP mailserver.login-guard.enabled=${LOGIN_GUARD:true} mailserver.login-guard.max-per-ip=${LOGIN_GUARD_MAX_PER_IP:10} mailserver.login-guard.max-per-account=${LOGIN_GUARD_MAX_PER_ACCOUNT:20} mailserver.login-guard.window=${LOGIN_GUARD_WINDOW:15m} mailserver.login-guard.block=${LOGIN_GUARD_BLOCK:15m} mailserver.login-guard.max-block=${LOGIN_GUARD_MAX_BLOCK:24h} mailserver.login-guard.trusted-ips=${LOGIN_GUARD_TRUSTED_IPS:}