Skip to content

Instantly share code, notes, and snippets.

@prem-prakash
Last active December 20, 2025 01:30
Show Gist options
  • Select an option

  • Save prem-prakash/17359f8a097f5bb31045891c2c4b80ec to your computer and use it in GitHub Desktop.

Select an option

Save prem-prakash/17359f8a097f5bb31045891c2c4b80ec to your computer and use it in GitHub Desktop.
Stalwart in Coolify
services:
stalwart:
image: 'stalwartlabs/stalwart:latest'
container_name: stalwart
networks:
- coolify
ports:
- '25:25'
- '587:587'
- '465:465'
- '143:143'
- '993:993'
- '4190:4190'
- '110:110'
- '995:995'
volumes:
- 'stalwart_data:/opt/stalwart'
- '/etc/localtime:/etc/localtime:ro'
restart: unless-stopped
labels:
- traefik.enable=true
- traefik.http.routers.stalwart-admin.rule=Host(`admin-mail.YOUR_DOMAIN.com`)
- traefik.http.routers.stalwart-admin.entrypoints=https
- traefik.http.routers.stalwart-admin.tls=true
- traefik.http.services.stalwart-admin.loadbalancer.server.port=8080
volumes:
stalwart_data: null
networks:
coolify:
external: true
authentication.fallback-admin.secret = "XXXXX"
authentication.fallback-admin.user = "admin"
directory.internal.store = "rocksdb"
directory.internal.type = "internal"
server.hostname = "mail.YOUR_DOMAIN.com"
server.listener.http.bind = "[::]:8080"
server.listener.http.protocol = "http"
server.listener.https.bind = "[::]:443"
server.listener.https.protocol = "http"
server.listener.https.tls.implicit = true
server.listener.imap.bind = "[::]:143"
server.listener.imap.protocol = "imap"
server.listener.imap.proxy.override = false
server.listener.imap.socket.override = false
server.listener.imap.tls.implicit = false
server.listener.imap.tls.override = false
server.listener.imaptls.bind = "[::]:993"
server.listener.imaptls.protocol = "imap"
server.listener.imaptls.tls.implicit = true
server.listener.pop3.bind = "[::]:110"
server.listener.pop3.protocol = "pop3"
server.listener.pop3s.bind = "[::]:995"
server.listener.pop3s.protocol = "pop3"
server.listener.pop3s.tls.implicit = true
server.listener.sieve.bind = "[::]:4190"
server.listener.sieve.protocol = "managesieve"
server.listener.smtp.bind = "[::]:25"
server.listener.smtp.protocol = "smtp"
server.listener.submission.bind = "[::]:587"
server.listener.submission.protocol = "smtp"
server.listener.submission.proxy.override = false
server.listener.submission.socket.override = false
server.listener.submission.tls.implicit = false
server.listener.submission.tls.override = false
server.listener.submissions.bind = "[::]:465"
server.listener.submissions.protocol = "smtp"
server.listener.submissions.tls.implicit = true
server.max-connections = 8192
server.socket.backlog = 1024
server.socket.nodelay = true
server.socket.reuse-addr = true
server.socket.reuse-port = true
storage.blob = "rocksdb"
storage.data = "rocksdb"
storage.directory = "internal"
storage.fts = "rocksdb"
storage.lookup = "rocksdb"
store.rocksdb.compression = "lz4"
store.rocksdb.path = "/opt/stalwart/data"
store.rocksdb.type = "rocksdb"
tracer.log.ansi = false
tracer.log.enable = true
tracer.log.level = "info"
tracer.log.path = "/opt/stalwart/logs"
tracer.log.prefix = "stalwart.log"
tracer.log.rotate = "daily"
tracer.log.type = "log"
# this below will output logs to the docker output
[tracer.console]
enable = true
type = "console"
level = "trace"
ansi = false
# configure acme provider
# Instead of adding this to the config I did this manually in the interface
# In order to reload generated certificates I needed to run:
# # stalwart-cli -u https://admin-mail.YOUR_DOMAIN.com server reload-certificates
[acme."letsencrypt"]
directory = "https://acme-v02.api.letsencrypt.org/directory"
challenge = "dns-01"
provider = "cloudflare"
secret = "YOUR_CLOUDFLARE_TOKEN"
domains = ["*.YOUR_DOMAIN.com", "YOUR_DOMAIN.com"] # wildcard covers mail/admin-mail
cache = "%{BASE_PATH}%/etc/acme-letsencrypt"
renew-before = "30d"
default = true
In `Domains` in the service config put: https://admin-mail.YOUR_DOMAIN.com:8080
In Stalwart you need to configure an ACME Provider (Server -> TLS -> Acme Providers)
- directory id: any name
- challenge type: dns-01
- contact email: any working email
- subject name: mail.YOUR_DOMAIN.com
- DNS Provider: Cloudflare
- Secret: Cloudflare API Token Scoped to your domain YOUR_DOMAIN.com, with Edit DNS
After changing this you usually will need to restart the docker service and run:
# stalwart-cli -u https://admin-mail.YOUR_DOMAIN.com server reload-certificates
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment