Securing a fresh VPS for IoT comes down to six steps, done in order: SSH key authentication, disable password login, enable a firewall that only opens what you need, install fail2ban, turn on automatic security updates, and create a non-root user for daily work. None of these are advanced. All of them matter, and skipping any one of them is how IoT projects end up compromised.
The problem: a default VPS is not a secure VPS
A freshly provisioned VPS arrives with root login enabled, password authentication on, no firewall, and no monitoring for repeated login attempts. That’s a reasonable default for a provider to ship, because hardening is a personal decision (different projects need different rules), but it means the responsibility sits with you, on day one, before you install anything else. Automated scanners find new IP addresses and start probing SSH within minutes of a server going online. This isn’t a hypothetical risk, it’s the default background noise of having a public IP address.
Step 1: SSH key authentication
Generate a key pair on your own machine if you don’t already have one:
ssh-keygen -t ed25519 -C "your-email@example.com"
Copy the public key to your server:
ssh-copy-id youruser@your-server-ip
Test that you can log in using the key before moving to the next step. This matters: if you disable password login before confirming key-based login actually works, you can lock yourself out entirely.
Step 2: Disable password authentication
Edit /etc/ssh/sshd_config and set:
PasswordAuthentication no
PermitRootLogin no
Then restart SSH:
sudo systemctl restart ssh
This single change removes the most common attack vector against a VPS: brute-force password guessing. Once password auth is off, an attacker needs your actual private key, not a guessable password, to get in.
Step 3: Create a non-root user
Running everything as root is unnecessary risk: any mistake or compromised process has full system access. Create a dedicated user with sudo privileges instead:
adduser yourname
usermod -aG sudo yourname
Use this account for daily work, and reach for sudo only when you actually need elevated privileges.
Step 4: Enable a firewall with ufw
Ubuntu’s uncomplicated firewall (ufw) is genuinely uncomplicated, which is exactly what you want here:
sudo ufw allow OpenSSH
sudo ufw enable
That’s the baseline: SSH allowed, everything else denied by default. From there, open only what each specific service needs as you install it, for example:
sudo ufw allow 51820/udp # WireGuard
sudo ufw allow 8883/tcp # MQTT over TLS
The discipline that matters here isn’t the tool, it’s the habit: every port you open should map to a service you’re actually running and understand, not a default left open “just in case”.
Step 5: Install fail2ban
fail2ban watches log files for repeated failed login attempts and temporarily bans the offending IP address. It’s a useful second layer even after disabling password authentication, since it also catches abuse against other services (web admin panels, for example) that might still use passwords.
sudo apt install fail2ban
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
The default configuration covers SSH out of the box. Service-specific jails (for Nginx, for example) can be added as you add services that need them.
Step 6: Automatic security updates
Unattended upgrades apply security patches automatically, without requiring you to remember to check:
sudo apt install unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgrades
This is one of those steps that feels optional until the month you forget to update manually and a known vulnerability sits unpatched on a public-facing server for weeks.
What this checklist deliberately doesn’t cover
Application-level security (MQTT authentication and ACLs, Node-RED’s adminAuth, ThingsBoard’s own user permissions) is covered in each service’s own dedicated guide, because the right configuration genuinely differs by software. This checklist is the baseline every server should have regardless of what runs on top of it.
Quick reference: the checklist in order
| Step | What it stops |
|---|---|
| 1. SSH key authentication | Brute-force password guessing becomes irrelevant |
| 2. Disable password auth + root login | Removes the most common attack vector entirely |
| 3. Non-root daily user | Limits blast radius of any mistake or compromised process |
| 4. ufw firewall, deny by default | Closes every port you’re not actively using |
| 5. fail2ban | Auto-bans repeated attack attempts against any service |
| 6. Unattended security upgrades | Closes known vulnerabilities without manual effort |
If you’re choosing a provider with this checklist in mind, LumaDock‘s VPS plans ship on Ubuntu 22.04/24.04 LTS templates that this entire checklist applies to directly, no provider-specific adjustments needed.
Verifying each step actually worked
It’s worth confirming each change took effect rather than assuming, since a typo in a config file can leave a setting silently unapplied. After completing the checklist, run through these checks from a separate terminal session (so you don’t lock yourself out if something’s wrong):
# Confirm password auth is actually disabled
ssh -o PreferredAuthentications=password youruser@your-server-ip
# Should be refused, not prompted for a password
# Confirm the firewall is active and only the expected ports are open
sudo ufw status verbose
# Confirm fail2ban is running and watching SSH
sudo fail2ban-client status sshd
# Confirm unattended-upgrades is actually configured to run
sudo unattended-upgrade --dry-run --debug
Keep your current SSH session open while testing the password-auth change specifically, in a second terminal window, until you’ve confirmed key-based login still works. This is the single most common way people accidentally lock themselves out of a fresh VPS.
Additional hardening worth considering
The six steps above are the baseline every server should have. A few further steps are worth knowing about, even if they don’t apply to every setup:
- Fail2ban jails for specific services, not just SSH, once you’re running a web-facing admin panel (Node-RED’s editor, ThingsBoard’s login page) that accepts its own credentials separately from SSH.
- Limiting SSH access to specific IP ranges via ufw, if you only ever connect from a small, known set of locations (a home IP, an office IP). Not always practical with dynamic home IPs, but a meaningful extra layer when it is.
- Two-factor authentication for SSH, using a tool like Google Authenticator’s PAM module, adds a second factor beyond the key itself. Worth it for anything genuinely high-stakes; often more friction than benefit for a personal smart-home project.
- Regular review of
lastandauth.logfor login activity that doesn’t match your own usage pattern, a simple habit that catches problems earlier than waiting for something to visibly break.
What “secure enough” actually looks like
It’s worth being honest that no checklist makes a server perfectly secure, and chasing that standard usually isn’t a good use of time for most projects on this site. The six steps in this guide address the overwhelming majority of real-world automated attacks, which is what an internet-facing VPS actually faces most of the time, rather than a targeted, sophisticated attacker. For most home, small business and even much industrial-adjacent use covered on this site, completing this checklist genuinely is “secure enough” to stop worrying about and move on to actually building the project.
Frequently asked questions
Should I change the default SSH port from 22?
It reduces automated scanning noise in your logs, but it’s not real security on its own, since a determined scan will find a non-standard port too. Worth doing as a minor extra step, not a substitute for the steps above.
How often should I check for updates manually, if unattended-upgrades is running?
Roughly monthly is reasonable, mainly to catch anything unattended-upgrades didn’t apply automatically (major version upgrades are usually excluded by default) and to review fail2ban’s ban log for anything unusual.
Is this checklist different for a VPS running Docker?
The same steps apply at the host level. Docker adds its own considerations (container isolation, not exposing the Docker socket, being careful with published ports) covered in Docker on a VPS.
What’s the single highest-impact step if I only do one thing today?
Disabling password authentication. It removes the attack that automated scanners attempt constantly and successfully against servers that skip this step.
What do I do if I think my VPS has already been compromised?
Treat it as compromised rather than trying to clean it in place: take a snapshot for forensic reference if you want to understand what happened, then rebuild from a fresh image with this checklist applied from the start, rather than trusting a server that may have a hidden backdoor installed. Change any passwords or keys that were used on it, on the assumption they may have been captured.
Common reasons people skip this, and why they don’t hold up
“It’s just a small personal project, nobody’s targeting me specifically.” True, and also irrelevant: automated scanners don’t target specific people, they scan entire IP ranges constantly looking for any server with weak defaults. Scale of the project has no bearing on whether it gets probed.
“I’ll do it once everything’s working, I don’t want to break anything mid-setup.” This is backwards in practice: hardening a server before installing services is simpler than hardening one with live services running, since there’s nothing yet that a misconfigured firewall rule could disrupt. The order in this guide (hardening first, services after) exists specifically to avoid this trap.
“My provider already secures the infrastructure.” Providers secure the physical infrastructure and the hypervisor layer; what runs inside your VPS, the operating system, the services, the configuration, is entirely your responsibility. This division of responsibility (sometimes called the shared responsibility model) is standard across virtually every hosting provider, not a gap specific to any one company.
“I’ll just remember to be careful.” Careful manual practice doesn’t stop automated scanning, which doesn’t care how careful you are, only whether the specific weaknesses it checks for (password auth enabled, no firewall) are present. The checklist exists because it removes the weaknesses scanners look for, not because it requires ongoing vigilance to work.
A simple way to confirm nothing was missed
Once all six steps are complete, an external port scan from a separate machine (not the server itself) gives an honest outside view of what’s actually reachable:
nmap -Pn your-server-ip
The result should show only the ports you deliberately opened (SSH, and whatever services you’ve since added), nothing else. Any unexpected open port is worth investigating immediately, since it usually means either a service binding to all interfaces by default when you expected it to stay local-only, or a firewall rule that’s broader than intended.
Does enabling ufw risk locking myself out if I get a rule wrong?
Yes, this is a real risk worth respecting: always confirm sudo ufw allow OpenSSH (or the equivalent rule for your actual SSH port) is in place and active before running sudo ufw enable, and keep a second connection method (most providers offer a web-based console as a fallback) in mind in case something goes wrong.
Is fail2ban necessary if I’ve already disabled password authentication?
It adds value beyond SSH specifically, since it also protects any other service on the server that still uses password-based login (a web admin panel, for example), and it provides useful visibility into who’s probing your server even if those attempts are already doomed to fail.
How long does the entire checklist actually take for someone doing it for the first time?
Realistically twenty to thirty minutes, including the verification steps above, for someone following the commands directly rather than troubleshooting unfamiliar territory. It’s a genuinely small time investment relative to the protection it provides.
Should I repeat this checklist if I rebuild the server from scratch later?
Yes, every time, since a fresh VPS image always reverts to the unhardened defaults described at the start of this guide regardless of how the previous instance was configured. Keeping this guide bookmarked, or your own notes based on it, makes repeating it quick.
Does any of this change if the VPS is only ever reachable through a VPN, never directly from the internet?
The risk profile is genuinely lower in that case, but the steps remain cheap enough relative to the protection they provide that skipping them on the assumption of “it’s behind a VPN anyway” isn’t recommended. A compromised device already inside the VPN, or a future change to the network setup, can both expose a server that was previously only reachable through the tunnel.