Tutorial

How to Allow SSH Login From Only Your IP on a VPS (Without Locking Yourself Out)

Allow SSH from one trusted IP using UFW, firewalld, iptables or sshd_config, plus a safe way to test so you never lock yourself out.

Ankit Singh
Software Engineer
·
10 min read
·
AI-assisted

AI tooling disclosure

Generative AI materially contributed to this article.

Human review confirmed by the author.

How to Allow SSH Login From Only Your IP on a VPS (Without Locking Yourself Out)

Spin up a fresh VPS, wait a few minutes, then look at your SSH logs.

sudo grep "Failed password" /var/log/auth.log | tail

On RHEL-based systems (Rocky, Alma, CentOS) the file is /var/log/secure instead. Some newer minimal images have no log file at all. In that case use sudo journalctl -u ssh (the service is called sshd on RHEL-based systems).

You'll probably see a bunch of login attempts from random IPs, trying usernames like root, admin and ubuntu. Nobody is targeting you personally. It's just bots scanning every address on the internet, all day, every day.

If you use SSH keys, those bots mostly get nowhere. But I still like to ask a simple question: why should the whole internet be able to knock on that door at all?

If you only ever log in from your home, your office or a VPN, you can tell the server to ignore everyone else. That's what this post is about.

I'll show you four ways to do it, plus the cloud firewall option. More importantly, I'll show you how to do it without locking yourself out of your own server, because that's the part that goes wrong.

Quick version: Add a rule that allows port 22 from your IP. Then remove the rule that lets everyone in, while your first SSH session is still open. Finally, open a second terminal and log in. Do it in that order, every time.

I'm using 203.0.113.10 as the example IP and port 22 everywhere. Swap in your own.

Before you touch anything

Spend two minutes on this checklist. It saves a lot of pain.

1. Find your public IP. Run this on your own computer, not on the server:

curl -4 ifconfig.me

If you're already logged in to the server, you can also ask it which IP it sees you coming from:

echo $SSH_CLIENT | awk '{print $1}'

2. Check which port SSH uses. Most servers use 22, but yours might not:

sudo ss -tlnp | grep sshd

3. Keep your current SSH session open. Don't close it until the new rule is proven to work. Firewalls generally let existing connections carry on when you change the rules, so an already-open session is your safety net.

4. Find your provider's web console. Most VPS providers give you a browser-based console or a rescue mode. If you do lock yourself out, that's how you get back in. Know where it is before you need it.

Where can you block SSH?

There are three places you can filter SSH traffic, and they stack on top of each other.

flowchart LR
    A["Internet"] --> B["Cloud firewall<br/>(provider level)"]
    B --> C["OS firewall<br/>(UFW, firewalld, iptables)"]
    C --> D["sshd<br/>(AllowUsers)"]
    D --> E["Your login"]

The earlier you block a stranger, the better. A cloud firewall stops traffic before it even reaches your server. An OS firewall stops it at the server's front door. sshd_config only kicks in after the connection has already reached the SSH daemon.

You can use more than one layer. I'd pick the OS firewall as the main one, and add the cloud firewall on top if your provider has it.

Method 1: UFW (Ubuntu and Debian)

UFW is the friendliest option, and it's what most Ubuntu servers use.

If UFW is already running and you currently allow SSH to everyone, start by adding the new rule:

sudo ufw allow from 203.0.113.10 to any port 22 proto tcp

Then look at your rules with numbers:

sudo ufw status numbered

You'll probably see something like OpenSSH or 22/tcp allowed from Anywhere. That's the broad rule that lets everyone in.

Before you remove it, check that the IP in your new rule matches the one you found earlier. A typo here is the classic way to lock yourself out. Then remove the broad rule, and keep your first session open while you do it:

sudo ufw delete allow OpenSSH

or, if you added it as a port:

sudo ufw delete allow 22/tcp

Either command removes both the IPv4 and IPv6 versions of that rule.

If you'd rather delete by number, do it one at a time and list the rules again between deletes, because the numbers shift after each one.

Now open a second terminal and log in. If it works, you're done. If it doesn't, your first session is still open, so you can fix the rule from there.

If UFW is not running yet, the order is everything:

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow from 203.0.113.10 to any port 22 proto tcp
sudo ufw enable

Heads up: If you run ufw enable before adding the allow rule, you've just shut the door with yourself on the outside. Allow first, enable second.

Running a website on the same server? Open those ports too, otherwise UFW will block them:

sudo ufw allow 80,443/tcp

Need to allow a second IP, like your office? Just run the allow command again with the new address. You can also allow a whole range, like 203.0.113.0/24.

Check the result with:

sudo ufw status verbose

Method 2: firewalld (Rocky, Alma, CentOS, RHEL)

On RHEL-style systems, firewalld is the default. Here we use a "rich rule" to allow SSH from one IP:

sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.10" port port="22" protocol="tcp" accept'

The --permanent flag saves the rule, but it doesn't apply to the running firewall until you reload. So reload now:

sudo firewall-cmd --reload

Check that the IP in the new rule matches your real IP. Then remove the general SSH service, which is the rule that lets everyone in, and reload again:

sudo firewall-cmd --permanent --remove-service=ssh
sudo firewall-cmd --reload

Keep your first session open, and log in from a second terminal to confirm it works.

You can check what's left with:

sudo firewall-cmd --list-rich-rules
sudo firewall-cmd --list-services

One small thing to remember: if you use a custom SSH port, change port="22" to match.

Method 3: iptables

iptables is the old-school way. It works on almost any Linux box, but it's the easiest one to get wrong, because the order of the rules matters. The firewall reads them top to bottom and stops at the first match.

So the "allow" rule has to come before the "drop" rule. I use -I (insert) with a position number to make sure of that:

sudo iptables -I INPUT 1 -p tcp -s 203.0.113.10 --dport 22 -j ACCEPT
sudo iptables -I INPUT 2 -p tcp --dport 22 -j DROP

Check the order:

sudo iptables -L INPUT -n -v --line-numbers

Your ACCEPT rule should show up above the DROP rule.

Keep your first session open and log in from a second terminal. If it fails, remove the DROP rule from the first session with sudo iptables -D INPUT 2.

One more thing: the DROP sits above everything else, so an open session from some other IP would be cut off. That's another reason to double-check your IP.

There are two gotchas here.

First, these rules disappear on reboot. You need to save them. On Debian and Ubuntu:

sudo apt install iptables-persistent
sudo netfilter-persistent save

On RHEL-based systems:

sudo dnf install iptables-services
sudo service iptables save

Second, IPv6 is separate. If your server has an IPv6 address, iptables doesn't touch it. SSH can still be reachable that way. You need to handle it with ip6tables:

sudo ip6tables -A INPUT -p tcp --dport 22 -j DROP

If you connect over IPv6 yourself, add an ACCEPT rule for your IPv6 address before that DROP, or you'll cut yourself off.

If you use UFW or firewalld, don't also write iptables rules by hand. Pick one tool. Two tools fighting over the same firewall is a good way to end up confused.

Method 4: sshd_config

This method doesn't use a firewall at all. You tell the SSH daemon itself who is allowed in.

Open the config file:

sudo nano /etc/ssh/sshd_config

Add this line, using your own username and IP:

AllowUsers [email protected]

This means the user deploy can log in, but only from 203.0.113.10. You can allow more than one on the same line, separated by spaces. A range also works, like [email protected]/24.

Heads up: The moment you add AllowUsers, every user that isn't on the list is denied. That includes root. If you forget to list your own username, you'll lock yourself out.

Before you reload, always check the config for mistakes:

sudo sshd -t

If it prints nothing, the syntax is fine. Now reload SSH:

sudo systemctl reload ssh

On RHEL-based systems the service is called sshd, so use sudo systemctl reload sshd.

Then log in from a second terminal before you close the first one.

To see what SSH is actually using (this includes any files in /etc/ssh/sshd_config.d/), run:

sudo sshd -T | grep -i allowusers

One limit to know about: with this method the SSH port is still open to the whole internet. Strangers can still connect and see the SSH banner, they just can't log in. A firewall rule is stronger because it blocks the connection before it reaches SSH.

Method 5: your provider's cloud firewall

Many providers have a firewall that sits outside your server. AWS calls them Security Groups. DigitalOcean and Hetzner call them Cloud Firewalls. Other providers have their own names.

The idea is the same everywhere. Create an inbound rule like this:

  • Protocol: TCP

  • Port: 22

  • Source: 203.0.113.10/32

The /32 just means "exactly this one address".

The nice thing about this layer is that you can change it from the provider's dashboard without logging in to the server. So if your IP changes, you can update the rule in a minute.

Just remember it can't undo a mistake in the firewall inside the server. For that, you need the provider's web console.

Which one should you use?

Method

Where it blocks

Survives reboot?

Good for

Watch out for

UFW

OS firewall

Yes

Ubuntu and Debian, simplest setup

Add the allow rule before enabling

firewalld

OS firewall

Yes, with --permanent

Rocky, Alma, CentOS, RHEL

Forgetting --reload

iptables

OS firewall

Only if you save the rules

Full control

Rule order and IPv6

sshd_config

SSH daemon

Yes

Limiting specific users

Port is still open to everyone

Cloud firewall

Before the server

Yes

Extra layer, easy to update when your IP changes

Can't fix a lockout inside the server

My rule of thumb: use your OS firewall as the main lock, and add the cloud firewall as a second one if you have it. Use AllowUsers when you want to limit which accounts can log in.

How to test it safely

This is the part most guides skip. Follow this order.

flowchart TD
    A["Add the allow rule for your IP"] --> B["Check it matches your real IP"]
    B --> C["Remove the broad SSH rule<br/>(first session stays open)"]
    C --> D["Log in from a second terminal"]
    D --> E{"Login works?"}
    E -- Yes --> F["Test from a different network<br/>(should be blocked)"]
    E -- No --> G["Fix the rule from your first session"]
    G --> D

Step 1: test from a second terminal. Do this after you've removed the open rule. Testing before that proves nothing, because the open rule lets you in either way. Leave your original session alone. In a new terminal window, run:

ssh -v deploy@your-server-ip

The -v flag shows what's happening, which helps if something fails.

Step 2: prove that strangers are blocked. Switch to a different network, like your phone's hotspot, and try again:

ssh -o ConnectTimeout=5 deploy@your-server-ip

You should not get a login prompt. Depending on your firewall, you'll see a timeout or a refused or "no route" error. Any of those means the block works. (This applies to firewall rules. If you only used AllowUsers, you'll still reach SSH and get "Permission denied" instead.)

Step 3 (optional): set a panic timer. If you're nervous, you can tell the server to turn the firewall off automatically after a few minutes, in case you lock yourself out. On a systemd server with UFW, it looks like this:

sudo systemd-run --on-active=5m /usr/sbin/ufw disable

If everything works, cancel the timer. The command prints the unit name, something like run-r1234.timer:

sudo systemctl stop run-r1234.timer

Use the name it actually shows you. It's a small trick, but it's a nice net to have under you.

What if my IP changes?

This is the biggest downside of IP filtering. Many home internet plans change your public IP every now and then. When that happens, the server stops recognizing you.

You have a few options.

  • Ask your ISP for a static IP. Some charge a bit extra for it.

  • Allow a small range like 203.0.113.0/24. It's less strict, but it survives small IP changes. Only do this if you understand who else is in that range.

  • Use a VPN with a fixed IP, then allow only the VPN's address. This is a clean setup if you travel a lot.

  • Put SSH behind a private network like Tailscale or WireGuard, and allow SSH only on that interface. With UFW and Tailscale that looks like this:

sudo ufw allow in on tailscale0 to any port 22

Whatever you pick, keep your provider's web console handy. It's your way in when your IP changes at a bad moment.

Common mistakes

Here are the ones I'd watch for.

  • Enabling the firewall before adding the allow rule. Allow first, enable second.

  • Whitelisting the wrong IP. If you're on a VPN, your public IP isn't your home IP. Check it with curl -4 ifconfig.me while connected the way you'll normally connect.

  • Sharing an IP without knowing it. Some ISPs put many customers behind one public address (it's called CGNAT). Whitelisting it lets all of them in.

  • Forgetting IPv6. If your server has an IPv6 address, filter that too.

  • Not saving iptables rules. One reboot and your rules are gone.

  • Closing your first session too early. Keep it open until a second terminal can log in.

  • Forgetting about the cloud firewall. Your server rule can be perfect, and the provider's firewall can still block you, or let everyone in.

Don't stop at IP filtering

Restricting by IP is a great extra layer, but it shouldn't be your only one. While you're in there, also do these:

  • Use SSH keys, and turn off password logins with PasswordAuthentication no.

  • Disable direct root login with PermitRootLogin no.

  • Consider Fail2ban to block repeated failed attempts.

IP filtering cuts down the noise. Keys keep out anyone who gets past it.

Wrapping up

Locking SSH down to your own IP takes about five minutes, and it removes you from the target list of every bot scanning for open servers.

The part to remember is the order. Add the allow rule, remove the open rule, then test from a second terminal. Keep your first session alive the whole time, and know where your provider's console is.

Do that, and you get a quieter server without the panicked "I can't get into my own VPS" moment.

Comments(0)

Join the discussion by logging into your account.

No comments yet. Be the first to comment!

Ankit Singh
Ankit Singh

Software Engineer

Passionate developer sharing knowledge about modern web technologies and best practices.

Subscribe to Ankit Singh's Newsletter

Direct email dispatches when new stories are published. Zero algorithms.