In this post you’ll learn how to spin up your own server and implement some of the essential tools and configurations your server needs before being exposed to the internet.

No server? No problem

Start by visiting linode.com. Linode is your new friend when it comes to cloud compute, whether you’re just looking to experiment or you’re looking to set up something more serious. But before you dive in and sign up, navigate to Linode’s Shared CPU product to see what they offer.

Shared CPU

The Shared CPU instances balance affordability with enough resources to get you up and running while you learn the basics. You can then begin to explore Linode’s more advanced (and pricier) offerings.

Make your way down the page and you’ll find a slider under “Shared CPU Plans”, which will show you what you can get for your budget. Assuming you opt for the $5 per month plan, you’ll get 1 GB of RAM, 1 CPU core, 25 GB of SSD storage and 1 TB total transfer allowance at 40 Gbps in and 1 Gbps out. This is plenty for a small, static website and other casual projects.

Operating system

The best choice of operating system to run on your server will entirely depend on your use case, but here are a couple of personal recommendations:

  • Debian stable (at the time of writing, Debian 12 is Debian’s stable release, codenamed bookworm).
  • Ubuntu Server LTS (long-term support release).

The above recommendations are also two of the most popular and they’re well-known for their stability, active community of users and developers, and overall use. At the time of writing, Linux has a public server market share of around 80%.

Remote access with SSH

With your Linode compute instance plan, operating system and region chosen, along with any optional labels or tags, the next step is to generate an SSH keypair, which will allow you to securely access your server going forward.

As the administrator of your server, it’s essential that you – and only you – have continued and secure remote access.

Generating an SSH keypair

On Linux and Windows systems, entering the following command in either the terminal or PowerShell, respectively, will initiate the creation of your new SSH keypair.

ssh-keygen -t ed25519

The interactive prompts will ask you where you’d like to save your keys and whether you’d like to secure your private key (id_ed25519) with a password.

A password is recommended because if your private key was lost or stolen, the person in possession of it would still need to know your password to use the key, otherwise it couldn’t be decrypted and would therefore be useless. For the same reason, it’s important that your password is strong, unique and, ideally, memorable, such as three or more random words (see Diceware).

If you’d prefer not to protect your key with a password, you can just type nothing in and hit Enter when prompted.

Your new SSH keypair will be stored in either C:\Users\You\.ssh (for Windows) or /home/you/.ssh (for Linux).

Logging in with SSH

Now that you have an SSH keypair ready to go, let’s use it to remotely access the server. Initially you’ll need to log in as the root user:

ssh root@server-ip-address

The above command will attempt a connection over SSH using the default SSH port 22. But what about the SSH key? Let’s put it to use now, after first logging out of the server:

exit

Before we can SSH back in, this time using just an SSH key, we’ll need to copy the public key to the server in such a way that the server will trust it as an authorised key for future connections (the key gets appended to the file at ~/.ssh/authorized_keys).

Here, we’re saying we want to use the (default) identity (-i) file we created earlier:

ssh-copy-id -i ~/.ssh/id_ed25519 root@server-ip-address

Before your local machine makes the connection, you’ll be prompted for the password to decrypt your private key, assuming you set one earlier. You should then see a new prompt in your terminal, indicating that you’re logged into your server as the root user.

Stop using root

To carry out most system-wide configuration, monitoring, maintenance and so on, you’ll need elevated privileges. For example, for creating new users, changing important configuration files and updating software packages.

Going about our business as the root user is all well and good in terms of convenience. After all, root has full control over the system and will therefore never be prompted for a password. But in most cases, so much passwordless power is what we don’t want.

There may come a time when you hit Enter after one too many rm -rf (forced recursive removal of files/directories). That password prompt and the momentary pause it provides could be the difference between a good day and a bad day, or worse…

As a wise admin once said,

Don’t drink and root

Be a wise admin

You’re going to create a new user called admin (feel free to use any other name) and then add it to the sudo group. The name “sudo” is an abbreviation of “superuser do”. Hence, the sudo group and command allows us to do stuff as the superuser – who is? You guessed it: root.

Using sudo and entering your password when prompted, you’ll be able to carry out most of the necessary administrative tasks. Then, going forward, you can be sensible and only log in as the user admin.

Start by upgrading all system packages and installing sudo if it isn’t already installed. Here, && means the next command will only be run if the previous one was successful. Hence, we don’t want to upgrade packages until we know the package repository is updated.

apt update && apt upgrade && apt install sudo

Create the user:

adduser admin

Append (-a) the user to the sudo group (-G) and reload the group:

usermod -aG sudo admin
newgrp sudo

Verify you’re now a ‘sudoer’:

sudo whoami

If the previous command returns “root”, you’ve successfully added your admin user to the sudo group and run a command as root.

Hardening SSH

There are a few changes you’ll want to make to your SSH server configuration, keeping in mind that you’ll now only be using your admin user and avoiding the use of the root user. Note that the lines shown in the sshd_config file below are only those which we’re changing.

Install the text editors nano and/or vim if they’re not already installed, take a look at their manuals (man) to see how to use them, then start editing the config:

sudo apt install nano vim
man nano
man vim
sudo vim /etc/ssh/sshd_config

/etc/ssh/sshd_config

PermitRootLogin no
PubkeyAuthentication yes
PasswordAuthentication no

Firewalling

We’ve made good progress, but there’s more we need to do. SSH hardening will make it more difficult for attackers to gain access, but it won’t stop them trying.

It’s essential that you’re always clear on what ports are open on your server, and why they’re open. For now, the only port we need to keep open is the default port for SSH, so let’s ensure that’s the only port that’s reachable from the outside.

First, we’ll need to install our firewall of choice, the Uncomplicated Firewall (UFW). UFW is a more beginner-friendly interface to the underlying IPTables.

sudo apt install ufw

Before creating any firewall rules, set the default policies. The policy for outgoing traffic will already be to allow everything out. This is fine for our purposes. The important part is to ensure all incoming traffic is denied and that we can only explicitly allow certain traffic in with allow rules.

sudo ufw default deny incoming

Next, add the allow rule for your SSH server listening on port 22. Here, we’re saying we want to allow from any external IP address. This assumes you’ll be accessing your server from a dynamic IP address (one which could change at any time). However, if you know you’ll only be accessing the server from a static IP address, you can replace any with the static address.

sudo ufw allow from any to any port 22

Your firewall is now ready to do its job, but first you’ll need to enable it. This is where we need to be careful not to lock ourselves out. For example, if we had allowed incoming traffic to port 22 only from a static address, and we had initiated the current SSH connection from a different address, we would suddenly find ourselves disconnected and locked out.

If you ever run into this issue, you’ll have to rely on Linode’s Lish Console, which is accessible via the Linode Cloud Manager GUI.

Finally, enable the firewall and verify the rule and policies are active:

sudo ufw enable
sudo ufw status verbose

You should now see the following output:

Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip

To                         Action      From
--                         ------      ----
22                         ALLOW IN    Anywhere
22 (v6)                    ALLOW IN    Anywhere (v6)

So what now?

The beauty of having your own server is that it’s yours to play with, to learn with, to experiment with. These days, there are countless ideas when it comes to server-based projects.

Here are a few of my own suggestions:

  • Build and host a static personal website with Hugo.
  • Host your own mobile push notifications service with ntfy.
  • Store your files privately, manage projects, tasks, calendars and more with Nextcloud.
  • Run a Tor relay to help those who need to stay anonymous online to stay safe.
  • Scan your other systems for security vulnerabilities with Nessus.