How to secure an Ubuntu server
Updated
To secure an Ubuntu server, allow SSH with keys only, run a default-deny firewall, install security updates automatically, block repeated login failures, expose only the services you need, and keep tested backups. Those six steps stop the vast majority of attacks a public server sees.
The commands are for Ubuntu 24.04 LTS and work on Debian 12 with small differences. Run them as a user with sudo. If you have not done the basics yet, start with first steps on a new VPS.
1. Patch first, then stay patched
sudo apt update && sudo apt full-upgrade -y
sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
Check that the timer is active:
systemctl list-timers | grep apt
Security updates now install every day. Check now and then whether a reboot is pending with cat /var/run/reboot-required.
2. Lock down SSH
Keys only
Make sure your key works for a sudo user (see SSH keys). Then:
sudo tee /etc/ssh/sshd_config.d/10-hardening.conf >/dev/null <<'EOF'
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
LoginGraceTime 20
X11Forwarding no
AllowUsers deploy
EOF
sudo sshd -t && sudo systemctl reload ssh
AllowUserslimits SSH to the accounts you name. Replacedeploywith your user.sshd -tvalidates the file; if it prints nothing, the syntax is good.
Test from a new terminal before you close the current one.
Check the effective settings
Drop-in files are read in order, and the first value wins. Confirm what sshd actually uses:
sudo sshd -T | grep -E 'permitrootlogin|passwordauthentication|allowusers'
If a cloud-init file such as 50-cloud-init.conf sets PasswordAuthentication yes, your 10- file is read first and wins.
3. Default-deny firewall with UFW
sudo apt install -y ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw limit OpenSSH # allow SSH, rate limit repeated attempts
sudo ufw allow 80,443/tcp # only if you run a web server
sudo ufw enable
sudo ufw status numbered
ufw limit blocks an address that opens six or more connections in 30 seconds, which slows scanners.
UFW covers IPv6 as long as IPV6=yes is set in /etc/default/ufw (the default on Ubuntu).
Restrict SSH to your own addresses
If you connect from a fixed address, allow SSH only from there:
sudo ufw delete limit OpenSSH
sudo ufw allow from 198.51.100.0/24 to any port 22 proto tcp # example range
Docker warning
Docker writes its own iptables rules, and published container ports bypass UFW. Bind containers to localhost (127.0.0.1:8080:80) and put a reverse proxy in front, or filter at the cloud firewall. The Docker Compose guide shows the pattern.
4. Add a cloud firewall in front
A firewall outside the server keeps working even if someone disables UFW, and one rule set can cover many servers. On Wolkra, firewalls are stateful, support inbound and outbound rules, apply to servers or label selectors and cost nothing. A typical rule set:
| Direction | Protocol | Port | Source |
|---|---|---|---|
| Inbound | TCP | 22 | your office or VPN range |
| Inbound | TCP | 80, 443 | any |
| Inbound | ICMP | – | any |
See firewalls in the docs for the steps.
5. Block brute force with Fail2ban
sudo apt install -y fail2ban python3-systemd
sudo tee /etc/fail2ban/jail.local >/dev/null <<'EOF'
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
backend = systemd
[sshd]
enabled = true
EOF
sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd
backend = systemd reads the journal directly, which is how Ubuntu 24.04 logs SSH. Add your own address to ignoreip in [DEFAULT] so you never ban yourself.
6. Reduce what is listening
Every open port is something to patch. List them:
sudo ss -tulpn
For each line, ask whether it needs to be reachable from the internet. Databases, Redis and admin panels usually do not. Bind them to 127.0.0.1 or to a private network address, and remove packages you don't use:
sudo apt purge --autoremove <package>
7. Protect the kernel and network stack
Ubuntu's defaults are sensible. A few extra settings help on a public server:
sudo tee /etc/sysctl.d/90-hardening.conf >/dev/null <<'EOF'
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.tcp_syncookies = 1
kernel.kptr_restrict = 2
kernel.dmesg_restrict = 1
EOF
sudo sysctl --system
Skip the redirect settings if the server routes traffic, for example as a VPN gateway.
8. Use sudo carefully
- Give sudo only to people who need it:
getent group sudolists them. - Never put
NOPASSWD: ALLon interactive accounts. Limit passwordless sudo to specific commands for automation. - Set a password on your sudo user even if SSH uses keys:
sudo passwd deploy.
9. Watch logs and logins
last -n 20 # recent logins
sudo journalctl -u ssh --since today
sudo journalctl -p err -b # errors since boot
For file integrity and a quick audit, Lynis is a good start:
sudo apt install -y lynis
sudo lynis audit system
It prints a hardening index and a list of suggestions. Treat them as ideas, not orders.
The Wolkra console shows CPU, disk and network graphs per server (monitoring). A sudden traffic or CPU spike is often the first sign of a compromised service.
10. Secure the account, not only the server
Server hardening is wasted if someone can log in to your cloud account. In the Wolkra console:
- turn on two-factor authentication (TOTP);
- review active sessions and sign out old ones;
- give team members the lowest role that works;
- use API tokens that are read-only, IP-restricted and expiring where you can.
See account security and API tokens.
11. Back up, and test the restore
Security includes recovery. Enable daily backups or snapshots and keep at least one copy outside the server. Then restore once to a new server to prove it works. The backup strategy guide has a full plan.
Checklist
- Automatic security updates on
- SSH keys only, root login off,
AllowUsersset - UFW default deny, only needed ports open
- Cloud firewall in front, SSH limited to known ranges
- Fail2ban running
- Nothing unexpected in
ss -tulpn - Sysctl hardening applied
- Sudo limited, 2FA on the cloud account
- Backups enabled and a restore tested
If something goes wrong while you tighten rules, the browser console and rescue mode in the server view get you back in without SSH.
Frequently asked questions
What is the most important step to secure an Ubuntu server?
Use SSH keys and disable password login. Most attacks on new servers are automated password guesses, and key-only login stops them.
Do I need Fail2ban if passwords are disabled?
It is less critical, but it still cuts log noise and slows scanners. It is also useful for web logins and other services that use passwords.
Should I change the SSH port?
It reduces log noise but is not real security. Restricting SSH to known source addresses with a firewall does far more.
Is UFW enough, or do I need a cloud firewall too?
UFW protects the server from inside. A cloud firewall filters traffic before it arrives and still works if someone breaks the server's own rules. Using both is a good default.
Do these steps work on Debian 12?
Almost all of them. Debian uses the same packages; install sudo and ufw first if they are missing.