Secure VPS Setup Guide
I have a few tutorials and posts in the pipeline that readers will be able to follow along with and I was debating writing this post so that if they had issues with different builds, distros and versions, I could provide a base build so everyone would have the same build to start from.
I thought it might be over the top until this thought popped into my head.
Every developer, sysadmin, hobbyist and IT student will need to spin up a VPS at some point and may not necessarily know how to do it securely. That was the decision made so let’s look at setting up a server securely.
A virtual private server is a virtual server that is hosted for you, usually on a cloud platform such as AWS, DigitalOcean, Hetzner and many others. The server will usually be provisioned for you and you’ll be given a method of accessing the server. From that point the server is yours to do as you please.
The problem is that the provisioned virtual machines are not always set up the same, some are more secure than others so this guide is designed to get your server up and running securely.
Also note that this isn’t restricted to just cloud servers, this can be used to set up a virtual machine on your local computer or even physical server hardware.
We will be using Ubuntu 26.04 LTS as our operating system. All hosts will provide slightly different configurations and access methods and we will address some of these as we go but in this guide we will run tests to make sure everything is set up correctly and how to configure it.
For this tutorial I will be using a VirtualBox VM that I will simulate the build on and I will highlight areas that usually differ between VPS providers.
Our server has now been provisioned and we will have a public IP or DNS address to connect to our server along with an authentication method to access it.
Depending on how your host provisions machines you might have a password to access your server or was asked to set up an SSH Key Pair to connect which is a lot more secure. Don’t worry, we will be setting up SSH Key access if your host didn’t set up by default.
I now have the details on how to access my server. I have been given the host my-server.dev.davidbarszczak.com and I have username and password. Open a terminal or PowerShell if on Windows and type in the following command. In my case the username I have is ubuntu.
ssh ubuntu@my-server.dev.davidbarszczak.com
If your host set up ssh keys your command might look like the following but don’t worry, we’ll be going through that process shortly.
ssh -i ~/.ssh/id_ed25519 ubuntu@my-server.dev.davidbarszczak.com
On the first connection you can continue but once you have connected to your server from your machine you shouldn’t be asked this question again. If you are asked again this is a sign that something is wrong. Either the server you think you’re connecting to has changed or that something is in the middle trying to intercept your connection and you should stop connecting. Your server could be at risk.
Now we have logged into our server we should begin securing it as soon as we can.
In my example I login as a non-root user but some host may provide you with a root account to access your server which is very insecure so let's take a look at how we fix that first.
If you’re unsure of the terminology, the user root is known as the root account and it has full unrestricted permissions to do anything on your server. It is normal and best practice not to use this account to login with.
ubuntu@ubuntu-server:~$ # Logged in as ubuntu
root@ubuntu-server:~# # Logged is as rootIf you’re still unsure of the account you’re logged in as you can run the whoami command.
root@ubuntu-server:~# whoami
rootIf you are a root user then follow these simple commands to create your new user and add to the sudo group. Run the following command to create a new user. Replace <newuser> with your desired username:
adduser adminReturns:
New password:
Retype new password:
passwd: password updated successfully
Changing the user information for admin
Enter the new value, or press ENTER for the default
Full Name []:
Room Number []:
Work Phone []:
Home Phone []:
Other []:
Is the information correct? [Y/n]
sudo usermod -aG sudo adminNotice on my server it asked for Full Name, Room Number etc. You can just hit enter and leave those fields blank. If you followed my commands then you now have a new user with the username admin.
We can now test our new account to see if it works before we continue.
su - admin # Switch to the admin user.
whoami
admin
sudo apt update[sudo: authenticate] Password:
Get:1 http://security.ubuntu.com/ubuntu resolute-security InRelease [137 kB]
Hit:2 http://archive.ubuntu.com/ubuntu resolute InRelease
Get:3 http://archive.ubuntu.com/ubuntu resolute-updates InRelease [137 kB]
Get:4 http://security.ubuntu.com/ubuntu resolute-security/main amd64 Packages [265 kB]
Get:5 http://security.ubuntu.com/ubuntu resolute-security/main Translation-en [69.2 kB]
Hit:6 http://archive.ubuntu.com/ubuntu resolute-backports InRelease
Get:7 http://security.ubuntu.com/ubuntu resolute-security/universe amd64 Packages [128 kB]
Get:8 http://archive.ubuntu.com/ubuntu resolute-updates/main amd64 Packages [301 kB]
Get:9 http://archive.ubuntu.com/ubuntu resolute-updates/universe amd64 Packages [190 kB]
Fetched 1,228 kB in 0s (2,552 kB/s)
20 packages can be upgraded. Run 'apt list --upgradable' to see them.Let’s take a quick look at what we did in the commands above.
su - admin : We switched from the user root to the user admin.
whoami : We checked to see if we are now logged in as the user admin
sudo apt update : We ran an update command which requires Super User (root) privileges. The command sudo essentially means Super User Do.
Congratulations! You have a non-root account you can use to access your server. You can even connect to your account over ssh.
ssh admin@my-server.dev.davidbarszczak.comadmin@my-server.dev.davidbarszczak.com's password:
Welcome to Ubuntu 26.04 LTS (GNU/Linux 7.0.0-27-generic x86_64)
* Documentation: https://docs.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
System information as of Thu 2 Jul 18:13:25 UTC 2026
System load: 0.16
Usage of /: 22.0% of 24.44GB
Memory usage: 16%
Swap usage: 0%
Processes: 114
Users logged in: 0
IPv4 address for enp0s3: 10.0.2.15
IPv6 address for enp0s3: fd17:625c:f037:2:a00:27ff:fe62:fcce
Expanded Security Maintenance for Applications is not enabled.
19 updates can be applied immediately.
6 of these updates are standard security updates.
To see these additional updates run: apt list --upgradable
Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status
The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.This is a great start but using passwords to authenticate is very insecure as it’s very easy for a hacker to compromise the password. We’ll look into setting this up ssh public and private key authentication and tighten up the access rules.
In this section we’re going to set up SSH Key Authentication Only to access our server. This means that you say goodbye to passwords and hackers will have a more difficult time gaining access to your server.
On your LOCAL machine you need to generate an SSH Key Pair. This will create you 2 files. A Public Key and a Private Key.
The way this works is that you get a Public Key that you can upload to any server that you want access to. You don’t have to worry about who gets this because all you can do with it is give you access to something. You also get a Private Key which you need to keep safe and secure and make sure nobody gets access to this key. Someone with this key has access to any server that has the matching public key!
To create the key pair all we need to do is run the ssh-keygen command on your LOCAL machine. If you set a password you will need to enter it every time you use the key, this may help if the key is ever stolen but that choice is up to you.
ssh-keygenGenerating public/private ed25519 key pair.
Enter file in which to save the key (/home/david/.ssh/id_ed25519):
Created directory '/home/david/.ssh'.
Enter passphrase for "/home/david/.ssh/id_ed25519" (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/david/.ssh/id_ed25519
Your public key has been saved in /home/david/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:YL9Nfab1LihkdS30pS/oZUBnhUTrg667+7TvaMndwOQ david@ubuntu-server
The key's randomart image is:
+--[ED25519 256]--+
| ooo.|
| . =..|
| o . +.+.|
| . o .oo= o|
| S ..oB*o |
| +o..=Eo.|
| .o.o+* +.|
| .o*oo..|
| =B+oo..|
+----[SHA256]-----+You can see from my command it created my Private Key at /home/david/.ssh/id_ed25519 and my Public Key was created at /home/david/.ssh/id_ed25519.pub
All we need to do now is to copy the contents of the public key and place it in a file in our non-root user account.
Login to the REMOTE server as the user that you created and create a file at ~/.ssh/authorized_keys and paste the line from your Public Key.
You can now login to your server with the following command, password free and secure!
ssh -i ~/.ssh/id_ed25519 admin@my-server.dev.davidbarszczak.comOnce you have tested your connection and confirmed that you can login we can move on to the next section of disabling password authentication completely. Although we can now access the server using our key, you still have the option to use your password which means that right now we’re no more secure than we were previously but we needed to do this first because once we disable password authentication we will only be able to access with our Private Key.
On your server get sudo privileges by using sudo su - and take a look at the file /etc/ssh/sshd_config using nano or vim.
We want to look for the following in the file and make the following changes.
Look for PermitRootLogin
On my server the line looks like this. The comment usually means that this is the default and if that’s the case this is configured to allow root logins but not with a password.
#PermitRootLogin prohibit-password
As we want to disable root login completely we need to uncomment and change to no.
PermitRootLogin no
We need to do the same thing for PasswordAuthentication and on my server it looks like the following.
#PasswordAuthentication yes
Again we need to uncomment and change the value to no.
PasswordAuthentication no
In some instances you might have a second file /etc/ssh/sshd_config.d/50-cloud-init.conf where you will need to do the same thing for PasswordAuthentication I noticed this on my server and made the change there too.
Once the changes are made we need to restart the ssh daemon and our changes will be applied. The you will not be able to ssh login as root at all and you won’t be able to login with a password to any account.
systemctl restart sshd
systemctl status sshd ssh.service - OpenBSD Secure Shell server
Loaded: loaded (/usr/lib/systemd/system/ssh.service; disabled; preset: enabled)
Active: active (running) since Thu 2026-07-02 20:10:20 UTC; 11s ago
Invocation: aa93c64659f241d6a3c3b369307f68a1
TriggeredBy: ● ssh.socket
Docs: man:sshd(8)
man:sshd_config(5)
Process: 3168 ExecStartPre=/usr/sbin/sshd -t (code=exited, status=0/SUCCESS)
Main PID: 3173 (sshd)
Tasks: 1 (limit: 1888)
Memory: 1.5M (peak: 1.9M)
CPU: 24ms
CGroup: /system.slice/ssh.service
└─3173 "sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups"
Jul 02 20:10:20 ubuntu-server systemd[1]: Starting ssh.service - OpenBSD Secure Shell Server…
Jul 02 20:10:20 ubuntu-server sshd[3173]: Server listening on 0.0.0.0 port 22.
Jul 02 20:10:20 ubuntu-server sshd[3173]: Server listening on :: port 22.
Jul 02 20:10:20 ubuntu-server systemd[1]: Started ssh.service - OpenBSD Secure Shell server.If you try to connect to your server with the following command that forces your client to use password and not the key you configured earlier you can see that the server refused to allow authentication when you look through the output.
ssh -v -o PubkeyAuthentication=no admin@my-server.dev.davidbarszczak.comdebug1: Authentications that can continue: publickey
debug1: No more authentication methods to try.Congratulations, your server is now configured to only allow ssh keys and won’t allow the root user to login. Let’s move onto getting the server up to date now we know we have a safe space to work.
It’s important to keep your server up to date so that if any packages have bug fixes or security patches we won’t suffer with the bugs or be left running a vulnerable server. Luckily Ubuntu makes this very simple for us.
Login to your server as your non-root user and run the following commands.
sudo apt updateThis will update all of the package repos and versions to the latest versions so that the upgrade process will know if later versions have been released for your operating system.
sudo apt upgradeThis will give you a list of packages that will be upgraded and ask if you want to continue. Say yes to the prompt and the server will begin to upgrade the packages. At the end it will tell you if it restarted any services and if a server restart is required. If so, restart your server and log back in as your non-root account.
You can configure unattended upgrades but you need to consider if this is the right thing for your server. For our little project or personal sandbox you’re probably fine. You do need to remember though that by default only “safe” packages will be updated. You will still need to login and run updates manually. I have a reminder in my phone to update twice a week.
If you’re running a production server that’s dependent on revenue then you might want to run the upgrades at a safe time, be confident that the upgrade worked and that everything is still up and running after. In a production environment we have automated upgrades but they are monitored, run when we want them to on servers we’re happy to automate and we get alerts when they upgrade, what they upgrade and if they need to be rebooted.
There is no set correct answer on if you should or shouldn’t. It all depends on what the server is used for and how critical there’s no downtime or failure.
If you decide you want to enable it though you can use the following commands.
sudo dpkg-reconfigure unattended-upgradesA prompt will appear in your terminal asking if you want to "Automatically download and install stable updates?".
Use your arrow keys to select Yes and press Enter.
Your server is now configured to run updates every day.
The last thing we would normally do if the server has a direct connection to the internet like a VPS might normally have is install Fail2Ban.
Even though we have secured our server by disabling root logins and password logins it doesn’t stop hackers and bots from attempting. Although I feel pretty happy that the server won’t be compromised this way it can still use a lot of CPU responding to all the requests which slows our server down and if you’re using something like an AWS t3.nano or DigitalOcean Basic Droplet you could really notice the server running slowly.
This is where Fail2Ban comes in. It monitors logs or the systemd journal to look for failed login attempts, if it gets enough failed attempts defined by your config it creates a firewall rule to block traffic from the offending IP address for an hour by default. The firewall blocking connections uses a lot less CPU and usually once the attacker stops getting connections they will move on to the next target.
Installing Fail2Ban is very simple to configure.
sudo apt update
sudo apt install fail2ban -yBy default Fail2Ban usually configures SSH but we are going to create our local configuration. Create this file /etc/fail2ban/jail.local and enter the following.
[DEFAULT]
# Whitelist your own trusted IP ranges (separated by spaces) so you don't lock yourself out
ignoreip = 127.0.0.1/8 ::1 192.168.1.0/24
# Global default ban metrics
bantime = 10m
findtime = 10m
maxretry = 5
[sshd]
enabled = true
port = ssh
maxretry = 3
findtime = 10m
bantime = 1hAfter we save that configuration we just need to start the service.
sudo systemctl enable fail2ban --now
sudo systemctl status fail2banNow if 3 failed ssh login attempts are detected the IP address will be banned for 1 hour.
With this setup you will have a hardened OS that you can use for almost every project that requires a Linux Server.
We picked Ubuntu mainly because of the community and tutorials available online. Other distros have good communities too but I found if I google something like “How to install nginx” nearly always Ubuntu Tutorials show first.
We talked about non-root users and why password authentication is not a good idea.
We talked about why allowing root authentication is bad.
We updated our server and ways to keep it that way.
We ended on setting up Fail2Ban to keep our server secure and protect our CPU.
If you want to take this further you can learn how to use UFW which is the Ubuntu Firewall. I didn’t go over it here because the providers we use have some kind of firewall protection and generally speaking as a DevOps engineer we would use and trust that as a firewall solution and wouldn’t generally configure one on the server too. Although there can be exceptions where we would.
I hope this post helped you build your VPS Server.