The SSH connection drops, you try again, it drops again. You see no error message, no response. Just Connection timed out or Connection refused stuck on the screen, and you know that a few minutes ago you closed this door yourself with an iptables command or a line in sshd_config. In technical circles, this situation is called being "locked out of the server," and the first thing most people do is wrong: resetting the machine.
Restarting the server almost never solves the problem, because the rule you wrote is saved on disk and gets reloaded after boot. If you've enabled the firewall service, it's even worse: the same rule comes back on every boot. So before anything else, stop rebooting and look for a second way in.
The rescue console: the only door still open
Every infrastructure provider has an access path outside the public network. At ServerNet, this path is available from the control panel in the form of a serial console or VNC. This console does not pass through the server's network stack, so the firewall rule that closed SSH has no effect on it. If you have a dedicated server or VPS, find the location of this button in the panel right now, before you need it.
Once you're in through the console, first look at the actual state, not what you think it is:
iptables -L INPUT -n -v --line-numbers
nft list ruleset
ss -tlnp | grep :22
systemctl status sshd
The output of ss tells you whether the SSH service is even up. If port 22 isn't in the LISTEN list, the problem isn't the firewall; the service has crashed or its config is broken. This distinction saves you half your time. If the port is listening but you get no response from outside, the filter layer is to blame.
Removing the guilty rule without losing the rest
View the rules with numbers and delete only that one line. Deleting the entire chain with iptables -F is a foolish move, because it takes all the other protective rules with it and leaves the server naked.
iptables -D INPUT 3
# or for nftables
nft delete rule inet filter input handle 12
After deleting, immediately test from another terminal that SSH comes back. Until this test succeeds, don't close the console terminal.
The timed rule: build a safety net before applying
The main problem isn't that the rule was wrong; it's that you applied it without a way back. The standard solution I've used on production servers for years is automatic rollback scheduling. Before any change to the firewall, set up a job that reverts the previous rules if you don't confirm.
# save the healthy state
iptables-save > /root/fw-good.rules
# automatic rollback after 5 minutes if you don't confirm
echo "iptables-restore < /root/fw-good.rules" | at now + 5 minutes
Now apply the new rule. If your connection drops, wait five minutes and reconnect. If everything is fine, cancel the job with atrm and save the new state. This pattern also works in nftables with nft list ruleset > /root/fw-good.nft and nft -f. The cost is that you have to wait five minutes, and during that time the server runs with the old rules. For a production environment, those five minutes are the cheapest insurance you can buy.
An important note about at: it isn't installed on many minimal distributions. If you don't have at, do the same thing with systemd-run:
systemd-run --on-active=5min /sbin/iptables-restore /root/fw-good.rules
The order that never leads to losing access
The order of operations matters more than the commands themselves. This is the order that works in practice:
- Open a second SSH session and keep it open. Never make firewall changes on a single connection.
- Save the healthy state to a file.
- Activate the automatic rollback job.
- Apply the new rule.
- Test from the second session. If it works, close the first session and cancel the job.
If you're changing the SSH port, add the new rule before restarting sshd, not after. The common mistake is that a person changes the port in sshd_config, restarts the service, and then remembers the firewall only opened the old port. The result: being locked out of the server while the service is healthy. For details on this and configuring fail2ban, see the guide SSH security; from changing the port to fail2ban and public keys.
This is where they go wrong
The real mistake I've seen many times: someone runs ufw enable with ufw without having run ufw allow 22/tcp beforehand. The symptom is exactly that SSH drops immediately, but ping still responds and the website still comes up. Because ufw blocks incoming traffic by default but doesn't touch ICMP or previously opened ports. This makes a person think the network is fine and the problem is elsewhere. If you see these symptoms, first check ufw status verbose from the rescue console.
Mistake number two: applying a rule to the OUTPUT chain without understanding that SSH responses go back out through it. The connection is established, authentication is rejected, then it drops. Diagnose this with iptables -L OUTPUT -n -v and counting the drop packets.
When the rescue console isn't available either
If you're on a physical server and don't have physical access, or you have a VPS whose console doesn't work, the only option is booting in rescue mode through the provider's panel. In this case, the system comes up from a minimal environment and you mount the main disk to manually fix the rules file. This path is slow, which is why I take the "automatic rollback job" step seriously before any change.
For services whose access is critical, put the protection layer somewhere you won't lock yourself out of. ServerNet's DDoS protection does this at the network edge, and the filter rules stay separate from your operating system's stack; meaning even if you break your local firewall, malicious traffic is stopped right there and you're dealing with a cleaner system.
After recovery, check these
Getting the connection back isn't the end of the job. Be sure to check three things:
- The saved rules should match the current in-memory state. Compare
iptables-savewithiptables -L. - The firewall service should be enabled on boot so you aren't caught off guard after a restart:
systemctl is-enabled nftables. - Keep a change log. Every change to the firewall should have a record so that when you get locked out again, you know which rule is to blame. I've explained how to keep these records in What are audit logs and how do we keep them intact?.
If you're unsure whether the rule you're writing is correct, before applying it on the main server, run the same rules on a test machine with the same distribution version. The difference in nftables versions between Debian 11 and 12 in the syntax of some commands isn't small, and this causes a rule that worked on the test to throw an error on production.
One last note about order: always have network inspection tools available before closing ports, so you can confirm from outside what's open. The DNS and network lookup tools are for exactly these moments.
Frequently asked questions
Why can't I still SSH in after restarting the server?
Because the firewall rules are saved on disk and the service reloads them on boot. A restart only clears the memory state, not the rules file. Until the /etc/nftables.conf file or the iptables-save output is fixed, every boot brings back the same lockout.
How do I tell whether the problem is the firewall or the SSH service itself?
From the rescue console, run ss -tlnp | grep :22. If the port is in LISTEN state, the service is healthy and the filter layer is to blame. If it isn't, the problem is in sshd or its config, and touching the firewall is pointless.
Is deleting all rules with iptables -F a quick fix?
No. This command wipes all input, output, and forward rules and leaves the server open to any traffic. The right way is to find the guilty rule with --line-numbers and delete only that one line with -D.
How do I run the timed rollback rule on a server that doesn't have at?
Use systemd-run --on-active=5min. This command creates a transient unit that runs the rollback command after five minutes and doesn't require at to be installed. If everything is fine, cancel the unit with systemctl stop.
Comments 0
No comments yet — be the first!