VPS setup on digitalocean

In this post I will document my setup of an Ubuntu 22.04 VPS at Digital Ocean. I will provide links to resources at digitalocean.com (and elsewhere) that guided the choices I made. Of course, this is just one way to do it. This post is primarily a documentation of the process for myself. The results may, or may not, be helpful to you. So, be sure to read mutiple resources if you are trying this yourself.

Create a droplet

The first step is create a droplet at digitalocean.com . Droplet is the name Digital Ocean gives to its VPS. See How to create a droplet at digitalocean.com for a more detailed overview. Some choices I made are:

  • New York datacenter
  • Ubuntu 22.04 LTS image
  • Droplet type: Basic, regular with SSD ($6/mo for 1GB ram, 25GB SSD disk 1000GB transter)
  • Chose to connect to droplet with password (for now)
  • I enabled improved metric monitoring and alerting (free)
  • I gave the droplet a Hostname under Finalize Details and made sure to select the correct project.

Click Create droplet and wait for a minute or so for the droplet to be created-- an IP should appear on the control board.

Intial server setup

The next step is to do some setup, following: Initial Server Setup with Ubuntu 22.04 . Everything is pretty straightforward, but it's nice to follow a guide to make sure that I don't miss anything.

Create a new user

First we create a new user so that we don't have to do everything as root. To do this we ssh to the new droplet using the IP from the control panel (I'll use xxx.xxx.xx.xxx as a stand-in). Of course you'll need to use the password that you gave when creating the droplet:

$ ssh root@xxx.xxx.xx.xxx

To create a new user (I'll use username as a stand-in here) the command is:

# adduser username

As always, be sure to create strong password and save with a password manager.

Admin privileges

Next we want to give the new user admin privelges so that we can execute sudo commands with the account:

# usermod -aG sudo username

Change the ssh port

The default port for ssh is port 22. This port gets a lot of malicious activity for obvious reasons, so I change my ssh port. This means we have to edit the /etc/ssh/sshd_config file. I will use vim, but nano, vi, etc. will work.

# vim /etc/ssh/sshd_config

To change the port to YYYY find the line that has #Port 22 and change it to Port YYYY. Finally, restart the ssh daemon

# systemctl restart ssh

We can check that the new port is selected using

# ss -tlpn | grep ssh

It should be clear that ssh is listening on port YYYY.

Setup ssh key

I want to setup ssh key access to the server for my new user account. To do this I will start by generating a new ssh key on my laptop using:

  • The rsa algorithm -t rsa
  • 4096 bits -b 4096
  • Specify a filename for the key -f keyfile
  • Specfiy the username and host in the comment -C "username@xxx.xxx.xx.xxx"

$ ssh-keygen -t rsa -b 4096 -f ~/.ssh/keyfile -C "username@xxx.xxx.xx.xxx"

We can check the keyfile and keyfile.pub files have been created using:

$ ls ~/.ssh/

Now we can copy the public key to the new server (using port YYYY):

$ ssh-copy-id -i ~/.ssh/keyfile.pub -p YYYY username@xxx.xxx.xx.xxx


If you get an error about too many authentication errors this is apparently caused by having many/(more than one) ssh keys and can be resolved using the following:

$ ssh-copy-id -o IdentitiesOnly=yes -i ~/.ssh/keyfile.pub -p YYYY userame@xxx.xxx.xx.xxx

Finally, I will add this identity to my ~/.ssh/config file on my laptop by adding the following entry:

Host           my-host-name
HostName xxx.xxx.xxx.xx
IdentityFile ~/.ssh/keyfile
User username

This will allow me to ssh to the machine without specifying the keyfile, port, etc using the simple command:

$ ssh my-host-name

Disable Root Login

Once access is setup and tested as described above it's a good idea to disable root login on the machine. DigitalOcean has a good overview here: How To Disable Root Login on Ubuntu 20.04 and the process is simple. First, edit the sshd_config with vim (or nano, vi, etc):

$ sudo vim /etc/ssh/sshd_config

Scroll down to find the Authetication section and change the line that says:

PermitRootLogin yes

so that it reads

PermitRootLogin no

Next, we have to restart the sshd daemon using

$ sudo systemctl restart ssh

Finally, you should log out and try to log in using root-- this should now fail.

Setup ufw firewall

Now we setup UFW for some basic protections. This also a good time to make sure all of the above works and we can ssh to the droplet using the ssh key setup above-- this must work before setting up elements of the firewall that will restrict access! Start by logging out of the root account and then ssh to the droplet as shown above:

$ ssh my-host-name

A quick test to make sure that ufw is inactive should produce:

$ sudo ufw status
Status: inactive

Next, let's start with defaults:

  • Allow all outgoing
  • Deny all incoming

$ sudo ufw default allow outgoing
$ sudo ufw default deny incoming

We can make sure that IPv6 is enabled using (it should be on our Ubuntu 22.04 server):

$ grep IPV6 /etc/default/ufw

Next, we enable incoming ssh on port YYYY-- we won't be able to access the server after enabling the firewall if we don't do this!

$ sudo ufw allow YYYY/tcp

Finally, let's enable ufw and check the status:

$ sudo ufw enable
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup
$ sudo ufw status
Status: active

To Action From
-- ------ ----
YYYY/tcp ALLOW Anywhere
YYYY/tcp (v6) ALLOW Anywhere (v6)

Notice the warning that ssh connections might be disrupted. You have to type y and enter to enable.

Install nginx

Next up I will install the nginx web server. Following How To Install Nginx on Ubuntu 22.04 , this is done with a simple:

$ sudo apt update
$ sudo apt install nginx

With nginx installed it will now show on the ufw list:

$ sudo ufw app list
Available applications:
Nginx Full
Nginx HTTP

For now, I will allow 'Nginx HTTP' to test my IP using a browser:

$ sudo ufw allow 'Nginx HTTP'
$ sudo ufw status
Status: active

To Action From
-- ------ ----
YYYY/tcp ALLOW Anywhere
Nginx HTTP ALLOW Anywhere
YYYY/tcp (v6) ALLOW Anywhere (v6)
Nginx HTTP (v6) ALLOW Anywhere (v6)

nginx has a default/example ready to go. At this point we can type/paste the IP address of our droplet, xxx.xxx.xx.xxx, into a browser and see the Welcome to nginx! page.

Useful nginx commands

While we are here, it's useful to document some basic commands for nginx for future reference:

$ sudo systemctl stop nginx
$ sudo systemctl start nginx
$ sudo systemctl restart nginx

There is also a reload command, but I typically restart:

$ sudo systemctl reload nginx

Finally, nginx is setup to start when the server reboots. This is useful when updating the server and a reboot is needed. This behavior can be started or stopped using:

$ sudo systemctl enable nginx
$ sudo systemctl disable nginx

We can always check on the status of nginx using

$ sudo systemctl status nginx

Setting up server blocks

This section is for demonstration purposes and will only work if you have a domain name purchased and DNS setup. As a placeholder I'll use testsite, but for this site I would substitute chrisstrelioff.ws. The following steps would be sufficient to have http setup for testsite. I'll go over securing the site with https below.

To start, we create the need directories and make sure ownership and permissions are set:

$ sudo mkdir -p /var/www/testsite/html
$ sudo chown -R $USER:$USER /var/www/testsite/html
$ sudo chmod -R 755 /var/www/testsite

Next, we create a basic index.html file to display using vim

$ sudo vim /var/www/testsite/index.html

Following How To Install Nginx on Ubuntu 22.04 , the file contents are

<title>Welcome to testsite!</title>
<h1>Success! The <u>testsite</u> server block is working!</h1>

Next we create a configuration file for testsite

$ sudo vim /etc/nginx/sites-available/testsite

with the contents

server {
listen 80;
listen [::]:80;

root /var/www/testsite/html;
index index.html index.htm index.nginx-debian.html;

server_name testsite www.testsite;

location / {
try_files $uri $uri/ =404;

Finally, to make testsite active (this won't work here for reasons discussed above) the file we created in /etc/nginx/sites-available/ has to have a symbolic link to the /etc/nginx/sites-enabled/ directory. We do this with

$ sudo ln -s /etc/nginx/sites-available/testsite /etc/nginx/sites-enabled/

There are now two site enables on the VPS

  • The default site-- we see this when using the IP
  • The testsite-- we would see this if we had the domain name and DNS setup
We can see that is the case by listing the contents of the sites-enabled directory:

$ ll /etc/nginx/sites-enabled/
total 8
drwxr-xr-x 2 root root 4096 Nov 10 21:02 ./
drwxr-xr-x 8 root root 4096 Nov 2 20:09 ../
lrwxrwxrwx 1 root root 34 Nov 2 20:09 default -> /etc/nginx/sites-available/default
lrwxrwxrwx 1 root root 35 Nov 10 21:02 testsite -> /etc/nginx/sites-available/testsite

The arrows indicate that there is a symbolic link to the files shown. As you can see, this single nginx server can host multiple sites at the same time as long as there are sufficient computing resources available.

Securing nginx with https

A final step in this setup is to secure the site, following How To Secure Nginx with Let's Encrypt on Ubuntu 22.04 .

Installing Certbot

As suggested in the post linked above, install certbot with snap. Apparently snap is available from the get-go, so we only need to make sure that snapd core is updated:

$ sudo snap install core
[sudo] password for username:
core 16-2.57.2 from Canonical✓ installed
$ sudo snap refresh core
snap "core" has no updates available

This is a fresh install of Ubuntu 22.04 on the droplet, so I don't have to worry about removing old versions of certbot. The install command is:

$ sudo snap install --classic certbot
certbot 1.32.0 from Certbot Project (certbot-eff✓) installed

Finally, as suggest in the post, we create a symbolic link for certbot to run from /usr/bin/:

$ sudo ln -s /snap/bin/certbot /usr/bin/certbot

Now, certbot can be run by typing the name. For example:

$ certbot --version
certbot 1.32.0

Check nginx's configuration

In order to create an SSL certificate, the nginx server blocks have to be setup as discussed above. In particular, testsite and its www counterpart need to be listed in the server_name line. This can be check using:

$ more /etc/nginx/sites-available/testsite | grep server_name
server_name testsite www.testsite;

If you don't get something similar, with testsite replaced with your domain name the server block setup should be checked carefully-- see above, as well as the links to original source posts!

Update ufw to allow https

Next up, the firewall needs to updated to allow for https traffic. We did not allow that above, so this has to be changed. This is accomplished by enabling 'Nginx Full' and disabling 'Nginx HTTP':

$ sudo ufw allow 'Nginx Full'
Rule added
Rule added (v6)
$ sudo ufw delete allow 'Nginx HTTP'
Rule deleted
Rule deleted (v6)

Changes can be confirmed by checking the status:

$ sudo ufw status
Status: active

To Action From
-- ------ ----
YYYY/tcp ALLOW Anywhere
Nginx Full ALLOW Anywhere
YYYY/tcp (v6) ALLOW Anywhere (v6)
Nginx Full (v6) ALLOW Anywhere (v6)

The results should look something like this-- there is also entries for ssh that we setup above.

Get an SSL certificate

Okay, we should be ready to generate our SSL certificate using the following command (again, change testsite with you domain name):

$ sudo certbot --nginx -d testsite -d www.testsite

If everthing goes well you are asked a set of questions:

  • Asked to provide an email
  • Asked to agree with Terms of Service here
  • Asked if you want emails from EFF (Electronic Frontire Foundation) - you should say yes :)
If everthing goes well, your account should be registed and a certificate/key created in /etc/letsencrypt/live/testsite/

If you start a browser and go to testsite it should now automatically redirect to https. You can test the strength of the setup at the SSL Labs Server Test. As suggested on the digitalocean post, How To Secure Nginx with Let's Encrypt on Ubuntu 22.04 , the results are an A grade!

Auto-renewal for Certbot

The certificates that are generated only last for 90 days. The certbot install is supposed to configure automatic renewall. We should check:

$ sudo systemctl status snap.certbot.renew.service
[sudo] password for username:
○ snap.certbot.renew.service - Service for snap application certbot.renew
Loaded: loaded (/etc/systemd/system/snap.certbot.renew.service; static)
Active: inactive (dead)
TriggeredBy: ● snap.certbot.renew.timer

This response is a little cryptic-- it's hard to know what it means. We can try a test/dry-run and should get something like the following if everthing is good to go:

$ sudo certbot renew --dry-run
Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/testsite.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Account registered.
Simulating renewal of an existing certificate for testsite and www.testsite

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Congratulations, all simulated renewals succeeded:
/etc/letsencrypt/live/testsite/fullchain.pem (success)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Wrapping Up

That's it for this post. We went from nothing to a VPS running Ubuntu 22.04, nginx, and https working! That's enough for one day...