← All Writing
March 7, 20267 min read

How to Secure an Always-On Home Server

The safest service on the internet is one that isn’t on it, so start with a private network, then stack simple, independent locks behind it.

YieldA home Linux server you can reach and control from anywhere, that the public internet can’t see, with layered defenses and an alert on every login
DifficultyIntermediate (firewall rules, SSH config, a small web service, systemd)
Total Cook TimeAn afternoon for the layers, plus a session for the remote-control API

Ingredients

The Idea: Don’t Put It on the Internet

The moment a machine answers on the public internet, automated scripts start probing it. They scan huge ranges of addresses, look for a login prompt, try common passwords, and move on. It’s background noise, and it starts within hours.

Most home servers are fine by default because the router doesn’t let that traffic in. The risk starts when you want to reach the server from outside: from your phone on the road, or a laptop at a coffee shop. The tempting fix is to open a port on the router. The better fix is to not open anything at all.

So the plan has two parts. First, make the server reachable only from your own devices. Second, stack independent locks behind that, so a single mistake never exposes the machine.

Layer 1: A Private Network

A private network like Tailscale puts your devices on their own encrypted network, no matter where they are. Your phone at a coffee shop and your server at home can reach each other as if they were on the same Wi-Fi. Everyone else on the internet sees nothing: no open ports, no login prompt, nothing to probe.

Install it on the server and on each device you’ll connect from, sign in to the same account, and that’s it. No router changes. This one step removes most of the risk, because there’s no public door left to attack.

Why this comes first

Every other layer in this post makes a public door harder to get through. A private network means there isn’t a public door. Start here, then add the rest as backup.

Layer 2: A Firewall That Denies by Default

UFW makes firewall rules readable. The strategy: block everything coming in, then allow only what you need, from only where you need it.

🔧 Developer section: UFW setup

Order matters

Allow SSH before you enable the firewall. Enable a default-deny firewall with no SSH rule and you’re locked out on the spot, and you’ll need a keyboard and monitor to fix it.

Layer 3: SSH Keys Only, No Passwords

A password can be guessed. An SSH key, practically, can’t. Once you’ve confirmed you can log in with your key, turn password logins off entirely.

🔧 Developer section: key-only SSH

Keep a safety session open

Test every SSH or firewall change from a second connection while the first stays open. If the new setup is wrong, the old session is how you fix it. Closing the only session before testing is how people get locked out.

Layer 4: Fail2ban

Fail2ban watches login logs and temporarily bans addresses that keep failing. With a private network and key-only SSH it rarely has anything to do, which is the point: it’s there for the day something else is misconfigured.

🔧 Developer section: Fail2ban setup

Layer 5: An Alert on Every Login

Locks are only half of it. You also want to know when someone gets in, even if it’s you. OpenSSH can run a script on every successful login; point that script at an email API and you get a message with the time and source of each login within seconds.

The rule of thumb: if you see a login alert you didn’t cause, that’s your cue to investigate. If you never get alerts you didn’t cause, the layers are doing their job.

A Private Remote Control

SSH is great for a full terminal, but most of the time you don’t need one. You want to run one job, check that the server is healthy, or pull a quick report. A tiny API does that in one request, from a phone shortcut or a script.

Keep it simple: a few capabilities, each doing one thing.

The security rule is the same as everything else here: reachable only over the private network, allowed only from your own devices by the firewall, and every request carries a long secret key that lives outside the code. Three independent locks, so no single mistake exposes it.

🔧 Developer section: keep it running with systemd

No Private Network? Port Knocking as an Alternative

If you can’t use a private network, port knocking is an older technique that hides SSH instead. The SSH port stays closed. To open it, you send connection attempts to a secret sequence of ports, in order; if the sequence matches, the firewall briefly opens SSH for your address only. Scanners see a machine with nothing open.

🔧 Developer section: knockd, briefly

It works, but it means opening router ports, and it’s more moving parts to get right. If a private network is an option, use that instead.

The Checklist

What goes fast

What needs patience

None of these layers is clever on its own. The point is that they’re independent: the network hides the server, the firewall limits who can talk to it, keys make logins unguessable, Fail2ban slows anyone who tries, and alerts tell you about every success. Get the first one right and the rest are insurance.

← Back to all writing