Setting Up a VPS with sensible defaults

In this guide I will walk you through setting up a VPS with secure and sensible defaults. First manually so you understand what's happening a bit better and then with cloud-init to automate the whole process.

This guide focuses on Ubuntu.

Manually: Understanding the basics

Step 1: Generate your SSH key

First things first - if you don't already have an SSH key, let's create one on your local machine:

ssh-keygen -t ed25519 -C "[email protected]"
💡
ed25519 keys are more secure than RSA keys. They're also much shorter and easier to work with. Unless you're dealing with ancient systems, there is no reason to use RSA anymore.

Now copy the public key it generated into your VPS provider's SSH keys area. You can get the public key with cat ~/.ssh/id_ed25519.pub if it was stored in the default path.

Go ahead and use this key to create your new server.

Step 2: Create a non-root user

Log into your new server with ssh root@your-server-ip

We shouldn’t generally be interacting with the server as the root user. If compromised, an attackers gets complete system control, and you can accidentally damage critical files with no safety net. A regular user with sudo is safer since you need to explicitly escalate privileges when you need it. Let's create one:

adduser tutorial
adduser tutorial sudo

The first command creates the user and asks you to set a password. The second adds it to the sudo group. We're using tutorial for the non-root user as an example but you can name it whatever you want.

Step 3: Setting up SSH access for the new user

Now we need to copy your SSH key to the new user:

mkdir /home/tutorial/.ssh
cp ~/.ssh/authorized_keys /home/tutorial/.ssh/authorized_keys
chown -R tutorial:tutorial /home/tutorial/.ssh
chmod u+rwx,go-rwx /home/tutorial/.ssh
chmod 600 /home/tutorial/.ssh/authorized_keys
📝
When you create a VPS and provide your public key, they automatically add it to root's authorized_keys file. That's why we can simply copy it over to our new user.
⚠️
SSH is can be picky about permissions. The .ssh directory needs to be 700 (owner-only access) and authorized_keys needs to be 600 (owner read/write only).

Step 4: Harden the SSH configuration

We should go ahead and lock down SSH access. Open the SSH config:

sudo vi /etc/ssh/sshd_config

Find and change these lines to:

PermitRootLogin no
PasswordAuthentication no

Then restart SSH to apply the changes:

sudo systemctl restart ssh
🚨
Before you close your current SSH session, open a new terminal and verify you can log in as your new user: ssh tutorial@your-server-ip.

Some people also like to change the default ssh port from 22 to something else but I won't cover that here as it doesn't affect security in any meaningful way.

Step 5: Set up the firewall

Ubuntu comes with UFW (Uncomplicated Firewall). Let's set it up:

sudo ufw allow OpenSSH
sudo ufw allow http
sudo ufw allow https
sudo ufw enable
sudo ufw status verbose

When you run ufw enable, it'll warn you about disrupting SSH connections. Since we already allowed OpenSSH, you can safely proceed.

Step 6: Enable automatic security updates

Keep your server patched without having to remember to manually update:

sudo apt update
sudo apt install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades

Select "Yes" when asked if you want to automatically download and install updates.

⚠️
While automatic security updates are generally a good idea, they can occasionally cause issues: very rarely, an update might break compatibility with your applications and updates happen at random times, which could coincide with high traffic.

For personal projects and non-critical services, automatic updates are usually worth the peace of mind.

The cloud-init way: automation from the start

Doing all that manually can get old fast. I'll show you how to configure your server automatically on first boot with cloud-init because it is supported by most VPS providers.

Here's a configuration that does everything we just did manually:

#cloud-config
users:
  - name: tutorial
    groups: sudo
    shell: /bin/bash
    sudo: ALL=(ALL) ALL
    lock_passwd: false
    passwd: $6$randomsalt$yourHashedPasswordHere
    ssh_authorized_keys:
      - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... [email protected]

# Disable password authentication for SSH
ssh_pwauth: false

# Disable root SSH login
disable_root: true

# Install required packages
packages:
  - ufw
  - unattended-upgrades

# Configure firewall and security updates
runcmd:
  - ufw allow OpenSSH
  - ufw allow http
  - ufw allow https
  - ufw --force enable
  - dpkg-reconfigure -plow unattended-upgrades

Do you see that passwd: $6$randomsalt$yourHashedPasswordHere part? We need to change that. You can generate it with openssl passwd -6 on your local machine. If you're on Windows then WSL could be a good option to get access to some Linux standard tools such as openssl.

This will prompt you for the password and output the hash. Copy the output and replace $6$randomsalt$yourHashedPasswordHere with it.

Also, don't forget to change the ssh_authorized_keys part to include the key you generated in the first step.

Then go ahead and use it during the VPS creation step (it should mention cloud-init somewhere). I am using Hetzner (referral URL: https://hetzner.cloud/?ref=KHnQDk7q1gJg, you'll get 20€, I'll get 10€) but many other providers support cloud-init as well, such as AWS, Google Cloud Platform, Azure, DigitalOcean, Linode, Vultr and OVHcloud.

What's next?

And with that your foundation is ready! With either approach, you now have:

  • A non-root user with sudo access
  • SSH key authentication only (no passwords)
  • Root login disabled
  • Basic firewall protecting your server
  • Automatic security updates

Author’s Note: Claude assisted with editing and proofreading this post but the concept and final content are my own.