Running Mailu mail server Behind Traefik, Unable to Send Emails via mail clients (Thunderbird...)

Hello :waving_hand:

Im running Mailu behind Traefik v3.3, sending and receiving mails via the webmail service works with no issue, additionally i've configured Thunderbird to connect to the servers IMAP and SMTPS (port 993/465).

Once Authenticated, the sent/inbox folders are retrieved with all the data, and receiving mails seems to be working, but sending mails doesn't work from Thunderbird, it keeps loading with no clear errors in logs.

This is my configuration files:

  • Traefik compose file:
services:
  reverse-proxy:
    image: traefik:v3.3
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
      - "25:25"
      - "465:465"
      - "587:587"
      - "110:110"
      - "995:995"
      - "143:143"
      - "993:993"
      - "4190:4190"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./traefik.yaml:/etc/traefik/traefik.yaml
      - ./letsencrypt:/letsencrypt
    networks:
      - traefik-net

networks:
  traefik-net:
    external: true
  • Traefik static configuration :
global:
  checkNewVersion: false
  sendAnonymousUsage: false
log:
  level: DEBUG
  format: common
accessLog:
  format: common
  filePath: /var/log/traefik/access.log
api:
  dashboard: true
  insecure: true
entryPoints:
  web:
    address: :80
    # Redirect all HTTP to HTTPS
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https
  websecure:
    address: :443
  imaps:
    address: :993
  imap:
    address: :143
  pop3s:
    address: :995
  smtps:
    address: :465
  smtp:
    address: :587
  sieve:
    address: :4190
  smtp-relay:
    address: :25
providers:
  docker:
    exposedByDefault: false
  file:
    directory: /etc/traefik
    watch: true
certificatesResolvers:
  le:
    acme:
      email: user@gmail.com
      storage: /letsencrypt/acme.json
      httpChallenge:
        entryPoint: web
  • Mailu front service:
  front:
    image: ${DOCKER_ORG:-ghcr.io/mailu}/${DOCKER_PREFIX:-}nginx:${MAILU_VERSION:-2024.06}
    restart: always
    env_file: mailu.env
    logging:
      driver: journald
      options:
        tag: mailu-front
    networks:
      - default
      - webmail
      - traefik-net
    volumes:
      - "/mailu/certs:/certs"

    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=traefik-net"

      - "traefik.http.routers.mail.rule=Host(`mail.mydomain.org`)"
      - "traefik.http.routers.mail.entrypoints=websecure"
      - "traefik.http.routers.mail.tls=true"
      - "traefik.http.routers.mail.tls.certresolver=le"
      - "traefik.http.services.mail.loadbalancer.server.port=80"

      # IMAPS (993)
      - "traefik.tcp.routers.mail-imaps.rule=HostSNI(`*`)"
      - "traefik.tcp.routers.mail-imaps.entrypoints=imaps"
      - "traefik.tcp.routers.mail-imaps.tls.passthrough=true"
      - "traefik.tcp.routers.mail-imaps.service=mail-imaps"
      - "traefik.tcp.services.mail-imaps.loadbalancer.server.port=993"

      # IMAP (143)
      - "traefik.tcp.routers.mail-imap.rule=HostSNI(`*`)"
      - "traefik.tcp.routers.mail-imap.entrypoints=imap"
      - "traefik.tcp.routers.mail-imap.service=mail-imap"
      - "traefik.tcp.services.mail-imap.loadbalancer.server.port=143"

      # POP3S (995)
      - "traefik.tcp.routers.mail-pop3s.rule=HostSNI(`*`)"
      - "traefik.tcp.routers.mail-pop3s.entrypoints=pop3s"
      - "traefik.tcp.routers.mail-pop3s.tls.passthrough=true"
      - "traefik.tcp.routers.mail-pop3s.service=mail-pop3s"
      - "traefik.tcp.services.mail-pop3s.loadbalancer.server.port=995"

      # relay SMTP (25)
      - "traefik.tcp.routers.smtp-insecure.rule=HostSNI(`*`)"
      - "traefik.tcp.routers.smtp-insecure.entrypoints=smtp-relay"
      - "traefik.tcp.routers.smtp-insecure.service=smtp-insecure"
      - "traefik.tcp.services.smtp-insecure.loadbalancer.server.port=25"

      # SMTPS (465)
      - "traefik.tcp.routers.mail-smtps.rule=HostSNI(`*`)"
      - "traefik.tcp.routers.mail-smtps.entrypoints=smtps"
      - "traefik.tcp.routers.mail-smtps.tls.passthrough=true"
      - "traefik.tcp.routers.mail-smtps.service=mail-smtps"
      - "traefik.tcp.services.mail-smtps.loadbalancer.server.port=465"

      # SMTP Submission (587)
      - "traefik.tcp.routers.mail-smtp.rule=HostSNI(`*`)"
      - "traefik.tcp.routers.mail-smtp.entrypoints=smtp"
      - "traefik.tcp.routers.mail-smtp.service=mail-smtp"
      - "traefik.tcp.services.mail-smtp.loadbalancer.server.port=587"

      # SIEVE (4190)
      - "traefik.tcp.routers.mail-sieve.rule=HostSNI(`*`)"
      - "traefik.tcp.routers.mail-sieve.entrypoints=sieve"
      - "traefik.tcp.routers.mail-sieve.service=mail-sieve"
      - "traefik.tcp.services.mail-sieve.loadbalancer.server.port=4190"
      
    depends_on:
      - resolver
    dns:
      - 192.168.203.254
  • mailu.env:
TLS_FLAVOR=mail-letsencrypt

  • Connecting with openssl command to verify the certificate on port 465 seem to have the right one:
openssl s_client -connect mail.mydomain.org:465 -4

CONNECTED(00000003)
depth=2 C = US, O = Internet Security Research Group, CN = ISRG Root X1
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = E5
verify return:1
depth=0 CN = mail.mydomain.org
verify return:1
---
Certificate chain
 0 s:CN = mail.mydomain.org
   i:C = US, O = Let's Encrypt, CN = E5
   a:PKEY: id-ecPublicKey, 256 (bit); sigalg: ecdsa-with-SHA384
   v:NotBefore: Mar 11 06:14:12 2025 GMT; NotAfter: Jun  9 06:14:11 2025 GMT
 1 s:C = US, O = Let's Encrypt, CN = E5
   i:C = US, O = Internet Security Research Group, CN = ISRG Root X1
   a:PKEY: id-ecPublicKey, 384 (bit); sigalg: RSA-SHA256
   v:NotBefore: Mar 13 00:00:00 2024 GMT; NotAfter: Mar 12 23:59:59 2027 GMT
 2 s:C = US, O = Internet Security Research Group, CN = ISRG Root X1
   i:C = US, O = Internet Security Research Group, CN = ISRG Root X1
   a:PKEY: rsaEncryption, 4096 (bit); sigalg: RSA-SHA256
   v:NotBefore: Jun  4 11:04:38 2015 GMT; NotAfter: Jun  4 11:04:38 2035 GMT
...
...

I've tried the solution provided in Mailu docs, but no luck on making things works, the closest working configuration to our needs is the one above.

I appreciate any help or guidance on fixing this problem :folded_hands:

Don't use this. If it's a TCP router without TLS, Traefik will just pass the encrypted traffic through anyway. So it's not needed and rather does the opposite, it activates Traefik TLS handling, which will usually create a custom TLS cert, something you don't want.

Without any error message it's hard to debug, enable and check Traefik debug log.

We use mailu for some transactional emails, but it run's on it's own cloud server, direct port access, without reverse proxy in between.

@bluepuma77 I will remove it since Traefik pass the encrypted traffic anyway, i added it because i wanted mailu to handle the TCP certificates (That's what TLS_FLAVOR=mail-letsencrypt supposed to do, TCP certs managed by mailu and HTTP certs will be managed by Traefik) so i didn't want Traefik to interfere with that, Thanks for the tip.

i did try another solution (in this GitHub Ticket) where it uses TLS_FLAVOR=mail and passes the certificate dumped from Traefik to mailu to use it, the solution worked but still got the same problem, sending mails doesn't work

In our case we needed a reverse proxy by domain because we have multiple services requiring HTTP and HTTPS ports to run on same server, so far we almost reached our goal if it wasn't the outgoing problem.

I will check again and provide any logs that could help fixing it.

Just an idea: if you want multiple web services, but only one mail service, just use a reverse proxy for ports 80/443, have other mail ports open directly on host.

Update: Ah, but then you probably get an issue with mailu creating certificates.

exactly, mailu need the HTTP ports for certificate.
I just checked Traefik logs and there was some errors related to SMTP port 465 :

DBG github.com/traefik/traefik/v3/pkg/tcp/proxy.go:41 > Handling TCP connection address=172.19.0.2:465 remoteAddr=SERVER_PUBLIC_IP:47190
2025-03-12T09:05:50Z DBG github.com/traefik/traefik/v3/pkg/tcp/proxy.go:104 > Error while terminating TCP connection error="close tcp 172.19.0.3:465->SERVER_PUBLIC_IP:47190: use of closed network connection"

PS: 172.19.0.2 => mailu front internal IP, 172.19.0.3 => Traefik internal IP

Update: After removing the passthrough label, sending mails is working from Thunderbird :smiley:, but the above error is still occuring, should i be concerned?

Thank you @bluepuma77 for the guidance :folded_hands:
The final setup to run multiple services (mailu + app) in same server via Traefik is the following (in case someone looking for similar setup):

  • first make sure to create a shared docker network :
docker network create traefik-net
  • Traefik compose file:
services:
  reverse-proxy:
    image: traefik:v3.3
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
      - "25:25"
      - "465:465"
      - "587:587"
      - "110:110"
      - "995:995"
      - "143:143"
      - "993:993"
      - "4190:4190"

    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./traefik.yaml:/etc/traefik/traefik.yaml
      - ./dynamic_conf.yaml:/etc/traefik/dynamic_conf.yaml
      - ./letsencrypt:/letsencrypt
    networks:
      - traefik-net

networks:
  traefik-net:
    external: true
  • Mailu front service:
  front:
    image: ${DOCKER_ORG:-ghcr.io/mailu}/${DOCKER_PREFIX:-}nginx:${MAILU_VERSION:-2024.06}
    restart: always
    env_file: mailu.env
    logging:
      driver: journald
      options:
        tag: mailu-front
    networks:
      - default
      - webmail
      - traefik-net
    volumes:
      - "/mailu/certs:/certs"

    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=traefik-net"

      # Dashboard mailu
      - "traefik.http.routers.mail.rule=Host(`mail.mydomain.org`)"
      - "traefik.http.routers.mail.entrypoints=websecure"
      - "traefik.http.routers.mail.tls=true"
      - "traefik.http.routers.mail.tls.certresolver=le"
      - "traefik.http.services.mail.loadbalancer.server.port=80"


      # IMAPS (993)
      - "traefik.tcp.routers.mail-imaps.rule=HostSNI(`*`)"
      - "traefik.tcp.routers.mail-imaps.entrypoints=imaps"
      - "traefik.tcp.routers.mail-imaps.service=mail-imaps"
      - "traefik.tcp.services.mail-imaps.loadbalancer.server.port=993"


      # IMAPS (143)
      - "traefik.tcp.routers.mail-imap.rule=HostSNI(`*`)"
      - "traefik.tcp.routers.mail-imap.entrypoints=imap"
      - "traefik.tcp.routers.mail-imap.service=mail-imap"
      - "traefik.tcp.services.mail-imap.loadbalancer.server.port=143"

      # POP3S (995)
      - "traefik.tcp.routers.mail-pop3s.rule=HostSNI(`*`)"
      - "traefik.tcp.routers.mail-pop3s.entrypoints=pop3s"
      - "traefik.tcp.routers.mail-pop3s.service=mail-pop3s"
      - "traefik.tcp.services.mail-pop3s.loadbalancer.server.port=995"

      # SMTP Relay (25)
      - "traefik.tcp.routers.smtp-insecure.rule=HostSNI(`*`)"
      - "traefik.tcp.routers.smtp-insecure.entrypoints=smtp-relay"
      - "traefik.tcp.routers.smtp-insecure.service=smtp-insecure"
      - "traefik.tcp.services.smtp-insecure.loadbalancer.server.port=25"

      # SMTPS (465)
      - "traefik.tcp.routers.mail-smtps.rule=HostSNI(`*`)"
      - "traefik.tcp.routers.mail-smtps.entrypoints=smtps"
      - "traefik.tcp.routers.mail-smtps.service=mail-smtps"
      - "traefik.tcp.services.mail-smtps.loadbalancer.server.port=465"

      # SMTP Submission (587)
      - "traefik.tcp.routers.mail-smtp.rule=HostSNI(`*`)"
      - "traefik.tcp.routers.mail-smtp.entrypoints=smtp"
      - "traefik.tcp.routers.mail-smtp.service=mail-smtp"
      - "traefik.tcp.services.mail-smtp.loadbalancer.server.port=587"

      # SIEVE (4190)
      - "traefik.tcp.routers.mail-sieve.rule=HostSNI(`*`)"
      - "traefik.tcp.routers.mail-sieve.entrypoints=sieve"
      - "traefik.tcp.routers.mail-sieve.service=mail-sieve"
      - "traefik.tcp.services.mail-sieve.loadbalancer.server.port=4190"
  • mailu.env:
TLS_FLAVOR=mail-letsencrypt
  • Traefik static configuration (Traefik.yml): UNCHANGED

  • Traefik dynamic configuration (dynamic_conf.yml) (to reverse proxy an app running on Host machine):

http:
  routers:
    app:
      rule: "Host(`app.domain.org`)"
      entryPoints:
        - websecure
      tls:
        certResolver: le
      service: app

  services:
    app:
      loadBalancer:
        servers:
          - url: "http://app.domain.org:4443"
        passHostHeader: true

If you are wondering why we are exposing the app publicly on port 4443, the reason is we couldn't figure out how to configure Traefik to reach the host network and mailu docker network at same time, we tried network_mode: host but it complicated things for mailu, to gain time we just exposed the app publicly on port 4443 and made it accessible only by traefik-net network.

you can do that by running docker network inspect traefik-net, copy subnet address e.g. 172.18.0.0/16

and add the following lines to you nginx conf (or equivalent for other webservers):


server {
        # Allow Docker network subnet (traefik-net) and deny the rest
        allow 172.1.0.0/16;  
        deny all;
...
... 
...
}
1 Like