Why Bother with HTTPS?
In the not to distant future the web will require a SSL certificate on all websites. As it is now Google penalizes websites for using HTTP and plans to flag websites that are not using HTTPS as unsafe in Chrome. If that isn’t enough Google prioritizes websites that use HTTPS in Google search results. It’s the future. Fortunately we can acquire a free SSL certificate from the Let’s Encrypt certificate authority.
Let’s Encrypt & Nginx Reverse Proxies
In order to install a Let’s Encrypt certificate on our Dockerized WordPress we need to use a reverse proxy which, if you are familiar with Antsle and running Docker apps on Antsle Antlets, then you probably are already familiar with serving Docker apps with the Antsle’s Nginx reverse proxy.
We begin by creating an Antlet that can be cloned for this and future Dockerized projects using HTTPS.
Spin Up a New Ubuntu LXC
If you are not familiar with creating a new server on Antsle the documentation for Antsle can walk you through the process. What is important is that you spin up a Ubuntu 18 LXC bare metal server and not a KVM virtual machine. Since an LXC relies on the host machine’s kernel and operating system to function, the Antsle can access the ssl certificates that we will install on the new Ubuntu LXC server whereas KVM servers run their own operating system, with its own kernel, and virtual hardware separately from the Antsle so the Antsle does not have direct access to the Antlet’s SSL certificates.
Okay, you have a new Ubuntu 18 LXC Antlet, before you start the Antlet we need to set up a network bridge with a virtual nic. This bridge will allow us to connect our Antlet to our local network which is useful for development and debugging.
Set Up a Bridged Network with Virtual NIC
Quoting from Antsle’s documentation:
Bridged networking allows you to attach an additional virtual network interface to an antlet. This virtual NIC will connect to one of the physical ethernet ports on the rear of the Antsle by way of an internal bridge. Then the antlet can be accessed directly on the local LAN.
Antsle
Again, if you are not familiar with how to set a bridged network the Antsle docs can walk you through the process.
Once the virtual nic is created you can start the server.
Using Netplan Configure a Static IP Address
Now we have a live Ubuntu Antlet but before we can access the Antlet on our local network we need to configure Netplan to configure the network interface which will give us an ip address for our browser to hit the server.
Configuring Netplan
SSH into the new Ubuntu Antlet and run the following:
cd /etc/netplan
ls
You should see something like this where I have added an additional ethernet eth1 with a static ip:
network:
version: 2
ethernets:
eth0: {dhcp4: true}
eth1:
dhcp4: no
addresses:
- 10.0.0.92/24
gateway4: 10.0.0.1
nameservers:
addresses: [8.8.8.8, 1.1.1.1]
Close the file and run:
netplan apply
Reboot the Antlet. Once we’re back up we can get the ip address for our local network by running:
ip a
Which should display something like the following. Scroll way down to find eth1 with our bridged local network ip address which in my case is 10.0.0.92 which will probably be different in your environment. If you are on a Mac like I am the ip will be 10.0.0.xx and on a PC something like 192.168.1.xx
79: eth1@if80: mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 52:54:00:b1:10:d0 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 10.0.0.92/24 brd 10.0.0.255 scope global eth1
valid_lft forever preferred_lft forever
inet6 fe80::5054:ff:feb1:10d0/64 scope link dadfailed tentative
valid_lft forever preferred_lft forever
Now we install Docker and Docker Compose.
Install Docker Engine
Run the following to install Docker:
apt-get update
apt install apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable"
apt update
apt-cache policy docker-ce
# confirm Installed: (none)
apt install docker-ce
systemctl status docker
With docker installed we can move onto the installation of Docker Compose.
curl -L "https://github.com/docker/compose/releases/download/1.24.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
If using curl doesnβt work, try pip.
# install python3
apt update
apt install python3
# install pip3
apt update
apt install python3-pip
# install docker-compose
pip3 install docker-compose
# confirm that docker-compose is installed
docker-compose --version
Now that we have an Antlet with Nginx, Docker and Docker Compose installed we can clone this Antlet for future reverse proxied docker projects! This significantly streamlines our workflow. π
Clone this Antlet for our reverse nginx/docker project then the fun begins.
These are our next steps in setting up our reverse proxy:
- Create a domain or subdomain for our project
- Clone two dockerized WordPress sites from my Gitlab repo
- Test the WordPress installations using our subdomain
Create & Configure a Domain Name
Before we can continue you will have to have a registered domain name or subdomain available to use for this project. You can obtain a free domain name from Freenom. I haven’t used Freenom but I hear its pretty good.
Once you have your domain you will need to setup a DNS A record for your domain. The Freenom the Freenom Knowlege base can walk you through this.
Personally, what I like to do is to group my Docker projects under one of my single domain names and assign each Docker app a unique subdomain. You can call these subdomains anything you want.
To keep this tutorial simple but useful, we are going to set up two Dockerized WordPress installations that can run concurrently on our new Antlet. Eventually we will be installing Let’s Encrypt on each of these WordPress sites. Cool huh?
For simplicity I will assign our WordPress apps:
- wp1.mydomainname.tld
- wp2.mydomainname.tld
You can swap in your domain and tld that you will be using.
Next step: add a dockerized WordPress app to the root of our Antlet.
Install & Configure WordPress
We are at the stage now where we need some apps to proxy. For the sake of expedience we are going to clone a Dockerized WordPress site from my public repository on Gitlab.
If you are not already in the root directory on your Antlet change to the root directory now:
cd ~/
… and clone the Dockerized WordPress site:
git clone https://gitlab.com/ronleeson/dockerized-wordpress.git
# List directory contents
ls
dockerized-wordpress
# Rename the directory to wp1
mv dockerized-wordpress wp1
ls
wp1
# Clone a second dockerized-wordpress
git clone https://gitlab.com/ronleeson/dockerized-wordpress.git
# List directory contents
ls
dockerized-wordpress
# Rename the directory to wp2
mv dockerized-wordpress wp2
ls
wp1
wp2
Now we have two dockerized-wordpress sites: wp1 and wp2.
Lets spin up wp1:
cd ~/wp1
# Boot up the site
docker-compose up -d
The wp1 WordPress installation should be up and running. We can confirm this by running:
docker-compose ps
Name Command State Ports
----------------------------------------------------------------------------------------------------
wp1_db_1 docker-entrypoint.sh mysqld Up 3306/tcp, 33060/tcp
wp1_phpmyadmin_1 /docker-entrypoint.sh apac ... Up 0.0.0.0:8182->80/tcp,:::8182->80/tcp
wp1_wordpress_1 docker-entrypoint.sh apach ... Up 0.0.0.0:8080->80/tcp,:::8080->80/tcp
Test WP1 WordPress Installation
Before we can hit the wp1 home page we need to update two records in the wp1 database using phpMyAdmin.
First: grab our static ip for this server which we setup earlier using netplan:
ip a
… scroll down your results to eth1 and note your static ip which in my case is 10.0.0.92.
Second: note that phpMyAdmin is using 8182 so point your browser to http://10.0.0.92:8182 which should display phpMyAdmin’s home page:

… click on options:

… update the siteurl and home records with your server static ip, in my case http://10.0.0.92:8080. Note the port that wordpress service is running on. You can always view the wp1’s ports by running docker-compose ps.

Now we can hit the wp1’s WordPress home page via: http://10.0.0.92:8080.

Access WP1 with Subdomain
Configure the Antsle Nginx Reverse Proxy
We are close. We need only to update our Antsle Nginx config to point to wp1.yourdomainname.tld.
We begin: ssh into your Antsle and create a conf file for your domain name.
cd /etc/nginx/virtualhosts
vi yourdomainname.tld.conf
Add the following config code. Remember to swap in your domain name and Antlet ip address on the highlighted lines.
server {
listen 80;
server_name wp1.yourdomainname.tld;
location / {
proxy_read_timeout 300;
proxy_connect_timeout 300;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Protocol $scheme;
proxy_set_header X-Forwarded-Ssl off;
proxy_set_header X-Url-Scheme $scheme;
proxy_set_header X-Frame-Options SAMEORIGIN;
proxy_pass http://10.1.1.24:8080; # Replace with your Antlet ip
}
}
server {
listen 80;
server_name wp2.yourdomainname.tld;
location / {
proxy_read_timeout 300;
proxy_connect_timeout 300;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Protocol $scheme;
proxy_set_header X-Forwarded-Ssl off;
proxy_set_header X-Url-Scheme $scheme;
proxy_set_header X-Frame-Options SAMEORIGIN;
proxy_pass http://10.1.1.24:8081; # Replace with your Antlet ip
}
}
Note that we added the server config for wp2 since we are here. After all, the point of this tutorial is to be able to run multiple Docker projects on one Antlet.
Save the file and restart Nginx
service nginx restart
Update wp1 Site to Point to Subdomain
Go back into phpMyAdmin: http://10.0.0.92:8181 and update the siteurl and home records in the options table to point to http://wp1.yourdomainname.tld.
Test the WordPress Installation
At this point when you hit wp1.mydomainname.tld you should see the WordPress installation screen.

Huzzah! Our wp1.yourdomainname.tld is now live.
Now, catch your breath… breath… good. Go get a cup of tea, come back and we get to do whole thing over again for the wp2 WordPress site. Well… not the whole thing. π Start from Configure wp1 Site swapping in wp2 for wp1.
In a nutshell, here’s the sequence for configuring wp2:
- cd into wp2
- change the port on phpmyadmin service to 8183 so we don’t get a port conflict with wp1’s phpmyadmin service which is already running on 8182
- boot up wp2
- update siteurl and home records using phpMyAdmin to point to static ip on port 8183
- confirm wp2 is running by hitting http://10.0.0.92:8080 (your ip will be different the port will be the same)
- you might want to go into the wp2 admin to change the site’s title to differentiate the two WordPress sites wp1 and wp2 from each other
- power up wp2
- point your browser to http://wp2.yourdomainname.tld
- wp2 site comes up: jump for joy
What’s Next?
We now have two Dockerized WordPress installations running on one server. We could easily add a third, fourth, ad infinitum… So, now our revels have ended… not!
Both our websites are running on http where all our requests and responses are sent in plain text which susceptible to interception or eavesdropping. Here’s a great resource for detailing all the mischief that can be perpetrated over http. π
In the very near future all websites will need to move to https protocol where all traffic between the user and server is encrypted protecting the user. As well, TLS also authenticates the server you are connecting to and protects that transmitted data from tampering. To quote from the linked resource above:
It helps me to think about it like this – HTTP in HTTPS is the equivalent of a destination, while SSL is the equivalent of a journey. The first is responsible for getting the data to your screen, and the second manages the way it gets there. With joint forces, they move data in a safe fashion.
Globalsign
Not to worry, the last steps of this tutorial will detail how to install a free SSL certificate using Let’s Encrypt.
Implementing SSL on the Antlet
The tricky part about implementing Let’s Encrypt on an Antlet is that the SSL server configuration will reside on the Antsle whereas the actual SSL certificates will reside on the Antlet. The Antlet we created is a LXC container. What does this mean? To quote from Wikipedia:
LXC (Linux Containers) is an operating-system-level virtualization method for running multiple isolated Linux systems (containers) on a control host using a single Linux kernel.
Wikipedia
What does that mean to us? Since our LXC server is running on the Antsle host, Antsle can access files from within our LXC container. So, when we get to updating the server config on the Antsle for SSL, Antsle can access the ssl certs that we will install on our running Antlet.
Essentially what we have set up now serving over http are two reverse proxies, one proxy on the Antsle, the second proxy on the Antlet which then directs traffic to each of our Docker WordPress sites. This will become clear as we move along.
Manual Installation of Let’s Encrypt on the Antlet
First step, install certbot on the Antlet:
apt-get update
apt-get install certbot
… using certbot we generate the certificates:
certbot certonly --manual --preferred-challenges dns
Let’s Encrypt will walk us through the process of providing your email address and the names of your subdomains that we want certificates for after which Let’s Encrypt is going to ask us to deploy a DNS TXT record under the names of our subdomains:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please deploy a DNS TXT record under the name
_acme-challenge.wp1.leesonresearch.com with the following value:
sgU6O0ugaY0Grn93uRJ053gop3Wx6uktiJCfoM02a40
Before continuing, verify the record is deployed.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue
Now you will have to go to your domain’s registrar to add the TXT record for wp1.yourdomainname.tld which will lood something like this:

After entering and submitting the txt record for wp1, go get a cup of tea and chill because it will take some time for the update to propagate through the internet before Let’s Encrypt can shake hands with your TXT record for wp1.
Once Let’s Encrypt verifies your TXT record for wp1 you can hit enter to continue with setting the text record for wp2.
After continuing with the verification process on our subdomains, Let’s Encrypt will confirm success and tell us where the certs live on our Antlet. We will need the paths to our certs in order to update the Nginx config on our Antsle.
IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/wp2.yourdomainname.tld/fullchain.pem
Your key file has been saved at:
/etc/letsencrypt/live/wp2.yourdomainname.tld/privkey.pem
Your cert will expire on 2021-09-07. To obtain a new or tweaked
version of this certificate in the future, simply run certbot
again. To non-interactively renew *all* of your certificates, run
"certbot renew"
Currently the Nginx server configuration for our subdomains on our Antsle is serving over http protocol.
SSH into your Antsle:
Now we have to update these server configs to switch over from http to https protocol. We’ll start with wp1. My LXC Ubuntu server is running on my Antsle as 10.1.1.24. Your LXC server ip will be different.
Might be easiest to delete the config for http above and replace with https server config below.
Update the highlighted rows with your subdomain, Antlet ip and Antlet name with yours.
# THE FOLLOWING SERVER CONF RESIDES ON ANTSLE WHICH POINTS TO ANTLET
# /antlets/your-antlet-name/etc/letsencrypt is the path to certs on Antlet
# So /antlets/your-antlet-name is the path to the Antlet
server {
listen 80;
server_name wp1.yourdomainname.tld;
rewrite ^ $scheme://wp1.yourdomainname.tld$request_uri redirect;
}
upstream sslwp1.yourdomainname.tld {
server 10.1.1.14:8080;
}
server {
listen 443 ssl;
ssl on;
ssl_certificate /antlets/your-antlet-name/etc/letsencrypt/live/wp1.yourdomainname.tld/fullchain.pem;
ssl_certificate_key /antlets/your-antlet-name/etc/letsencrypt/live/wp1.yourdomainname.tld/privkey.pem;
server_name wp1.yourdomainname.tld;
root /var/www;
location / {
proxy_read_timeout 300;
proxy_connect_timeout 300;
proxy_redirect off;
proxy_buffers 4 256k;
proxy_buffer_size 128k;
proxy_busy_buffers_size 256k;
proxy_set_header Host $host;
proxy_set_header X-Real_IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Protocol $scheme;
proxy_set_header X-Forwarded-Ssl off;
proxy_set_header X-Url-Scheme $scheme;
proxy_set_header X-Frame-Options SAMEORIGIN;
proxy_pass http://sslwp1.yourdomainname.tld;
}
}
Save your file and test your new configuration:
nginx -t
If Nginx configuration is successful, restart the Antsle’s Nginx:
service nginx restart
Before testing https://wp1.yourdomainname.tld you might want to clear your browser cache otherwise your browser may automatically kick over to http://wp1.yourdomainname.tld.
Cache cleared? Hit https://wp1.yourdomainname.tld and you should see something like this in Google Chrome:

Repeat the same process above to configure Nginx server for wp2.
If all went well we should have to SSL secured Dockerized WordPress sites running on our Antlet.
We are not quite done because these certificates will expire in ninety days so we need to setup a cron job that will try to renew the certificates and if the certs have yet to expire nothing happens. If the certificates are up for expiration then the certificates on both wp1 and wp2 will automatically renewed.
vi /etc/cron.daily/renewcerts
Add the following to the renewcerts shell script:
#!/bin/bash
certbot renew
service nginx reload
… make our shell script executable:
chmod a+x /etc/cron.daily/renewcerts
We now have both WordPress sites secured with https protocol. Congratulations!