VPS nftables Firewall With Docker
VPS nftables Firewall With Docker
This note records a small nftables firewall setup for a
VPS that also runs Docker.
The goal is not to write a clever universal ruleset. The goal is
simpler: keep the host input path small, expose only the
ports that should be public, and avoid fighting Docker's own NAT
rules.
The example uses documentation IP addresses:
203.0.113.10as the admin's trusted IP198.51.100.20as the server IP22222as the SSH port
Replace them with your own values.
Basic Rules
Use the inet family so the same table can handle IPv4
and IPv6:
1 | #!/usr/sbin/nft -f |
Docker Notes
Docker manages its own forwarding and NAT rules. A global ruleset
flush deletes those rules regardless of the replacement forward policy;
forward accept cannot reconstruct NAT. This file only
recreates inet vps_host and leaves other tables untouched.
Do not share that table name with another firewall manager. Existing
base-chain drops can still block traffic: acceptance in one table does
not override a later drop.
Docker-published bridge ports usually traverse forward,
not host input, so this host-only policy does not restrict
them. Bind private published ports to localhost or apply forwarding
restrictions through the interface documented for your installed Docker
firewall backend. This example does not establish backend-independent
container filtering.
For a small VPS, I usually keep the boundary simple:
- protect the host through the
inputchain - let Docker manage container NAT
- expose public services through Caddy on
80/443 - bind internal app ports to
127.0.0.1when possible
Example Docker port mapping:
1 | ports: |
Then let Caddy publish it over HTTPS.
Apply Safely
Save this dedicated-table example to a separate root-owned file such
as /etc/nftables-vps-host.nft; do not overwrite or enable a
distribution service whose startup/stop actions flush the whole ruleset.
The following is only a check, not an application command. Verify your
actual admin source address and SSH listening port first, including IPv6
needs (the example allows SSH only from the specified IPv4 address).
Before any live load, keep provider-console access and a second SSH
session, save the current ruleset for diagnosis, and arrange a timed
rollback that removes or restores only
inet vps_host. Check the exact rollback facility on your
distribution. Restoring an old full ruleset can overwrite Docker rules
created since the snapshot. Then validate before loading:
1 | sudo nft -c -f /etc/nftables-vps-host.nft |