You Just Deployed a New Linux Server. Now What?
So, you’ve just spun up a fresh Linux server in the cloud. It has a public IP, you can SSH in, and you’re ready to deploy your application. But hold on. Before you do anything else, we need to talk about security. Right now, that server is a blank slate. Without a firewall, it’s essentially a sitting duck. Automated bots and scanners are constantly probing the internet for vulnerable machines just like this one.
Leaving a server exposed is a massive risk. Every running service with an open port is a potential door for an intruder. It’s time to lock those doors.
The Root of the Problem: Unfiltered Network Ports
Any network service, like a web server on ports 80/443 or SSH on port 22, must open a port to accept connections. On a default Linux install, there’s often nothing stopping anyone on the internet from knocking on those ports. A freshly deployed server can see thousands of automated port scans and login attempts within its first hour online.
These automated scripts scan IP ranges 24/7. They’ll try to brute-force your SSH password, exploit a known vulnerability, or find any other way in. The solution is to control exactly what traffic reaches your server in the first place. That’s the job of a firewall.
In Linux, the kernel’s packet filtering framework is called Netfilter. For years, we managed Netfilter with a tool called iptables. Today, however, its modern and more efficient successor, nftables, has taken center stage.
The Tools of the Trade: iptables vs. nftables
When you research Linux firewalls, two names immediately pop up: iptables and nftables. Understanding the difference is key to building a modern, secure server.
The Classic: iptables
iptables was the standard for a very long time. If you’ve worked with Linux for more than a few years, you’ve definitely used it. Its logic is built on separate tables (like filter, nat) and chains of rules (like INPUT, OUTPUT).
A typical iptables ruleset might look like this:
# Allow incoming SSH connections
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# Allow established connections (crucial for return traffic)
sudo iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
# Drop all other incoming traffic by default
sudo iptables -P INPUT DROP
While effective, iptables has its drawbacks:
- Complexity: The syntax is verbose and can be confusing. It’s easy to make a mistake.
- IPv4/IPv6 Separation: You need a completely separate tool (
ip6tables) and a duplicate set of rules for IPv6. This is a real headache in modern networks. - Performance: As rulesets grow,
iptablescan slow down. Every packet has to be checked against a long chain of rules one by one, which can introduce latency.
The Modern Successor: nftables
nftables was designed from the ground up to fix the shortcomings of iptables. It provides a new, unified command-line tool and a much cleaner syntax. Most modern Linux distributions, including Debian, Ubuntu, and Fedora, now use nftables by default.
Here are its key advantages:
- Unified Framework: A single
nftcommand manages rules for IPv4, IPv6, and ARP. No more jugglingiptables,ip6tables, andarptables. - Atomic Updates: You can replace your entire ruleset in a single, atomic operation. This prevents temporary firewall gaps or syntax errors from taking down your connectivity during an update.
- Superior Performance: It uses more efficient data structures, like hash tables and sets. This allows for much faster packet processing, especially with thousands of rules.
For any new server deployment, I strongly recommend starting with nftables. It’s the future-proof choice.
My Go-To Firewall Setup with nftables
Here’s a step-by-step guide to setting up a solid, secure baseline firewall. This is the exact configuration I use for new production web servers.
First, make sure nftables is installed. On Debian/Ubuntu, run sudo apt update && sudo apt install nftables. On RHEL/CentOS/Fedora, it’s sudo dnf install nftables.
1. Create the Configuration File
Instead of running commands one-by-one, the most reliable way to manage a firewall is with a configuration file. This makes your ruleset easy to read, version control, and restore. Create and edit /etc/nftables.conf:
sudo nano /etc/nftables.conf
2. Build the Ruleset
Paste the following configuration into the file. We’ll break down what each part does below.
#!/usr/sbin/nft -f
# Start with a clean slate
flush ruleset
# Create our main table for both IPv4 and IPv6
table inet filter {
# INPUT chain: for traffic coming into the server
chain input {
# Default policy is to drop everything. This is the most secure stance.
type filter hook input priority 0; policy drop;
# --- ACCEPT RULES (in order of importance) ---
# 1. Allow traffic from the loopback interface (for local services)
iifname "lo" accept
# 2. Allow established and related connections (the magic rule)
ct state established,related accept
# 3. Allow ICMP (ping) - useful for network diagnostics
ip protocol icmp accept
ip6 nexthdr icmpv6 accept
# 4. Allow SSH (port 22) - absolutely essential!
tcp dport 22 accept
# 5. Allow HTTP and HTTPS (ports 80, 443) for a web server
tcp dport { 80, 443 } accept
}
# FORWARD chain: for traffic passing through the server (e.g., a router)
# We are a server, not a router, so we drop this.
chain forward {
type filter hook forward priority 0; policy drop;
}
# OUTPUT chain: for traffic going out from the server
chain output {
# We trust our own outgoing traffic by default.
type filter hook output priority 0; policy accept;
}
}
Breaking Down the Configuration:
flush ruleset: Wipes all existing rules. This ensures your new configuration is applied to a clean state.table inet filter: Creates a single table named `filter` for the `inet` family, which elegantly handles both IPv4 and IPv6. This is a huge win for simplicity.chain input { policy drop; }: This is the heart of our security policy. We define the chain for incoming traffic (`input`) and set its default policy to drop. Any packet that doesn’t match an explicit `accept` rule is silently discarded.iifname "lo" accept: Allows all traffic on the local loopback interface. Many applications need this to communicate with themselves.ct state established,related accept: This is the magic rule. It uses the connection tracker (`ct`) to automatically allow return traffic for connections your server initiates. Without it, you could make outgoing requests, but you’d never get the responses back.tcp dport 22 accept: Explicitly allows incoming TCP connections to port 22 (SSH). If you forget this rule, you will lock yourself out of your own server.tcp dport { 80, 443 } accept: Shows how easily you can allow traffic to a set of ports—in this case, for a standard web server.chain output { policy accept; }: For simplicity, we allow all outgoing traffic. While high-security environments might restrict this further, it’s a practical and secure starting point for most applications.
This nftables configuration is my go-to baseline for any new server. It’s proven to be incredibly stable in production, providing robust security without being overly complex.
3. Activate and Persist the Rules
Now, load your new ruleset and enable the nftables service so it starts automatically on every boot.
# Atomically apply the rules from the file
sudo nft -f /etc/nftables.conf
# Enable the nftables service to start on boot
sudo systemctl enable nftables.service
# Start the service now
sudo systemctl start nftables.service
4. Verifying Your Ruleset
To see your active rules in a clean, readable format, run this command:
sudo nft list ruleset
The output should perfectly mirror your /etc/nftables.conf file. This confirms your firewall is active and configured exactly as you intended.
And just like that, your server is no longer a sitting duck. You’ve built a solid, modern firewall that closes the door on unwanted traffic while giving you a clear, maintainable configuration for the future.

