Fresh Ubuntu Server install. The installer finished, you're at a shell, and the clock is ticking before someone puts a workload on it. Here's the checklist I run on every new box — in order — before it touches anything real.
1. Update everything first
apt update && apt full-upgrade -y
apt autoremove -y
Do this before anything else. You don't want to harden a box and then have an update undo half of it.
2. Set the hostname
hostnamectl set-hostname web01
echo "127.0.1.1 web01" >> /etc/hosts
Do this now. Changing the hostname later breaks certificates, monitoring agents, and anything that baked the old name in at install time.
3. Set timezone and sync NTP
timedatectl set-timezone Europe/Berlin
timedatectl set-ntp true
timedatectl status
Wrong timestamps break logs, cron jobs, certificates, and make incident timelines useless. Fix it early.
4. Create a non-root admin user
adduser lukas
usermod -aG sudo lukas
Never work as root day-to-day. Create your personal user now, add it to sudo, and log in as that user for everything below.
Test it before you lock root out:
su - lukas
sudo whoami
# → root
5. Set up SSH key authentication
On your local machine, generate a key if you don't have one:
ssh-keygen -t ed25519 -C "your-server"
Copy it to the server:
ssh-copy-id lukas@your-server-ip
Test it connects without password, then lock down SSH:
nano /etc/ssh/sshd_config
Change or add these lines:
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
Port 22
Restart SSH — but keep your current session open until you've verified the new session works:
systemctl restart sshd
Open a second terminal and test before closing the first. If you lock yourself out now it's your own fault.
6. Configure UFW firewall
ufw default deny incoming
ufw default allow outgoing
ufw allow ssh
ufw enable
ufw status verbose
If you changed the SSH port:
ufw allow 2222/tcp
Add rules for whatever the server actually runs — HTTP, HTTPS, custom ports — before enabling, not after.
7. Install fail2ban
apt install fail2ban -y
systemctl enable --now fail2ban
Default config blocks IPs after 5 failed SSH attempts. Good enough for most servers. Check status with:
fail2ban-client status sshd
8. Set up unattended security updates
apt install unattended-upgrades -y
dpkg-reconfigure --priority=low unattended-upgrades
Say yes. See the full guide for what to configure beyond the defaults.
9. Set the correct locale
locale-gen en_US.UTF-8
update-locale LANG=en_US.UTF-8
Missing locale causes weird warnings from Perl, Python, and half the tools you'll install later. Easier to fix now.
10. Disable unused services
# Check what's listening
ss -tlnp
# Disable anything you don't need, e.g.:
systemctl disable --now snapd
systemctl disable --now avahi-daemon
Less attack surface. Less noise in your logs.
11. Configure the swap file (if the box has low RAM)
fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab
Not a substitute for real RAM. But it stops the OOM killer from taking down your app when something spikes.
12. Snapshot / base image
If this is a VM — Proxmox, Hyper-V, ESXi, cloud provider — take a snapshot right now. Before any role or application gets installed. You'll want to roll back to this exact point at least once.
13. Document it
Hostname, IP, subnet, SSH port, installed roles, who manages it. One line in a notes file or wiki. If it's not written down it doesn't exist when you're debugging at 2 AM six months from now.
The one-shot script
Adjust the variables at the top, run as root on a fresh install:
#!/bin/bash
HOSTNAME="web01"
USERNAME="lukas"
TIMEZONE="Europe/Berlin"
hostnamectl set-hostname $HOSTNAME
echo "127.0.1.1 $HOSTNAME" >> /etc/hosts
timedatectl set-timezone $TIMEZONE
timedatectl set-ntp true
apt update && apt full-upgrade -y
adduser --gecos "" $USERNAME
usermod -aG sudo $USERNAME
ufw default deny incoming
ufw default allow outgoing
ufw allow ssh
ufw --force enable
apt install -y fail2ban unattended-upgrades
systemctl enable --now fail2ban
locale-gen en_US.UTF-8
update-locale LANG=en_US.UTF-8
echo "Done. Set up SSH keys and disable password auth manually."
Related guides on this blog:
- Set Up Automatic Updates on Ubuntu/Debian — step 8 in more detail
- Linux User Management — adduser, groups, sudo, SSH keys — step 4 and 5 expanded