Basic VPS security checklist
A fresh VPS is set up for convenience, not defense, by default: root logs in with a password, the firewall is off, and there are no automatic updates. This checklist closes eight basic gaps in one pass — afterwards your server will be noticeably more resistant to the automated scanning and password guessing that never stops against any public IP address on the internet.
What you'll need
- An mHost VPS on Ubuntu 22.04/24.04, Debian 11/12, or AlmaLinux/Rocky Linux 8/9.
- SSH access as
root— usually the only option on a brand-new server. - Access to the VMmanager 6 panel — as a safety net in case you make a mistake with the firewall or SSH and lose remote access: the panel has a browser-based server console that doesn't depend on SSH. If you haven't logged in yet, start with "How to log in to VMmanager 6".
- An hour or so of free time: a few steps need to be verified with a new connection before you close the old one.
The checklist at a glance
In short — all eight items, each covered in detail below:
- Update the system — install all available package updates.
- Create a sudo user — stop working as root all the time.
- Set up SSH — key-based login instead of a password, root login over SSH disabled.
- Enable a firewall — ufw (Ubuntu/Debian) or firewalld (AlmaLinux/Rocky), deny everything by default.
- Install fail2ban — automatic IP banning for repeated failed logins.
- Close unused services and ports — fewer listening ports means a smaller attack surface.
- Enable automatic security updates — unattended-upgrades (Ubuntu/Debian) or dnf-automatic (AlmaLinux/Rocky).
- Change default passwords — the temporary root password and any passwords left over from VMmanager recipes.
Step 1. Update the system
Always start here — some vulnerabilities are closed simply by updating packages, and the rest of the checklist is better done on an up-to-date system.
Ubuntu / Debian
sudo apt update
sudo apt upgrade -yCheck whether a reboot is needed (usually after a kernel update):
[ -f /var/run/reboot-required ] && echo "Reboot needed" || echo "No reboot needed"AlmaLinux / Rocky
sudo dnf update -yCheck whether a reboot is needed:
sudo dnf install -y dnf-utils
needs-restarting -rReboot the server if needed:
sudo rebootStep 2. Create a sudo user instead of root
Working as root day to day is a risk: any mistake in a command runs with full privileges, and compromising the one account hands over the whole server. Set up a separate user with sudo and use it for everyday work.
Ubuntu / Debian
sudo adduser admin
sudo usermod -aG sudo adminAlmaLinux / Rocky
sudo useradd -m -s /bin/bash admin
sudo passwd admin
sudo usermod -aG wheel adminFeel free to replace admin with any other name — the rest of the article uses this one.
Verify the new user actually has root privileges:
su - admin
sudo whoamisudo whoami will ask for the admin user's password (not root's) and should print root.
Step 3. Set up SSH: keys and hardening
A password can be brute-forced — an SSH key technically can't be. Later in this step, root login over SSH gets disabled completely, so before continuing, make sure key-based login as the admin user actually works.
Generate a key pair on your own computer (not on the server):
ssh-keygen -t ed25519 -C "you@example.com"You can just press Enter at the path and passphrase prompts — the key will be saved as ~/.ssh/id_ed25519 (private) and ~/.ssh/id_ed25519.pub (public).
Copy the public key to the server, to the admin user:
ssh-copy-id -i ~/.ssh/id_ed25519.pub admin@203.0.113.10If ssh-copy-id isn't available (for example, in Windows PowerShell without the OpenSSH utilities), do the same thing manually:
cat ~/.ssh/id_ed25519.pub | ssh admin@203.0.113.10 "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"Verify it: ssh admin@203.0.113.10 should now log you in without asking for a password.
Disable password authentication and root login — on the server, edit /etc/ssh/sshd_config:
PasswordAuthentication no
PermitRootLogin noCheck the configuration syntax before restarting — that's cheaper than fixing the server blind after a mistake:
sudo sshd -tRestart SSH:
# Ubuntu/Debian
sudo systemctl restart ssh
# AlmaLinux/Rocky
sudo systemctl restart sshdWithout closing your current session, open a new terminal window and verify key-based login as admin — only close the old session once you've confirmed everything works.
For deeper SSH protection — a non-standard port, AllowUsers, limiting login attempts — see "Hardening SSH".
Step 4. Enable a firewall: ufw or firewalld
A firewall with a "deny everything not needed" policy is basic protection against any service you never meant to expose.
Ubuntu / Debian (ufw)
sudo apt install -y ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw enable
sudo ufw status verboseAlmaLinux / Rocky (firewalld)
sudo systemctl enable --now firewalld
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --reload
sudo firewall-cmd --list-allFor the full rundown of ufw commands — opening arbitrary ports, listing and deleting rules — see "The ufw firewall: managing ports".
Step 5. Install fail2ban
ufw and firewalld decide which ports are visible from outside, but they don't stop someone from guessing passwords against an SSH port that's already open. fail2ban watches the logs and adds a temporary ban itself for any IP that fails to log in too many times.
Ubuntu / Debian
sudo apt install -y fail2banAlmaLinux / Rocky
fail2ban isn't in the standard repositories — enable EPEL first:
sudo dnf install -y epel-release
sudo dnf install -y fail2ban fail2ban-systemd fail2ban-firewalldEnable and start the service:
sudo systemctl enable --now fail2banBetter not to touch settings in jail.conf — the file can get overwritten on a package update. Create jail.local instead:
sudo nano /etc/fail2ban/jail.local[DEFAULT]
ignoreip = 127.0.0.1/8 ::1
[sshd]
enabled = true
bantime = 1h
findtime = 10m
maxretry = 5enabled— thesshdjail is off by default injail.conf(except on Debian/Ubuntu, where the package turns it on already via its owndefaults-debian.conf); the line above turns it on explicitly and identically on every distro.bantime/findtime/maxretry— how long to ban for, over what window failed attempts are counted, and how many attempts are allowed before a ban; these values are stricter than the defaults (bantimedefaults to10m; here it's an hour).ignoreip— addresses that never get banned; add your own static IP here, space-separated, if you have one — otherwise you risk banning yourself.
Apply the configuration:
sudo systemctl restart fail2banCheck that the sshd jail is active:
sudo fail2ban-client status
sudo fail2ban-client status sshdTo unban an IP address early:
sudo fail2ban-client set sshd unbanip 198.51.100.23For a deeper look at filters, actions, and the recidive jail for repeat offenders, see "Fail2ban: protection against brute-force".
Step 6. Close unused services and ports
A firewall blocks ports from the outside, but an unnecessary listening service on the server is a risk on its own: a mistake in a firewall rule, a second network interface, or a tunnel — and whatever was supposedly closed turns out to be reachable. Fewer running services means less that can be broken into.
Check what's actually listening:
sudo ss -tulpn-t/-u— TCP and UDP;-l— listening sockets only;-p— which process holds the port (needssudo, otherwise the process name is hidden);-n— show ports and addresses as numbers, don't resolve names.
Check which services are enabled and running:
systemctl list-units --type=service --state=running
systemctl list-unit-files --type=service --state=enabledCommon candidates to disable on a bare VPS with no desktop environment, if you don't use them: avahi-daemon, cups, rpcbind. If you're on Debian/Ubuntu without your own mail server, the local MTA (exim4 or postfix) usually listens only on 127.0.0.1 and isn't exposed — but it's still worth checking with ss.
Stop a service and disable its autostart:
sudo systemctl disable --now <service>If you don't need it at all, remove the package entirely:
# Ubuntu/Debian
sudo apt purge <package>
# AlmaLinux/Rocky
sudo dnf remove <package>Check the result with ss -tulpn again — the list of listening ports should be shorter and easier to account for.
Step 7. Enable automatic security updates
Step 1 updated the system once; this step gets security updates installed by themselves, without you, from now on.
Ubuntu / Debian
sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgradesdpkg-reconfigure creates /etc/apt/apt.conf.d/20auto-upgrades with this content:
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";The main logic lives in /etc/apt/apt.conf.d/50unattended-upgrades: by default it installs only security updates, not package version upgrades.
Check that everything is configured without waiting for the real scheduled run:
sudo unattended-upgrade -v --dry-runAlmaLinux / Rocky
sudo dnf install -y dnf-automatic
sudo nano /etc/dnf/automatic.confIn the [commands] section:
[commands]
upgrade_type = security
apply_updates = yesEnable the timer:
sudo systemctl enable --now dnf-automatic.timerStep 8. Change default passwords
The temporary root password mHost emailed you when the server was activated is the first thing worth changing, if you haven't already done it on first login:
passwdAs the admin user with sudo, you can also change another user's password, including root's:
sudo passwd rootIf you deployed any software through a ready-made VMmanager 6 recipe, some of them leave their own default password (for example, Zabbix: Admin/zabbix, Redmine: admin/admin) — change those too, right after your first login to the application's web interface. For the full list of recipes and their quirks, see "Ready-made VMmanager 6 recipes".
What's next
All eight checklist items are done — the server has basic security in place. From here it's worth going deeper on each topic: "The ufw firewall: managing ports" — the full rundown of ufw commands; "Hardening SSH" — a non-standard port, restricting users and login attempts; "Fail2ban: protection against brute-force" — filters and actions for other services, not just SSH.