gitpuls

Blog · Server

Does Docker bypass UFW, and do my rules survive a reboot?

By Cédric Freivogel, outperform GmbH · Published

Yes: traffic to a published container port is routed before UFW’s rules are applied, so UFW never sees it, and your own rules belong in Docker’s DOCKER-USER chain. Whether they really protect you shows only when you look at IPv4 and IPv6 separately and again after a reboot; on our VPS both families carry the same 4 rules in DOCKER-USER, restored from a file saved weeks before the last boot.

Why does Docker skip UFW?

Because the packets never reach the chain UFW filters. Docker’s documentation puts it in one line in its section on ufw: “Packets are routed before the firewall rules can be applied” (Docker Docs, packet filtering and firewalls, checked 9 October 2026). The same page asks you not to change the rules Docker creates.

ufw status therefore looks correct while a published port is reachable from the internet. Our own VPS has no UFW installed at all; it runs plain iptables rules, saved with netfilter-persistent, which makes the effect easier to see but does not change it.

Where do my own rules go?

Into the DOCKER-USER chain. Docker describes it as “a placeholder for user-defined rules that will be processed before rules in the DOCKER-FORWARD and DOCKER chains” (Docker Docs, Docker with iptables, checked 9 October 2026), and the same page says: “to add additional rules to filter these packets, use the DOCKER-USER chain.”

On our VPS, read on 9 October 2026, DOCKER-USER holds 4 rules and INPUT holds 8, one of which jumps into a chain that a VPN daemon creates on every start.

Do the same rules exist for IPv6?

They have to be set up a second time, with ip6tables. Docker manages IPv6 rules as well; its documentation says of the ip6tables option: “It is enabled by-default, but can be disabled” (Docker Docs, IPv6, checked 9 October 2026). With Docker 29.8 on our VPS the DOCKER-USER chain exists in both families, and both carry the same rules: 4 in DOCKER-USER and 8 in INPUT for IPv4, and exactly the same for IPv6.

A rule that exists only for IPv4 leaves the port open on the server’s IPv6 address. That gap does not show in iptables -S; it only shows in ip6tables -S.

Do the rules survive a reboot?

Only if they were saved and something restores them at boot. Rules typed at the shell are gone after the next reboot. On our VPS netfilter-persistent is enabled and active; the saved files for IPv4 and IPv6 date from 8 September 2026, the last boot was on 1 October 2026, and after that boot the running rules in INPUT and DOCKER-USER were identical to the saved ones in both families.

The date matters in both directions. A saved file older than the boot is fine as long as nothing was changed after saving; a rule added after the last save looks active today and is lost at the next boot. Comparing running and saved rules shows that before the reboot does.

How do I check all of this in one minute?

Run these as root on the server, then test from a second machine:

iptables -S DOCKER-USER; ip6tables -S DOCKER-USER
iptables -S INPUT | grep -c '^-A'; ip6tables -S INPUT | grep -c '^-A'
systemctl is-enabled netfilter-persistent
diff <(iptables-save | grep -E '^-A (INPUT|DOCKER-USER)') <(grep -E '^-A (INPUT|DOCKER-USER)' /etc/iptables/rules.v4)
diff <(ip6tables-save | grep -E '^-A (INPUT|DOCKER-USER)') <(grep -E '^-A (INPUT|DOCKER-USER)' /etc/iptables/rules.v6)

The two diff lines print nothing when running and saved rules match. From outside, curl -4 and curl -6 against a published port show whether the rules hold for each address family.

How this text was written

This article was drafted with an AI assistant from our own measurements and the sources linked above, which were checked on 9 October 2026. The numbers come from the method page; nothing was estimated.