Hardening Ubuntu Server 24.04 LTS - Basic Security Measures
Posted on November 4, 2025 (Last modified on November 5, 2025) • 11 min read • 2,218 wordsStep-by-step guide to securing a freshly installed Ubuntu Server 24.04 LTS with firewall, Fail2Ban, and automatic updates
Recently I rented a Cloud Virtual Private Server (VPS) with pre-installed Ubuntu Server 24.04 LTS. However, before installing the first services on it, the most important step always comes first: securing the system. An unsecured server on the internet is like an open door – it often takes only minutes before the first automated attack attempts start.
The steps described here apply equally if you operate a server in your homelab that is accessible from the internet. Whether cloud VPS or your own hardware – the fundamentals of hardening remain the same.
In this article, I’ll show you how to secure an Ubuntu Server 24.04 LTS with basic but effective security measures. We will go through the most important steps together that every server should undergo before being put into productive use.
Even in a private homelab, security should not be neglected. The reasons are manifold:
For this article you will need:
Before we start hardening, we should get an overview of which services are currently running and which ports are exposed to the outside. This is important to understand what we need to secure.
The ss command (socket statistics) shows us all listening ports:
sudo ss -tulpn
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
tcp LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=1234,fd=3))
tcp LISTEN 0 128 [::]:22 [::]:* users:(("sshd",pid=1234,fd=4))Parameters explained:
-t = TCP connections-u = UDP connections-l = Only listening sockets-p = Shows process information-n = Numeric display (no name resolution)In this example, we see that only SSH (port 22) is listening – a good starting point for a fresh server.
Let’s see which services are active on the system:
systemctl list-units --type=service --state=running
UNIT LOAD ACTIVE SUB DESCRIPTION
ssh.service loaded active running OpenBSD Secure Shell server
systemd-journald.service loaded active running Journal Service
systemd-udevd.service loaded active running Rule-based Manager for Device EventsIf you have a second computer on the network, you can scan from the outside using nmap:
nmap -sV 192.168.1.100
Starting Nmap 7.94
Nmap scan report for homelab-srv (192.168.1.100)
Host is up (0.0012s latency).
Not shown: 999 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.6p1 Ubuntu 3ubuntu13 (Ubuntu Linux; protocol 2.0)The first and most important step is to bring the system up to date. Security updates close known vulnerabilities.
sudo apt update
Reading package lists... Done
Building dependency tree... Donesudo apt upgrade -y
The following packages will be upgraded:
base-files curl libcurl4 openssh-client openssh-server
5 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.If kernel updates were installed, a reboot is required:
sudo rebootUFW (Uncomplicated Firewall) is a user-friendly frontend solution for iptables. It allows us to quickly and easily define firewall rules.
sudo apt install ufw -ysudo ufw allow 22/tcp comment 'SSH'
Rules updated
Rules updated (v6)Set the default policy (block incoming connections, allow outgoing):
sudo ufw default deny incoming
sudo ufw default allow outgoing
Default incoming policy changed to 'deny'
Default outgoing policy changed to 'allow'Enable the firewall:
sudo ufw enable
Firewall is active and enabled on system startupCheck the status:
sudo ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip
To Action From
-- ------ ----
22/tcp ALLOW IN Anywhere # SSH
22/tcp (v6) ALLOW IN Anywhere (v6) # SSHDepending on your requirements, you can open additional ports. Here are some common examples:
Web server
sudo ufw allow 80/tcp comment 'HTTP'
sudo ufw allow 443/tcp comment 'HTTPS'
Docker Swarm (example)
sudo ufw allow 2377/tcp comment 'Docker Swarm'
Proxmox Web Interface (if needed)
sudo ufw allow 8006/tcp comment 'Proxmox Web UI'It’s a best practice not to work as root and to restrict sudo access. Important: We set this up BEFORE we harden SSH and disable root login to avoid locking ourselves out!
sudo adduser frank
Enter new password:
Retype new password:
Enter the full name []: Franksudo usermod -aG sudo franksudo whoami
[sudo] password for frank:
rootIf you’re sure you no longer need root:
sudo passwd -l root
passwd: password expiry information changedSSH is usually the only externally accessible service on a server. That’s why it’s particularly important to secure it well.
If you’re not already using SSH keys, first create a key pair on your local machine:
ssh-keygen -t ed25519 -C "homelab-server"
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/frank/.ssh/id_ed25519):
Enter passphrase (empty for no passphrase): #### Enter password!
Your identification has been saved in /home/frank/.ssh/id_ed25519
Your public key has been saved in /home/frank/.ssh/id_ed25519.pubCopy the public key to the server:
ssh-copy-id ubuntu@192.168.1.100
Number of key(s) added: 1
Now try logging into the machine, with: "ssh 'ubuntu@192.168.1.100'"Test the login with the key:
ssh ubuntu@192.168.1.100
Welcome to Ubuntu 24.04 LTS (GNU/Linux 6.8.0-45-generic x86_64)Now let’s adjust the SSH configuration. First create a backup:
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backupEdit the configuration:
sudo nano /etc/ssh/sshd_config# Change port (optional but recommended)
Port 2222
# Only use SSH Protocol 2
Protocol 2
# Disable root login
PermitRootLogin no
# Disable password authentication (only after SSH key test!)
PasswordAuthentication no
PubkeyAuthentication yes
# Forbid empty passwords
PermitEmptyPasswords no
# Disable X11 forwarding (if not needed)
X11Forwarding no
# Limit MaxAuthTries
MaxAuthTries 3
# Reduce LoginGraceTime
LoginGraceTime 20
# Only allow specific users (optional)
AllowUsers ubuntu
# Only use IPv4 (optional)
AddressFamily inetImportant: If you change the SSH port (e.g., to 2222), don’t forget to open this port in the firewall and close the old one:
sudo ufw allow 2222/tcp comment 'SSH custom port'
sudo ufw delete allow 22/tcpCheck the configuration for syntax errors:
sudo sshd -t
# No output means: Everything OK!Enable and restart the SSH service:
sudo systemctl enable ssh
sudo systemctl restart ssh.socketImportant: Open a second SSH connection to the server BEFORE you close the first one to ensure you can still log in!
Fail2Ban monitors log files and automatically blocks IP addresses after repeated failed login attempts. This protects against brute-force attacks.
sudo apt install fail2ban -yFail2Ban uses a jail.conf that we should not edit directly. Instead, we create a jail.local:
sudo nano /etc/fail2ban/jail.local[DEFAULT]
# Ban time: 1 hour
bantime = 3600
# Time window in which attempts are counted: 10 minutes
findtime = 600
# Maximum number of failed attempts
maxretry = 5
# Ignore local IPs (adjust to your network)
ignoreip = 127.0.0.1/8 ::1 192.168.1.0/24
[sshd]
enabled = true
port = 2222
# If you're using the standard port 22, change this accordingly
logpath = %(sshd_log)s
backend = %(sshd_backend)signoreip to your local network so you don’t lock yourself out. If you changed the SSH port, adjust the port value accordingly.
Start Fail2Ban and enable autostart:
sudo systemctl enable fail2ban
sudo systemctl start fail2banCheck the status:
sudo fail2ban-client status
Status
|- Number of jail: 1
`- Jail list: sshdDetails for a specific jail:
sudo fail2ban-client status sshd
Status for the jail: sshd
|- Filter
| |- Currently failed: 0
| |- Total failed: 0
| `- File list: /var/log/auth.log
`- Actions
|- Currently banned: 0
|- Total banned: 0
`- Banned IP list:To test if Fail2Ban works, you can try logging in multiple times from another computer with a wrong password:
ssh ubuntu@192.168.1.100 -p 2222
ubuntu@192.168.1.100's password: #### Enter wrong password
Permission denied, please try again.
# After 5 attempts: Connection refusedThe IP will now be blocked for 1 hour. You can check this:
sudo fail2ban-client status sshd
Status for the jail: sshd
|- Currently banned: 1
`- Banned IP list: 192.168.1.50If you want to manually unblock an IP:
sudo fail2ban-client set sshd unbanip 192.168.1.50Security updates should be applied promptly. With unattended-upgrades we can automate this.
sudo apt install unattended-upgrades -yEdit the configuration file:
sudo nano /etc/apt/apt.conf.d/50unattended-upgradesUnattended-Upgrade::Allowed-Origins {
"${distro_id}:${distro_codename}";
"${distro_id}:${distro_codename}-security";
// Also install important updates (optional)
// "${distro_id}:${distro_codename}-updates";
};
// Packages that should NOT be automatically updated
Unattended-Upgrade::Package-Blacklist {
// Example: "docker-ce";
};
// Automatically reboot if necessary (caution in homelab!)
Unattended-Upgrade::Automatic-Reboot "false";
// If reboot enabled: set time
Unattended-Upgrade::Automatic-Reboot-Time "02:00";
// Email notification (optional)
// Unattended-Upgrade::Mail "your@email.com";
// Unattended-Upgrade::MailReport "on-change";
// Automatically remove old kernels
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
Unattended-Upgrade::Remove-Unused-Dependencies "true";Enable automatic updates:
sudo dpkg-reconfigure -plow unattended-upgrades
# Select "Yes"Check the configuration:
sudo unattended-upgrades --dry-run --debugAutomatic-Reboot "true") can be problematic in a homelab when services are running. Think carefully about whether you want to enable this option.
After implementing all measures, we’ll perform a final verification.
sudo ss -tulpn | grep LISTEN
tcp LISTEN 0 128 0.0.0.0:2222 0.0.0.0:* users:(("sshd",pid=1234,fd=3))Perfect! Only our custom SSH port is still open.
sudo ufw status numbered
Status: active
To Action From
-- ------ ----
[ 1] 2222/tcp ALLOW IN Anywhere # SSH custom port
[ 2] 2222/tcp (v6) ALLOW IN Anywhere (v6) # SSH custom portsudo systemctl status fail2ban
● fail2ban.service - Fail2Ban Service
Loaded: loaded (/lib/systemd/system/fail2ban.service; enabled; preset: enabled)
Active: active (running) since Mon 2025-11-04 10:15:32 UTC; 2h agonmap -sV 192.168.1.100 -p 1-65535
Starting Nmap 7.94
Nmap scan report for homelab-srv (192.168.1.100)
Host is up (0.0012s latency).
Not shown: 65534 filtered ports
PORT STATE SERVICE VERSION
2222/tcp open ssh OpenSSH 9.6p1 Ubuntu 3ubuntu13Excellent! From the outside, only the custom SSH port is visible.
sudo tail -n 20 /var/log/auth.log
Nov 4 10:30:15 homelab-srv sshd[2456]: Accepted publickey for ubuntu from 192.168.1.50
Nov 4 10:30:15 homelab-srv sshd[2456]: pam_unix(sshd:session): session opened for user ubuntuDepending on your requirements, you can take further security measures:
AppArmor (Application Armor) is a Mandatory Access Control (MAC) system that comes pre-installed with Ubuntu. It restricts the permissions of individual programs by confining them to security profiles. For example, a compromised web server can only access the files it really needs.
Ubuntu 24.04 comes with AppArmor pre-installed. Check if it’s active:
sudo aa-status
apparmor module is loaded.
49 profiles are loaded.
49 profiles are in enforce mode.Lynis is an open-source security audit tool that checks your system for vulnerabilities and configuration problems. It scans hundreds of security aspects (SSH configuration, firewall settings, kernel parameters, installed packages, etc.) and provides you with a detailed report with suggestions for improvement.
sudo apt install lynis -y
sudo lynis audit system
# Lynis performs a comprehensive security scanFor maximum security, you can set up 2FA with Google Authenticator:
sudo apt install libpam-google-authenticator -y
google-authenticator
# Follow the instructions and scan the QR code with your Authenticator appCongratulations! You have successfully hardened your Ubuntu Server 24.04 LTS. The most important security measures are now implemented:
✅ System is up to date ✅ UFW firewall blocks unwanted connections ✅ SSH is secured through key authentication, custom port, and further hardening ✅ Fail2Ban protects against brute-force attacks ✅ Automatic security updates are enabled ✅ Only necessary ports are exposed
Your server is now significantly better protected against attacks and ready for productive use!
Of course, IT security is an ongoing process. Regularly check the logs, keep the system updated, and adapt the security measures to your changing requirements.
Now that your server is secured, you can start installing your services. Here are some ideas:
Have fun hardening your server and enjoy your homelab!