iptables is the classic command-line tool for the Linux kernel firewall. Even if you later use UFW or firewalld, knowing how iptables reads its rules helps you understand every other Linux firewall. This guide shows you how to list rules, allow the traffic you need, drop the rest and remove rules you no longer want.
Before you start
- A Linux VPS running Ubuntu 22.04/24.04, Debian 12, or AlmaLinux/Rocky Linux 8 or 9.
- Root access or a user with
sudo. - Only one firewall manager in charge. If UFW or firewalld is active, use that tool instead, or disable it first. Two managers writing rules at once causes confusing results.
- Your SSH port number. It is 22 unless you changed it.
Warning: A default DROP policy without an SSH allow rule cuts you off the moment you apply it. Always add the SSH rule first. If you do get locked out, open the browser console from your VPS provider's control panel. It works without SSH.
How chains and rule order work
iptables groups rules into tables. The one you use most is the filter table, which has three built-in chains:
INPUT: packets addressed to the server itself, such as an SSH login.OUTPUT: packets the server sends out.FORWARD: packets passing through the server to another host, used for routers and VPN gateways.
Each chain is a list read from top to bottom. The first rule that matches a packet decides what happens to it, usually ACCEPT, DROP or REJECT. If no rule matches, the chain's policy applies. That is why order matters: a DROP rule placed above an ACCEPT rule wins.
On current distributions the iptables command usually talks to the nftables backend (iptables-nft). The commands in this guide are the same either way. Check which one you have:
sudo iptables --versionThe output ends in (nf_tables) or (legacy).
List the current rules
Show the filter table with line numbers and numeric addresses:
sudo iptables -L -n -v --line-numbers-Llists the chains.-nskips DNS lookups, which makes the output fast.-vadds packet and byte counters and interface names.--line-numbersshows the position of each rule, which you need for deletes.
You can also print the rules in the same format you would type them:
sudo iptables -SBuild a basic ruleset
Add the rules in this order. Each -A appends to the end of the INPUT chain.
- Allow loopback traffic, which many local services rely on:
sudo iptables -A INPUT -i lo -j ACCEPT- Allow replies to connections the server already has open:
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT- Allow SSH. Change
22if you use a custom port:
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT- Allow web traffic if the server hosts websites:
sudo iptables -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT- Allow ping, which helps with troubleshooting:
sudo iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT- Drop invalid packets before they reach other rules (optional but tidy):
sudo iptables -I INPUT 2 -m conntrack --ctstate INVALID -j DROPOnly now, with SSH allowed, set the default policy for incoming traffic to DROP:
sudo iptables -P INPUT DROPLeave OUTPUT on ACCEPT unless you have a reason to filter outgoing traffic. Leave FORWARD on DROP unless the server routes traffic for others.
Note: These rules cover IPv4 only. If your server has IPv6, repeat the same rules with ip6tables, using --icmpv6-type echo-request with -p ipv6-icmp for ping. IPv6 also needs ICMPv6 neighbour discovery, so the simplest safe choice is sudo ip6tables -A INPUT -p ipv6-icmp -j ACCEPT.
Allow a port only from one IP
Use -s to limit a rule to a source address or range. This allows MySQL only from one application server:
sudo iptables -A INPUT -p tcp -s 198.51.100.20 --dport 3306 -j ACCEPTDelete and insert rules
List the chain with line numbers, then delete by position:
sudo iptables -L INPUT -n --line-numbers
sudo iptables -D INPUT 5Line numbers shift after every delete, so list again before deleting another rule.
You can also delete by repeating the exact rule with -D instead of -A:
sudo iptables -D INPUT -p tcp --dport 3306 -s 198.51.100.20 -j ACCEPTTo place a rule above others, insert it at a position with -I. This blocks one address before the allow rules see it:
sudo iptables -I INPUT 1 -s 203.0.113.10 -j DROPWarning: sudo iptables -F flushes every rule in the chain but keeps the DROP policy. On a live server that blocks SSH instantly. Set sudo iptables -P INPUT ACCEPT before you flush.
Save the rules
Rules you add with the iptables command live in memory and vanish at reboot. To export the current set:
sudo iptables-save > ~/iptables-backup.rulesTo load them back:
sudo iptables-restore < ~/iptables-backup.rulesTo load rules automatically at boot, follow Make iptables rules survive a reboot.
Check it works
- Keep your current SSH session open and start a second SSH session from your computer. If it connects, the SSH rule works.
- Check the counters grow on the rules you expect:
sudo iptables -L INPUT -n -v- From another machine, test a port that should be closed. It should time out:
nc -vz -w 5 203.0.113.10 3306Troubleshooting
- SSH stopped working after setting the policy: the SSH rule is missing or uses the wrong port. Log in through the browser console, run
sudo iptables -P INPUT ACCEPT, then fix the rule. - A website is unreachable but port 80 is allowed: a
DROPorREJECTrule sits above the allow rule. List with--line-numbersand move or delete it. - Rules disappear after a reboot: they were never saved to a file the system loads at boot. Install the persistence package for your distro.
- Rules look different after you run UFW or firewalld: those tools manage iptables or nftables themselves and overwrite manual changes. Pick one tool.
- Outgoing updates fail: you set
OUTPUTto DROP. Set it back withsudo iptables -P OUTPUT ACCEPT.
Next steps
- Keep your rules after a reboot: Make iptables rules survive a reboot.
- Test changes with a safety net: Test firewall changes safely with an automatic rollback.
- Prefer a simpler front end: Set up UFW on Ubuntu and Debian.
