Upgraded to Umbrel 2.0 - UI not accessible

Issue Description

Upgraded to Umbrel 2.0
Stalwart works, I can send and receive email.
But the admin UI isnt working. When i open the umbrel app, it says “Oops, there was an error” … “There was an error connecting to Stalwart. Error code: ECONNRESET”

Expected Behavior

I expected to see the admin ui

Actual Behavior

When i open the umbrel app, it says “Oops, there was an error” … “There was an error connecting to Stalwart. Error code: ECONNRESET”

Reproduction Steps

  • Upgrade to umbrel 2.0
  • Open Stalwart app
  • You see the screen “Oops, there was an error” … “There was an error connecting to Stalwart. Error code: ECONNRESET”

( i also updated the stalwart app to 0.16.24, same problem)

Stalwart Version

v0.16.x

Installation Method

Docker

Database Backend

RocksDB

Blob Storage

RocksDB

Search Engine

Internal

Directory Backend

Internal

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

It sounds like you’ve tripped an auto-banning security rule. Log in via WebUI and examine your banned IPs. If you also can’t get in via WebUI, you’ll need to enable recovery mode so you can clear the entry and set a allowed IP rule. Don’t forget to do a actions → reload on blocked IP lists.

Summary

After upgrading to UmbrelOS 2.0, the Stalwart Mail Server WebUI is no longer accessible through its published HTTPS port.

The Stalwart container itself is healthy and HTTPS works correctly from inside the container and from another container on the same Docker network. The failure appears to be specifically between the Umbrel host and the Docker-published HTTPS port.

Environment

  • UmbrelOS: 2.0

  • Docker Server: 28.5.0

  • App: Stalwart Mail Server

  • Container: stalwart_server_1

  • Docker network: umbrel_main_network

  • Docker subnet: 10.21.0.0/16

  • Stalwart container IP: 10.21.0.2

Docker port mapping

Stalwart exposes:

10025 -> 25
10110 -> 110
10143 -> 143
10443 -> 443
10465 -> 465
10587 -> 587
10993 -> 993
10995 -> 995
14190 -> 4190

The container is reported as:

Up 2 days (healthy)

What works

From inside the Stalwart container:

https://127.0.0.1:443/ -> HTTP 302 /account
https://10.21.0.2:443/  -> HTTP 302 /account

A separate temporary container on the same Docker network can also access Stalwart:

curl https://10.21.0.2:443/
-> TLS 1.3 succeeds
-> HTTP/1.1 302 Found
-> location: /account

The Stalwart certificate is presented correctly:

CN=mail.zappy.email
Let's Encrypt

What fails

From the Umbrel host:

curl https://127.0.0.1:10443/
-> TLS unexpected EOF

curl https://10.21.0.2:443/
-> TLS unexpected EOF

Using the correct hostname/SNI does not change the result:

curl --resolve mail.zappy.email:10443:127.0.0.1 \
  https://mail.zappy.email:10443/
-> TLS unexpected EOF

For comparison, the other published Stalwart ports are reachable from the host:

10025 -> OPEN
10587 -> OPEN
10993 -> OPEN
10465 -> OPEN

So this does not appear to be a general Docker networking failure.

Docker/nftables observations

Docker has the expected DNAT rule:

tcp dport 10443 dnat to 10.21.0.2:443

The corresponding Docker filter rule also exists:

-d 10.21.0.2/32
-o br-015376facc3d
-p tcp
--dport 443
-j ACCEPT

The Docker bridge is:

br-015376facc3d
10.21.0.1/16

The Stalwart container is:

10.21.0.2

Additional environment information

A privileged host-network Docker container is running:

portainer_docker_1
network=host
privileged=true

There is also:

wireguard-tunnel_server_1
network=host
privileged=false

I am not claiming either container is responsible; this is included for debugging context.

Expected behavior

The published port:

127.0.0.1:10443 -> 10.21.0.2:443

should allow the Umbrel host/reverse proxy to establish a normal TLS connection to Stalwart.

Actual behavior

The TCP connection reaches the published port, but TLS is terminated with:

SSL error: unexpected eof while reading

while the exact same Stalwart HTTPS listener works normally from inside Docker.

Important observation

This started after the UmbrelOS 2.0 upgrade. No Stalwart configuration changes were intentionally made to cause this behavior.

Could this be related to the UmbrelOS 2.0 Docker/nftables/iptables-nft networking changes or regression in handling host-to-published-container HTTPS traffic?

I can provide the complete nft list ruleset, Docker network inspection, container inspection, and curl diagnostics if needed.

Your host test is the useful one: connecting from the Umbrel host straight to 10.21.0.2:443 skips Docker’s port publishing entirely and still fails. What changes between that and your working container is the source address. Connections from the host arrive from the bridge gateway, 10.21.0.1, and a handshake cut off right after the TCP connect is how Stalwart treats a blocked IP. The other ports showing as OPEN were TCP checks, which a blocked IP still passes.

Look for security.ip-blocked with remoteIp 10.21.0.1 in the Stalwart log. If it’s there, clear it from a container on umbrel_main_network, like the one you used for the curl test, after installing the CLI in it (curl --proto '=https' --tlsv1.2 -LsSf https://github.com/stalwartlabs/cli/releases/latest/download/stalwart-cli-installer.sh | sh):

stalwart-cli --url https://10.21.0.2 -k --user admin query BlockedIp
stalwart-cli --url https://10.21.0.2 -k --user admin delete BlockedIp --ids <id>

Then add an AllowedIp for 10.21.0.0/16 so the Umbrel proxy can’t be banned again: Auto-banning | Stalwart

Thanks, fixed it

Confirmed — this was caused by Stalwart auto-banning the Umbrel Docker bridge gateway IP.

From a temporary container on umbrel_main_network, I queried the Stalwart blocked IP list and found:

ID:       jejtqe3kaaaa
IP:       10.21.0.1
Reason:   Excessive port scanning attempts
Created:  2026-09-12T10:05:27Z
Expires:  <none>

This explains the behavior I was seeing:

  • Stalwart itself was healthy.

  • HTTPS worked from inside the Stalwart container.

  • HTTPS worked from another container on umbrel_main_network.

  • Connections from the Umbrel host to 10.21.0.2:443 failed during the TLS handshake.

  • Stalwart was seeing the Umbrel Docker bridge gateway as 10.21.0.1.

  • The other published ports being reachable did not rule this out, since the IP block happens at the Stalwart security layer.

So this was not a Docker/nftables port-publishing problem. Stalwart’s automatic IP blocking had permanently blocked the Umbrel Docker gateway.

For anyone else troubleshooting the same issue, from a container attached to umbrel_main_network:

curl --proto '=https' --tlsv1.2 -LsSf https://github.com/stalwartlabs/cli/releases/latest/download/stalwart-cli-installer.sh | sh
source /root/.cargo/env

Then check the blocked IPs:

stalwart-cli --url https://10.21.0.2 -k --user admin query BlockedIp

Look for:

10.21.0.1

Then remove the corresponding entry:

stalwart-cli --url https://10.21.0.2 -k --user admin delete BlockedIp --ids <id>

In my case:

stalwart-cli --url https://10.21.0.2 -k --user admin delete BlockedIp --ids jejtqe3kaaaa

The remaining step is to add 10.21.0.0/16 to AllowedIp so the Umbrel proxy cannot be auto-banned again, as suggested by Stalwart.

Thanks for pointing me in the right direction — this solved the mystery of the TLS unexpected EOF.