Add UFW - Uncomplicated Firewall rules
Self hosted PayloadCMS and PostgreSQL website on Docker
4 min read
Published Jun 17 2025, updated Jun 19 2025
Guide Sections
Guide Comments
At the moment the server isn't locked down. So if you were to try and access the database using the public IP in the connection string, it would let you, or you could access the public IP with port 3000 to access the website etc.
What we want to do is shut out access to all the ports on the main public IP, apart from ports 80 and 443 for the website traffic coming through to Caddy. We also want to leave access to anything on our Tailscale network so we can still SSH in to the server, access the database etc. over the secure Tailscale VPN.
Run this command on the server to get a list of the network adapters:
This will return a list of all the network adapters available on the server, here is an example:
So in the example above, the adaptors we are interested in are enp1s0f0
which is the main adapter that the public IP points to, we can ignore enp1s0f1
as that is a spare network adapter that has a state of down, and the tailscale0
adapter which is for our Tailscale.
So what we want to setup is:
enp1s0f0
: Port 80 allow.enp1s0f0
: Port 443 allow.enp1s0f0
: All other ports deny.tailscale0
: Allow all ports.
To do this we will add rules to the UFW, make sure you change the network adapter name to what yours is listed as, the order the rules happen matters (you want to say something specifics allowed before then denying all as they are processed in order).
Allow Tailscale on all ports:
Allow port 80 on the main network:
Allow port 443 on the main network:
Deny all other ports on main network:
Now enable the UFW:
Now test the rules have been applied:
Which lists a response like:
UFW, iptables & docker
In Linux, it is a thing called iptables that control a networks access. UFW just applies rules to the iptables.
However, docker also adds rules to the iptables, which get applied before the UFW rules. So even though you have just setup UFW to allow and disallow ports on the 2 network adapters, it wont actually fully lock the system down yet. Docker effectively punches holes through when a port is published, so even though you have UFW rules that restrict all ports apart from 80 and 443 on the main IP address, at the moment, any published docker port is also available on the main IP as the docker rules occur before the UFW rules and so override them.
What we need to do now is add our own iptable deny rules for all the ports we have published on docker. We can do that directly with ip tables by adding a line such as:
Which is saying, in the docker user chain part of iptables, all incoming requests on the main network, port 5000, drop the matching packets. So therefore blocking the incoming connection on the main IP for port 5000.
So we could go ahead and add one of them for the other ports we published which were 5000
, 5432
and 3000
. Wich will work for now and stop incoming requests on the main IP for those ports.
When the server is rebooted, the iptables data is reset, so any additional rules you have added, like the above, will have been removed.
What we can do is add these iptable deny rules in to the UFW before rules file /etc/ufw/before.rules
. That way they will always be applied by UFW when the server is rebooted.
Edit the /etc/ufw/before.rules
file and add the deny rules for each other the published ports to the top of the file, below the *filter
line but as high up as you can so they are processed before any other rules, so find the *filter
heading:
/etc/ufw/before.rules
We also want to add the 8000
, 9000
and 9443
, as these are Portainers published port numbers, so we also want them denied on the main IP.
When thats saved, need to disable and reenable UFW to apply the changes:
Now check UFW is enabled:
Check UFW is set to start on boot:
If not then run:
Your server access should now be more locked down, with main web traffic allowed through and more access over Tailscale.
If you need to debug any issues, you can list all active iptable rules:
Or NAT rules: