DEV Community

Cover image for Deploy an Eleventy Blog on Cloudflare pages with Strapi, MySQL, and Dokku on a Digital Ocean Droplet, part 2
Victor Feight
Victor Feight

Posted on • Originally published at

Deploy an Eleventy Blog on Cloudflare pages with Strapi, MySQL, and Dokku on a Digital Ocean Droplet, part 2

Table of Contents

Hardening the Ubuntu Dokku Droplet Part 1: Creating a new user in the sudo group

!!! tip
As a preface, to anybody who's interested in hardening your Linux server, I highly recommend "Unix and Linux Administration Handbook 5th edition" which contains the following advice and more.

!!! note
Recall, instructions prefaced with REMOTE are run on my remote Dokku droplet in an SSH terminal.

For further instructions, see here and here.

  1. REMOTE: Set the RAM swap. Although we will be temporarily increasing memory when installing Strapi, using a RAM swap decreases the chances of failure due to low memory when installing NPM packages. For more information, see here.
cd /var
touch swap.img
chmod 600 swap.img
dd if=/dev/zero of=/var/swap.img bs=1024k count=1000
mkswap /var/swap.img
swapon /var/swap.img
echo "/var/swap.img    none    swap    sw    0    0" >> /etc/fstab
Enter fullscreen mode Exit fullscreen mode

Here's my output:

  1. REMOTE: Let's adjust swappiness values and vfs_cache_pressure. swappiness values close to 100 (the default) attempt to load the SWAP with data to free up RAM. For a server, this is better closer to 0: sudo sysctl vm.swappiness=10

vfs_cache_pressure is related to inode/filesystem access and should be set conservatively, so inode information is removed from cache less often:

sudo sysctl vm.vfs_cache_pressure=50


To make these settings persist, sudo nano /etc/sysctl.conf and add the following to the bottom of the file:

vm.swappiness = 10
vm.vfs_cache_pressure = 50
Enter fullscreen mode Exit fullscreen mode


  1. Being root user is a huge security problem, as you could wipe your system in a single command if you're not careful. Let's remedy this by creating our own user account and later we'll be turning off root account.

REMOTE: Create a new user as root with adduser <username>


REMOTE: Grant that user administrative privileges with usermod -aG sudo <username>

  1. REMOTE: Next, we copy Public SSH keys from the root user to our own user. The set of commands is as follows, replace vic with the user you created:
mkdir -p /home/vic/.ssh # Create ssh directory
chmod 700 /home/vic/.ssh # Apply directory permissions
cp /root/.ssh/authorized_keys /home/vic/.ssh/authorized_keys # Copy the ssh authorized key
chown -R vic:vic /home/vic/.ssh # Apply user and group permissions
chmod 600 /home/vic/.ssh/authorized_keys # Apply file permissions
Enter fullscreen mode Exit fullscreen mode
  1. LOCAL: Reconnect to the droplet as your new user. exit the root remote connection, then, on a local Git Bash window, run ssh <user>@<subdomain>


  1. REMOTE: Delete the Docker ports which are opened by default to mitigate security risks.
sudo ufw status

# Delete Docker UFW rules
sudo ufw delete allow 2375/tcp
sudo ufw delete allow 2376/tcp

sudo ufw status
Enter fullscreen mode Exit fullscreen mode

My output:

Nice, we're getting there. 🌷

We've set up a new user account with sudo privileges and added a swap file.

Hardening the Ubuntu Dokku Droplet Part 2: Harden OpenSSH on Ubuntu and disable root

We're almost finished setting up our server. The last things we need to do are harden our security settings for OpenSSH, and install Fail2Ban to prevent unwanted logins.

Information is taken from this guide.

Now that we're logged in as a new user, let's go ahead and harden OpenSSH then install Fail2Ban.

  1. REMOTE: Backup configuration file: sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak

  2. REMOTE: Edit the SSHD configuration with sudo nano /etc/ssh/sshd_config

Change the following settings to more secure ones as follows:

PermitRootLogin no
MaxAuthTries 3
LoginGraceTime 20
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding no
Enter fullscreen mode Exit fullscreen mode


  1. REMOTE: Validate the syntax with sudo sshd -t, which should have no output if everything is correct:

  2. REMOTE: Reload the new settings with sudo service sshd reload

  3. LOCAL: Now, if we attempt to SSH at root, we get the following output:

Alright, good. Now root can no longer login, which was a big security risk. We'll continue by implementing an IP Address Allowlist.

The basic idea behind this is Deny By Default security, in which all inbound and outbound traffic not expressly permitted by SSHD service is blocked. πŸ›‘

Changing your Host to a Static IP and Implementing an IP address Allowlist Part 1

!!! note
The following commands are all local.

Even if by some odd chance your private keys or password gets leaked, if you have implemented an IP address allow list, you can ensure only users on those IP addresses can login. Let's do that now.

!!! tip
For the purposes of this guide, I'll be setting up my Ethernet IP as static, though the instructions are similar for a Wi-Fi adapter.

I'll be taking steps from this guide.

  1. Type ip address into the Windows 10 search bar, then click "Ethernet Settings"

  2. Click "Change Adapter options"

  3. Right click on Ethernet to set up a static IP over Ethernet, or right click on WiFi if your computer is connected via WiFi --> Then click "Status"


  1. Click "Details"

  2. Take note of the following info: IPv4 address, IPv4 subnet mask, IPv4 default gateway, and IPv4 DNS server.

!!! note
As my IP is currently Static, it says DHCP is not enabled.


This information can also be found via ip config /all on the Windows CMD prompt:


Note: the adapter's name is "Ethernet"

  • IPv4 Address: (Preferred)
  • IPv4 Subnet Mask:
  • IPv4 Default Gateway:
  • IPv4 DNS Servers:
  1. We can set our static IP address graphically or through the CMD. To set it graphically, follow the rest of this guide.

To set it manually via CMD using netsh command, keep following:

  1. To set a static IP address to for an adapter named "Ethernet", with subnet mask, and Gateway
   IPv4 Address. . . . . . . . . . . :
   Subnet Mask . . . . . . . . . . . :
   Lease Obtained. . . . . . . . . . : Thursday, February 24, 2022 9:07:06 AM
   Lease Expires . . . . . . . . . . : Saturday, February 26, 2022 9:07:06 AM
   Default Gateway . . . . . . . . . : fe80::10:18ff:fe46:236a%12
   DHCP Server . . . . . . . . . . . :

C:\WINDOWS\system32>netsh interface ip set address name="Ethernet" static
Enter fullscreen mode Exit fullscreen mode

Output of ipconfig /all:

Note: If you're using a Wifi Adapter, check the name in ipconfig /all:

Your adapter name would be "Wi-Fi" (case-sensitive).

  1. To set the DNS servers:
Set DNS servers:
C:\WINDOWS\system32>netsh interface ip set dns name="Ethernet" static
C:\WINDOWS\system32>netsh interface ip add dns name="Ethernet" index=2
Enter fullscreen mode Exit fullscreen mode

Output of ipconfig /all

Great, that's it. We're static. ⚑
There's a lot of other benefits to setting your static IP such as ease of SSH use and port forwarding, but I won't get into that here.

For our next steps, let's finish configuring OpenSSH on our remote server, then configure Fail2Ban.

Changing your Host to a Static IP and Implementing an IP address Allowlist Part 2

!!! note
These instructions are taken from the latter half of this guide.

Now we're going to implement an IP Address Allowlist in both OpenSSH and Fail2Ban.

  1. LOCAL: If you're not already, log on to the remote server again with ssh
  2. REMOTE: press w and enter to see your connecting IP:


!!! note
My remote Dokku server's connecting IP is

  1. REMOTE: Edit our OpenSSH server configuration: sudo nano /etc/ssh/sshd_config and add some configuration directives:
AllowUsers *@ *@
Enter fullscreen mode Exit fullscreen mode

Here we restrict all users to a specific IP address and a range.

To be a bit more secure, you can remove the range if you want.

  1. REMOTE: Run sudo service sshd reload

Congratulations, your SSHD server is hardened. Next, let's install Fail2Ban to prevent any unwanted users from accessing our server.

  1. REMOTE: Let's update with sudo apt update


  1. REMOTE: Install Fail2Ban on our Dokku server with sudo apt install fail2ban


  1. REMOTE: Create a .local Fail2Ban configuration file from the default jail.conf: sudo cp /etc/fail2ban/jail.{conf,local}

  2. REMOTE: Open the jail.conf file with sudo nano /etc/fail2ban/jail.local and uncomment the line starting with ignoreip, add our local IP address followed by all machines to whitelist (insert your IP from earlier here):

ignoreip = ::1

Output from /etc/fail2ban/jail.local

Modify the following additional settings in /etc/fail2ban/jail.local:
For more information on these options and more, see this guide.

!!! note
Set 1d to a negative like -99d if you want to permaban them.

bantime  = 1d
maxretry = 4
Enter fullscreen mode Exit fullscreen mode
  1. REMOTE: Finally, restart Fail2Ban to apply the configurations -- sudo systemctl restart fail2ban

So we've set up a static IP on our host PC, installed Fail2Ban on our remote Dokku server, and whitelisted our host PC's IP on the remote server in both SSHD and Fail2Ban configurations. We've also disabled root login and hardened both configurations against attackers.

It's a lot... but it's necessary in this dog eat dog world. πŸ€·πŸΌβ€β™‚οΈ
Check out your Fail2Ban logs sometime, you might see some surprising brute force and automated attack attempts...

Soon, finally, we will get to the cooler stuff πŸ₯

Setting up an Eleventy blog with MySQL-hosted posts and a Strapi CMS on our shiny new and secure server. πŸ’» πŸ•·

Connecting the dots...getting a blog pushed onto Cloudflare pages, querying our MySQL database on the DO Dokku droplet via a GraphQL playground in Strapi (hosted also on our DO Dokku droplet).

I'll save the nitty-gritty, inner-workings of my blog and how I emulate Eleventy's high performance blog example for later. For now, we just want to connect the dots...let's see what we have and what we still need to do:

  • Install MySQL plugin for Dokku
  • Install Strapi CMS onto Dokku
  • Allow Strapi CMS to talk to our MySQL instance.
  • Query from our MySQL instance using GraphQL query playground (built-in to Strapi).
  • Pull queries into Blog markdown files and present them with Nunjucks templating language (and some JS) in Eleventy.

Top comments (0)