Umbrel

Linux Server Security: 5 Steps + the Defaults That Undo Them

Most homelab breaches come from default configs. These 5 steps—SSH keys, UFW, Docker, unattended upgrades, and backups—fix that. Don't skip the gotchas.

Audio Narration Listen to this article
00:00 / 00:00

Linux Server Security: 5 Steps + the Defaults That Undo Them

Most homelab "hardening" guides tell you to install fail2ban and call it a day. The real problem lies somewhere else: in the default configurations that quietly undo your work in silence. Cloud-init drop-ins silently re-enable password logins that you thought you turned off. Docker publishes container ports directly via iptables, bypassing your firewall completely. Unattended upgrades install security patches but never restart the kernel. You thought your server was locked down—while the front door stood wide open.

Real Linux server hardening doesn't require twenty complex tools. It comes down to 5 essential steps, executed cleanly, while knowing exactly what traps can undo each one.

The 5 Steps in 60 Seconds

Step What It Protects Against The Default That Undoes It Estimated Time
1. SSH Keys Only Password brute-force bots Cloud-init drop-in re-enabling passwords 15 min
2. Auto Updates & Reboot Known unpatched kernel CVEs Automatic reboot disabled by default 10 min
3. UFW Default Deny Unintended exposed ports Docker publishing ports outside UFW 10 min
4. Docker Hardening Container breakout reaching root Unprotected port 2375 or root daemon 20 min
5. Encrypted Backups Disk failure and ransomware Untested backup that fails during restore 30 min

Any modest VPS (2 vCPU, 2 GB RAM) runs this baseline effortlessly. The bottleneck is never hardware—it's configuration.

1. SSH: Keys Only, No Passwords, No Root

If you only do one thing today, do this. Password authentication on an exposed SSH port is a permanent magnet for automated internet scanners.

On your local workstation, generate an Ed25519 key pair and copy it to your server:

ssh-keygen -t ed25519 -a 100 -C "you@homelab"
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server-ip

The -a 100 parameter increases key derivation rounds, adding zero cost for you while making stolen key files drastically harder to brute-force.

Next, on the server, create a dedicated drop-in file at /etc/ssh/sshd_config.d/99-hardening.conf:

PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
AllowUsers user

Here is the gotcha: On many VPS cloud images (Debian, Ubuntu), cloud-init drops /etc/ssh/sshd_config.d/50-cloud-init.conf with PasswordAuthentication yes. Because OpenSSH uses the first matching value it finds and reads drop-in files alphabetically, the cloud-init file wins.

Always verify the effective configuration that sshd will actually apply:

sudo sshd -T | grep -Ei 'passwordauthentication|permitrootlogin'

If it still outputs passwordauthentication yes, adjust or remove the winning cloud-init drop-in. Then test syntax and reload:

sudo sshd -t && sudo systemctl reload ssh

Rule of thumb: Always keep your current SSH session open until you have successfully tested a second independent connection in a new terminal window.

2. Automatic Security Updates (With Scheduled Reboot)

Installing patches without rebooting is like cleaning your house without taking out the trash. Critical kernel fixes only take effect once the machine restarts.

Install and configure unattended-upgrades:

sudo apt install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades

Then edit /etc/apt/apt.conf.d/50unattended-upgrades to enable scheduled reboots:

Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "04:00";

Test that the system evaluates updates properly:

sudo unattended-upgrades --dry-run --debug

3. UFW Default Deny (And Docker Bypassing It)

Docker quietly punching holes in your firewall is the single most common vulnerability in homelab setups.

First, set up your standard UFW baseline:

sudo apt install ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw limit 22/tcp
sudo ufw allow 51820/udp
sudo ufw enable

The trap: When you start a container with -p 8080:80, Docker injects rules directly into the iptables DOCKER chain, completely bypassing UFW. Inspect it yourself:

sudo iptables -L DOCKER -n

The solution: Bind internal services exclusively to 127.0.0.1 in your Compose file:

ports:
  - "127.0.0.1:8080:80"

Then route public traffic strictly through an authentic reverse proxy (like Caddy).

4. Docker Daemon Hardening

Never expose the unauthenticated Docker daemon over TCP (port 2375). Verify that nothing is listening:

sudo ss -tlnp | grep 2375

Enable user namespace remapping (userns-remap) and drop privileges in /etc/docker/daemon.json:

{
  "userns-remap": "default",
  "no-new-privileges": true,
  "log-driver": "json-file",
  "log-opts": { "max-size": "10m", "max-file": "3" }
}

Restart the daemon:

sudo systemctl restart docker

5. Encrypted, Offsite, and Tested Backups

A backup you haven't restored is merely a wish, not a backup. Use restic to encrypt data on the client side before sending it offsite:

sudo apt install restic
export RESTIC_REPOSITORY="rclone:remote:homelab-backups"
export RESTIC_PASSWORD="your-long-random-passphrase"

restic init
restic backup /etc /home /srv

Verify your snapshots with a test restore:

restic restore latest --target /tmp/restore-test

Frequently Asked Questions (FAQ)

How do I secure SSH on a Linux server?

Generate an Ed25519 key, copy it using ssh-copy-id, disable password authentication and root login in /etc/ssh/sshd_config.d/, and verify with sudo sshd -T.

Is fail2ban still necessary if passwords are disabled?

If SSH only accepts public keys, fail2ban provides little benefit for SSH because bots cannot guess cryptographic keys.

Does UFW block Docker published ports?

No. Docker modifies iptables before UFW sees the packets. Always bind ports to 127.0.0.1 or filter via the DOCKER-USER chain.