I'm new with docker, currently want to setup docker on vps by following this tutorial [https://www.howtoforge.com/how-to-install-and-use-nginx-proxy-manager/](https://www.howtoforge.com/how-to-install-and-use-nginx-proxy-manager/) so my setup plan will be like this :

vps ubuntu 22.04, wireguard for vpn, ufw for firewall :  
nginx proxy manager (NPM) to handle http, https, ssl certificate (exposed port 81)

portainer for docker management (exposed port 9443)

ghost, my other container will use docker network and talk directly with nginx proxy manager like tutorial and mapping to subdomain accordingly.  
I don't like to expose port 81, 9443 to public so i make ufw rule and allowed only local vpn ip that can connect to that port.

my problem is docker container seems to ignore ufw rule and make the port still accessible with public ip.  
I have tried [https://github.com/chaifeng/ufw-docker](https://github.com/chaifeng/ufw-docker) and it fix portainer and NPM problem.. but ghost, and my other container failed to connect with nginx proxy manager if somehow the box restarted or container restarted.

my work around right now is exposed ghost, other container to host port and then map it to NPM by ip address. it works but feels wrong because as the number of container grows the needs for port will increase too.

What is the clean solution for this? thanks
Docker inserts its first rule fairly early in iptables, at nat PREROUTING. You can view the rules with:

iptables -t nat -S

ufw often puts rules in INPUT which is after both PREROUTING and FORWARD!

There are two places in PREROUTING that are before nat however! raw and mangle. Mangle can do most things INPUT can do anyways, so you could keep packets incoming to the public interface from reaching nat let alone docker's network at all by making a rules for the pub interface such as:

iptables -t mangle -A PREROUTING -i eth0 -p tcp --dport 9443 -j ACCEPT

iptables -t mangle -A PREROUTING -i eth0 -p tcp --dport 81 -j ACCEPT

# So you don't lock yourself out of ssh:

iptables -t mangle -A PREROUTING -i eth0 -p tcp --dport 22 -j ACCEPT

Then drop the rest of tcp after your accepts on the pub interface (don't forget udp):

iptables -t mangle -A PREROUTING -i eth0 -p tcp -j DROP

For reference like with guides an equivalent rule for INPUT would look like:

iptables -A INPUT -i eth0 -p tcp --dport 22 -j ACCEPT

assuming your public interface name is eth0 of course. Change it in the rules if need :)


The alternative is what hypgn0sis suggested by using the DOCKER-USER chain. However that will be less efficient and packets still reach the network (including FORWARD accepts). Also ufw and firewalld will interfere with your iptables rules unless you disable or uninstall them.