Stalwart WebUI Bad Gateway

Issue Description

Webinterface not available, I get a Bad Gateway error message when I try to view https://mail.kuhnwebservices.de

Expected Behavior

A login page should appear

Actual Behavior

Bad Gateway

Reproduction Steps

not available

Relevant Log Output

nothing logged

Stalwart Version

v0.15.x

Installation Method

Docker

Database Backend

RocksDB

Blob Storage

RocksDB

Search Engine

Internal

Directory Backend

Internal

Additional Context

The mailserver is running as usual. Only the webinterface is not available. It is running through traefik. Interesting is, that nothing has changed, except a server reboot. I do not know since whe this error persists, I do not enter the WebUI often. As I am pretty new I am not sure what information could be neccessary.

My traefik configuration:

labels:
      - "traefik.enable=true"

 # Web UI (HTTPS) for mail.kuhnwebservices.de
  - "traefik.http.routers.stalwart-mail.rule=Host(`mail.kuhnwebservices.de`)"
  - "traefik.http.routers.stalwart-mail.entrypoints=websecure"
  - "traefik.http.routers.stalwart-mail.tls.certresolver=le"
  - "traefik.http.routers.stalwart-mail.service=stalwart-mail"
  - "traefik.http.services.stalwart-mail.loadbalancer.server.port=8080"

  # Autodiscover
  - "traefik.http.routers.stalwart-autodiscover.rule=Host(`autodiscover.kuhnwebservices.de`)"
  - "traefik.http.routers.stalwart-autodiscover.entrypoints=websecure"
  - "traefik.http.routers.stalwart-autodiscover.tls.certresolver=le"
  - "traefik.http.routers.stalwart-autodiscover.service=stalwart-autodiscover"
  - "traefik.http.services.stalwart-autodiscover.loadbalancer.server.port=8080"

  # Autoconfig
  - "traefik.http.routers.stalwart-autoconfig.rule=Host(`autoconfig.kuhnwebservices.de`)"
  - "traefik.http.routers.stalwart-autoconfig.entrypoints=websecure"
  - "traefik.http.routers.stalwart-autoconfig.tls.certresolver=le"
  - "traefik.http.routers.stalwart-autoconfig.service=stalwart-autoconfig"
  - "traefik.http.services.stalwart-autoconfig.loadbalancer.server.port=8080"

  # MTA-STS
  - "traefik.http.routers.stalwart-mtasts.rule=Host(`mta-sts.kuhnwebservices.de`)"
  - "traefik.http.routers.stalwart-mtasts.entrypoints=websecure"
  - "traefik.http.routers.stalwart-mtasts.tls.certresolver=le"
  - "traefik.http.routers.stalwart-mtasts.service=stalwart-mtasts"
  - "traefik.http.services.stalwart-mtasts.loadbalancer.server.port=8080"

Thank you for your help.

I have reviewed the documentation and FAQ and confirm that my issue is NOT addressed there.

on

I have searched this support forum (open and closed topics) and confirm this is not a duplicate.

on

I understand that topics in this category are triaged by a bot first but a human reply will follow up. If I’d prefer a human-only reply, I’ll add the no-ai tag to my topic.

on

The “Bad Gateway” from Traefik means Traefik can reach your domain but cannot connect to the Stalwart backend. Since nothing changed except a server reboot, the most likely causes are:

  1. The Stalwart container is not running or not healthy. Check with docker ps – if the container exited or is restarting, that is your problem. Review its logs with docker logs <container_name>.

  2. The HTTP/HTTPS listener port inside Stalwart is not bound or is misconfigured. Traefik proxies to a specific port on the Stalwart container (commonly 8080 for the web admin UI). After a reboot, confirm the container started cleanly and is actually listening on that port.

  3. Traefik’s dynamic config still points to the old container IP/name. After a reboot Docker may reassign container IPs. If you use label-based Traefik config this is usually automatic, but verify the Traefik service/router labels on the Stalwart container are correct and that Traefik picked them up (docker logs <traefik_container>).

Troubleshooting steps to try:

  • docker ps -a – confirm the Stalwart container is Up (not Exited)
  • docker logs <stalwart_container> --tail 100 – look for startup errors
  • From inside the Docker network, curl http://<stalwart_container>:<port>/ – confirm Stalwart itself responds
  • Check Traefik’s dashboard or logs for the backend health of the Stalwart service

Since you are on v0.15.x, the WebUI is served by the Stalwart process itself on the configured HTTP listener port – there is no separate component. If the mail server is working (SMTP/IMAP), the process is running, but the HTTP listener may have failed to bind (e.g. port conflict after reboot) or Traefik’s routing to it broke.

A “Bad Gateway” error from Traefik almost always means Traefik can reach the Docker network but the Stalwart container is not listening on the port Traefik is trying to forward to. Since the mail server itself is working (SMTP/IMAP), the issue is specifically with the HTTP service that serves the WebUI.

Here are the most common causes and checks to work through:

1. Verify the Stalwart container is actually running and its HTTP port is up

docker ps | grep stalwart
docker logs stalwart --tail 50

Look for any errors on startup, especially around the HTTP listener binding.

2. Check the Traefik label for the correct backend port

According to the Stalwart docs for Traefik, the HTTP service label should point to port 8080:

- traefik.http.services.mailserver.loadbalancer.server.port=8080

Make sure your docker-compose.yml (or however your stack is defined) uses port 8080 as the backend port for the HTTP/WebUI service. If Stalwart’s internal HTTP listener was reconfigured to a different port, Traefik won’t be able to reach it.

3. Confirm the container is on the same Docker network as Traefik

After a server reboot, Docker networks sometimes need to be recreated, or containers may start in the wrong order. Verify the Stalwart container is attached to the same network Traefik uses:

docker network inspect traefik_proxy   # or whatever your proxy network is named

The Stalwart container should appear in the Containers list.

4. Check Traefik’s own dashboard/logs for the upstream health

docker logs traefik --tail 50

Traefik will often log exactly why it is returning 502 (e.g., “no healthy upstream”, “connection refused”).

5. After a reboot: restart order matters

If Traefik starts before Stalwart is ready, it may mark the backend as unhealthy. Try:

docker restart stalwart

and then re-test the WebUI.

The official Traefik + Stalwart setup guide is at: Traefik | Stalwart

This is an automated reply from the Stalwart Help Bot. Other community members may follow up if this answer is incomplete or wrong.

The error is back, in docker logs I see:

2026-06-11T09:43:35Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 37168
2026-06-11T09:43:36Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 46968
2026-06-11T09:43:38Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 46982
2026-06-11T09:43:40Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 46988
2026-06-11T09:43:41Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 47004
2026-06-11T09:43:44Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 47006
2026-06-11T09:43:52Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 46174
2026-06-11T09:43:53Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 46190
2026-06-11T09:43:54Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 46200
2026-06-11T09:43:56Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 37624
2026-06-11T09:44:00Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 37640
2026-06-11T09:44:05Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 37656
2026-06-11T09:44:12Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 46750
2026-06-11T09:44:14Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 46760
2026-06-11T09:44:31Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 45724
2026-06-11T09:44:38Z INFO Invalid EHLO command (smtp.invalid-ehlo) listenerId = “smtp”, localPort = 25, remoteIp = 62.60.130.14, remotePort = 62895, domain = “User”
2026-06-11T09:44:45Z INFO Invalid EHLO command (smtp.invalid-ehlo) listenerId = “smtp”, localPort = 25, remoteIp = 130.12.182.143, remotePort = 55846, domain = “User”
2026-06-11T09:44:55Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 42008
2026-06-11T09:44:56Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 50126
2026-06-11T09:45:03Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 50130
2026-06-11T09:45:35Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 57586
2026-06-11T09:45:36Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 46910
2026-06-11T09:46:15Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 33468
2026-06-11T09:47:26Z INFO SMTP EHLO command (smtp.ehlo) listenerId = “smtp”, localPort = 25, remoteIp = 193.32.162.71, remotePort = 61177, domain = “``kuhn-online.co.uk``”
2026-06-11T09:47:26Z INFO SPF EHLO check failed (smtp.spf-ehlo-fail) listenerId = “smtp”, localPort = 25, remoteIp = 193.32.162.71, remotePort = 61177, domain = “``kuhn-online.co.uk``”, result = SPF check failed (spf.fail), elapsed = 14ms
2026-06-11T09:47:26Z INFO Authentication not allowed (smtp.auth-not-allowed) listenerId = “smtp”, localPort = 25, remoteIp = 193.32.162.71, remotePort = 61177
2026-06-11T09:50:00Z INFO Blocked IP address (security.ip-blocked) listenerId = “imaptls”, localPort = 993, remoteIp = 172.19.0.5, remotePort = 58452
2026-06-11T09:50:00Z INFO Blocked IP address (security.ip-blocked) listenerId = “imaptls”, localPort = 993, remoteIp = 172.19.0.5, remotePort = 58458
2026-06-11T09:50:10Z INFO Invalid EHLO command (smtp.invalid-ehlo) listenerId = “smtp”, localPort = 25, remoteIp = 130.12.182.143, remotePort = 56878, domain = “User”
2026-06-11T09:53:13Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 35562
2026-06-11T09:53:14Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 355742026-06-11T09:43:35Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 37168
2026-06-11T09:43:36Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 46968
2026-06-11T09:43:38Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 46982
2026-06-11T09:43:40Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 46988
2026-06-11T09:43:41Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 47004
2026-06-11T09:43:44Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 47006
2026-06-11T09:43:52Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 46174
2026-06-11T09:43:53Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 46190
2026-06-11T09:43:54Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 46200
2026-06-11T09:43:56Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 37624
2026-06-11T09:44:00Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 37640
2026-06-11T09:44:05Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 37656
2026-06-11T09:44:12Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 46750
2026-06-11T09:44:14Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 46760
2026-06-11T09:44:31Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 45724
2026-06-11T09:44:38Z INFO Invalid EHLO command (smtp.invalid-ehlo) listenerId = “smtp”, localPort = 25, remoteIp = 62.60.130.14, remotePort = 62895, domain = “User”
2026-06-11T09:44:45Z INFO Invalid EHLO command (smtp.invalid-ehlo) listenerId = “smtp”, localPort = 25, remoteIp = 130.12.182.143, remotePort = 55846, domain = “User”
2026-06-11T09:44:55Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 42008
2026-06-11T09:44:56Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 50126
2026-06-11T09:45:03Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 50130
2026-06-11T09:45:35Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 57586
2026-06-11T09:45:36Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 46910
2026-06-11T09:46:15Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 33468
2026-06-11T09:47:26Z INFO SMTP EHLO command (smtp.ehlo) listenerId = “smtp”, localPort = 25, remoteIp = 193.32.162.71, remotePort = 61177, domain = “``kuhn-online.co.uk``”
2026-06-11T09:47:26Z INFO SPF EHLO check failed (smtp.spf-ehlo-fail) listenerId = “smtp”, localPort = 25, remoteIp = 193.32.162.71, remotePort = 61177, domain = “``kuhn-online.co.uk``”, result = SPF check failed (spf.fail), elapsed = 14ms
2026-06-11T09:47:26Z INFO Authentication not allowed (smtp.auth-not-allowed) listenerId = “smtp”, localPort = 25, remoteIp = 193.32.162.71, remotePort = 61177
2026-06-11T09:50:00Z INFO Blocked IP address (security.ip-blocked) listenerId = “imaptls”, localPort = 993, remoteIp = 172.19.0.5, remotePort = 58452
2026-06-11T09:50:00Z INFO Blocked IP address (security.ip-blocked) listenerId = “imaptls”, localPort = 993, remoteIp = 172.19.0.5, remotePort = 58458
2026-06-11T09:50:10Z INFO Invalid EHLO command (smtp.invalid-ehlo) listenerId = “smtp”, localPort = 25, remoteIp = 130.12.182.143, remotePort = 56878, domain = “User”
2026-06-11T09:53:13Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 35562
2026-06-11T09:53:14Z INFO Blocked IP address (security.ip-blocked) listenerId = “http”, localPort = 8080, remoteIp = 172.19.0.2, remotePort = 35574

Seems that stalwart is not able to properly talk to traefik?

At the moment I am using a config.toml file for config:

authentication.fallback-admin.certificate.default.cert = “%{file:/data/certs/mail.kuhnwebservices.de/cert.pem}%”
authentication.fallback-admin.certificate.default.default = true
authentication.fallback-admin.certificate.default.private-key = “%{file:/data/certs/mail.kuhnwebservices.de/key.pem}%”
authentication.fallback-admin.secret = “KEY”
authentication.fallback-admin.user = “USER”
certificate.default.cert = “%{file:/data/certs/mail.kuhnwebservices.de/cert.pem}%”
certificate.default.default = true
certificate.default.private-key = “%{file:/data/certs/mail.kuhnwebservices.de/key.pem}%”
directory.internal.store = “rocksdb”
directory.internal.type = “internal”
enterprise.license-key = “KEY”
server.hostname = “``mail.kuhnwebservices.de``”
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.imaptls.bind = “[::]:993”
server.listener.imaptls.protocol = “imap”
server.listener.imaptls.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.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.console.ansi = false
tracer.console.buffered = true
tracer.console.enable = true
tracer.console.level = “info”
tracer.console.lossy = false
tracer.console.multiline = false
tracer.console.type = “stdout”
tracer.log.ansi = false
tracer.log.enable = true
tracer.log.level = “info”
tracer.log.lossy = false
tracer.log.path = “/opt/stalwart/logs”
tracer.log.prefix = “stalwart.log”
tracer.log.rotate = “daily”
tracer.log.type = “log”

Thanks for your help

The logs are saying that local IP is being denied as it’s in the Security → Block list. Add your local IP or prefix for Traefik to the Allowed list so it doesn’t get blocked again, remove it from the block list, and do a Reload → Blocked IPs.

Thank you for your fast reply. Could you explain how to remove it from the block list without access to the webinterface?

The CLI reference is here

I believe if you put the server in recovery mode, you should be able to get to the UI as well.

Cool that did work out. If someone else is facing this issue, I resolved it via stalwart-cli:

stalwart-cli -u ``http://127.0.0.1:8080`` -c admin:‘PASSWORD server delete-config server.blocked-ip.TRAEFIK-IP

stalwart-cli -u ``http://127.0.0.1:8080`` -c admin:‘PASSWORD’ server add-config server.allowed-ip.TRAEFIK-IP ““

stalwart-cli -u ``http://127.0.0.1:8080`` -c admin:‘PASSWORD’ server reload-config

After this go to Settings - Network via the web interface and add your Proxy IP or subnet to proxy networks. Reload config

Next add the traefik subnet or IP to Allowed IPs via web interface Settings - Security - Allowed IPs. Reload config

Hopefully I have all settings in order now.