# Stalwart Mail Server Configuration # Generated by Puppet - DO NOT EDIT MANUALLY [server] hostname = "<%= $node_facts['networking']['fqdn'] %>" greeting = "Stalwart ESMTP" [server.listener."smtp-relay"] bind = ["<%= $bind_address %>:25"] protocol = "smtp" greeting = "Stalwart SMTP Relay" <% if $enable_imap { -%> [server.listener."imap"] bind = ["<%= $bind_address %>:143"] protocol = "imap" <% } -%> <% if $enable_imap_tls { -%> [server.listener."imaps"] bind = ["<%= $bind_address %>:993"] protocol = "imap" tls.implicit = true <% } -%> <% if $enable_http { -%> [server.listener."https"] bind = ["<%= $bind_address %>:443"] protocol = "http" tls.implicit = true <% } -%> [server.tls] enable = true implicit = false certificate = "default" [webadmin] path = "<%= $webadmin_unpack_path %>" auto-update = <%= $webadmin_auto_update %> resource = "<%= $webadmin_resource_url %>" # Cluster Configuration [cluster] node-id = <%= $effective_node_id %> <% if $cluster_size > 1 { -%> # Peer-to-peer coordination [cluster.coordinator] type = "peer-to-peer" addr = "<%= $bind_address %>:11200" advertise-addr = "<%= $advertise_address %>:11200" <% $other_cluster_members.each |$node| { -%> [[cluster.coordinator.peers]] addr = "<%= $node %>:11200" <% } -%> # Cluster roles for 3-node setup [cluster.roles.purge] stores = ["1", "2", "3"] accounts = ["1", "2"] [cluster.roles.acme] renew = ["1"] [cluster.roles.metrics] calculate = ["1", "2"] push = ["1"] [cluster.roles.push-notifications] push-notifications = ["1", "3"] [cluster.roles.fts-indexing] fts-indexing = ["2", "3"] [cluster.roles.bayes-training] bayes-training = ["1"] [cluster.roles.imip-processing] imip-processing = ["2"] [cluster.roles.calendar-alerts] calendar-alerts = ["3"] <% } -%> # Storage Configuration # PostgreSQL store for data, FTS, and in-memory [store."postgresql"] type = "postgresql" host = "<%= $postgresql_host %>" port = <%= $postgresql_port %> database = "<%= $postgresql_database %>" user = "<%= $postgresql_user %>" password = "<%= $postgresql_password %>" timeout = "15s" [store."postgresql".tls] enable = <%= $postgresql_ssl %> allow-invalid-certs = false [store."postgresql".pool] max-connections = 10 [store."postgresql".purge] frequency = "0 3 *" # S3/Ceph-RGW store for blobs [store."s3"] type = "s3" bucket = "<%= $s3_bucket %>" region = "<%= $s3_region %>" access-key = "<%= $s3_access_key %>" secret-key = "<%= $s3_secret_key %>" endpoint = "<%= $s3_endpoint %>" timeout = "30s" key-prefix = "<%= $s3_key_prefix %>" compression = "lz4" [store."s3".purge] frequency = "30 5 *" # Storage assignment [storage] data = "postgresql" fts = "postgresql" blob = "s3" lookup = "postgresql" directory = "internal" in-memory = "postgresql" # Directory configuration [directory.internal] type = "internal" store = "postgresql" # Authentication configuration [authentication.fallback-admin] user = "<%= $fallback_admin_user %>" secret = "<%= pw_hash($fallback_admin_password.unwrap, 'SHA-512', 'stalwart') %>" [authentication] [authentication.directory] directories = ["internal"] # Authorization configuration [authorization] directory = "internal" # JMAP configuration [jmap] directory = "internal" [jmap.protocol] request-max-size = 10485760 get.max-objects = 500 query.max-results = 5000 changes.max-results = 5000 upload.max-size = 50000000 upload.ttl = "1h" # IMAP configuration [imap] directory = "internal" [imap.protocol] max-requests = 64 # SMTP configuration for postfix relay [session.data] pipe.command = "sendmail" pipe.arguments = ["-i", "-f", "{sender}", "{recipient}"] # Outbound SMTP configuration [queue] path = "<%= $data_dir %>/queue" [queue.schedule] retry = ["2s", "5s", "1m", "5m", "15m", "30m", "1h", "2h"] notify = ["1d", "3d"] expire = "5d" [session.extensions] future-release = "7d" # Relay configuration for postfix [remote."postfix"] address = "<%= $postfix_relay_host %>" port = 25 protocol = "smtp" # HTTP configuration [server.http] use-x-forwarded = false permissive-cors = false # Disable spam filtering (handled by postfix) [session.ehlo] reject-non-fqdn = false [session.rcpt] type = "internal" store = "postgresql" max-recipients = 25 [session.data] max-messages = 10 max-message-size = 52428800 # TLS configuration [certificate."default"] cert = "%{file:<%= $tls_cert %>}%" private-key = "%{file:<%= $tls_key %>}%" # Logging configuration [tracer] type = "log" level = "<%= $log_level %>" ansi = false multiline = true [tracer.file] path = "/var/log/stalwart/stalwart.log" rotate = "daily" keep = 30 # Report storage [report] path = "<%= $data_dir %>/reports" hash = "sha256" encrypt = false # Metrics configuration [metrics] prometheus.enable = true prometheus.port = 9090 # Queue routing configuration [queue.strategy] route = [ { if = "is_local_domain('', rcpt_domain)", then = "'local'" }, { else = "'relay'" } ] [queue.route."local"] type = "local" [queue.route."relay"] type = "relay" address = "<%= $postfix_relay_host %>" port = 25 protocol = "smtp" [queue.route."relay".tls] implicit = false allow-invalid-certs = false