The first 30 minutes on a new Linux server: a security checklist

Semih6 min read

What to do in the first half hour on a fresh Ubuntu or Debian server. SSH keys, firewall, fail2ban, automatic updates, swap and a first backup.

You bought the VPS, the root password landed in your inbox, you’re excited. Bad news: from the moment your server hits the internet, bots from every corner of the world start knocking on its SSH door. Don’t believe me? Check journalctl -u ssh in a few hours. Login attempts as root don’t trickle in one by one, they scroll by in pages.

Good news: nearly all of those bots are looking for easy prey. Close a few doors in the first half hour and you drop off their list. The commands here are for Ubuntu and Debian; on another distribution the logic is the same, only the package manager changes.

Short answer: update the system, create your own user, log in over SSH with keys only, disable root and password logins, open only the ports you need, turn on automatic security updates and take the first backup. The rest is detail.

1. Update first

Provider images are often weeks or months old. First job:

apt update && apt full-upgrade -y

If the kernel was updated, the file /var/run/reboot-required appears. If it’s there, reboot now: rebooting while nothing runs on the server costs nothing.

2. Don’t live as root, create your own user

Root can do anything, accidents included. Create a user with sudo rights for daily work:

adduser semih
usermod -aG sudo semih

3. SSH keys: give up on passwords

Generate a key pair on your own computer and copy the public key to the server:

ssh-keygen -t ed25519 -C "laptop"
ssh-copy-id semih@SERVER_IP

Now the most important rule: without closing the session you already have open, try ssh semih@SERVER_IP in a new terminal. Don’t move on until you’ve seen that the key works and sudo runs. Locking yourself out of your own server is the classic accident on this list.

4. Harden SSH

A separate file is cleaner than editing the main config. There’s a small, sneaky trap here: when OpenSSH sees the same option in several places, it uses the first value it reads, and it reads the files in sshd_config.d in alphabetical order. Some cloud images leave password login on in 50-cloud-init.conf. Start your file name with 00- and your settings are read first:

# /etc/ssh/sshd_config.d/00-hardening.conf
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no

Check the syntax before applying, restart the service, then look at what’s actually in effect:

sudo sshd -t && sudo systemctl restart ssh
sudo sshd -T | grep -E "permitrootlogin|passwordauthentication"

Moving SSH off port 22 cuts the noise in your logs, but it isn’t a security measure. The real protection is having password login switched off.

5. Firewall: open only what you need

ufw ships with Ubuntu; on Debian, install it with apt install ufw. Order matters: allow SSH before you enable the firewall:

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw allow 80,443/tcp
sudo ufw enable

A warning if you use Docker: ports that Docker publishes can bypass ufw rules. A database container started with -p 3306:3306 may be open to the internet even though ufw says it’s closed. Bind services that only the server itself should reach as -p 127.0.0.1:3306:3306.

6. fail2ban: make door-rattlers wait

With password login off, bots can’t get in anyway; here fail2ban mostly cuts noise and wasted resources. Still, it takes a minute:

sudo apt install fail2ban
# /etc/fail2ban/jail.local
[sshd]
enabled = true
maxretry = 5
bantime = 1h

On systems that don’t keep an auth.log, like Debian 12, add backend = systemd to the same section. Check the status with sudo fail2ban-client status sshd.

7. Let security updates arrive on their own

You won’t log in every morning to check for updates. Nobody does. Let the system handle it:

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

This installs security updates only. Major upgrades of PHP or your database stay under your control, as they should.

8. Time zone and clock

If the time in your logs doesn’t match your own, you’ll look in the wrong place when hunting a 3 a.m. error:

sudo timedatectl set-timezone Europe/Istanbul
timedatectl

Use your own zone, of course. The output should say System clock synchronized: yes; SSL certificates and two-factor codes both depend on the correct time.

9. A small swap for a small server

On a server with 1 or 2 GB of RAM, a composer install or a large backup job can exhaust memory. Linux then kills a process, and the one it kills is usually the database. A small swap file turns that sudden crash into a slowdown:

sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

Swap is no substitute for RAM. If the server is swapping all the time, that’s not a tuning problem, it’s a signal to upgrade.

10. First backup, first day

Putting backups off until “the site goes live” is the most expensive delay there is. Keep the rule simple: the database dump and uploaded files go somewhere off the server, every day. Your provider’s snapshots are nice, but if something happens to the provider account, the backup goes with it.

And write this sentence down somewhere: an untested backup isn’t a backup, it’s a hope. Once a month, restore it on an empty machine and see that it actually works.

Checklist

Step Command or file Why
Update apt full-upgrade Closes known holes
User adduser, usermod -aG sudo No daily work as root
SSH key ssh-copy-id Makes password guessing pointless
SSH config 00-hardening.conf Turns off root and password login
Firewall ufw Only 22, 80 and 443 stay open
fail2ban jail.local Makes brute-forcers wait
Auto updates unattended-upgrades Security patches don’t wait for you
Clock timedatectl Logs and certificates see the right time
Swap /swapfile Prevents sudden crashes when memory fills
Backup Off-server, daily Insurance for the bad day

That’s it. Half an hour later you have a server that sits in the “not worth the effort” part of the bots’ lists. Where to look when it slows down one day is a separate post.

If you’d rather hand server setup or a migration to someone, write to me through the quote form.

More from the rulebook

  1. 5 min read

    Tuning PHP-FPM: how to calculate pm.max_children

    Set PHP-FPM's pm.max_children by measuring, not guessing. Measuring process memory, the formula, pm modes, the status page and the slowlog.

  2. 5 min read

    The server is slow: 10 Linux commands to run before you panic

    Ten commands that tell you within five minutes whether a slow Linux server is short on CPU, memory, disk or network, or whether the application is to blame.

Chance

Got a project in mind? Let’s open the box: tell me what you want to build and I’ll prepare a proposal with the scope and a roadmap.

Request a quote

or write directly: email address