DIY 5$: deploy Wordpress + free SSL with letsencrypt and haproxy
If you are starting your new business or want to have your own blog, hosting your website is something are looking for. But you are asking yourself: “What is a cheap and good way to host my website?”.
Well, I have asked myself the same question and I got the answer. I had already an Orange pi, so I have decided to use it and deploy my website on it. It has been working since 5 months without any trouble or disfunction.
Therefore, I have decided to share this with you guys. We will go step by step:
- 1- Rent a server
- 2- Install the tools
- 3- Deploy wordpress
- 4- Buy a domain name
- 5- Deploy HAproxy + create certificate.
- 6- Optional : Please subscribe to my youtube channel if you like this tutorial to get all the new updates:)
Step 1 : Having a server
Personnally, I deploy it on my Orange pi.
Buy it from Aliexpress: Orange pi
Step 2 : Install the tools
The only 2 tools necessary for this tutorial are :
- Docker
- Docker swarm
I have used these commands to provision my ubuntu machine:
#!/bin/bash
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl gnupg-agent software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
docker swarm init
Step 3 : Deploy Wordpress
In this tutorial, we will deploy wordpress using docker swarm because it is easy to start using it.
Also, you will notice that wordpress and haproxy will be deployed using different swarm stacks
.
So that, we should create a network before deploying the stacks to connect them easily.
$ docker network create -d overlay --attachable proxy
Now copy this file to myproject/wordpress/docker-compose.yml
.
version: "3.8"
services:
wordpress:
image: wordpress
restart: always
container_name: wordpress
# You can uncomment these ligns to test the wordpress without haproxy
# ports:
# - 8080:80
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: exampleuser
WORDPRESS_DB_PASSWORD: examplepass
WORDPRESS_DB_NAME: exampledb
volumes:
- wordpress:/var/www/html
networks:
- proxy
db:
image: mysql:5.7
restart: always
container_name: mydb
environment:
MYSQL_DATABASE: exampledb
MYSQL_USER: exampleuser
MYSQL_PASSWORD: examplepass
MYSQL_RANDOM_ROOT_PASSWORD: "1"
volumes:
- db:/var/lib/mysql
networks:
- proxy
volumes:
wordpress:
name: "wordpress"
db:
name: db
networks:
proxy:
external: true
name: proxy
and run the docker swarm stack.
docker stack deploy -c docker-compose.yml wordpress
Step 4 : Buy a domain name
Buy the domain name that you want. In general, I use https://namecheap.com.
After buying it, add an A record
with the IP address of your server to your domain name.
Step 5 : Haproxy configuration + Letsencrypt certificate
In a previous post, I have described how to use letsencrypt with haproxy.
Let’s adapt the configuration used before to the wordpress server. In this part, we will:
- Create the haproxy configuration
- Create a dummy certificates necessary for haproxy
- Run haproxy
- Create a real certificate for the domain name
- Add a certificate renewal for the domain name
HAProxy configuration
Until now, we have deployed a wordpress with mysql server only. Let’s create the haproxy configuration that will redirect all HTTP to HTTPS and will pass the request to wordpress.
myproject
|--proxy
|--haproxy
|-- haproxy.cfg
On your root project folder, create a folder tree called proxy/haproxy. Add the file haproxy.cfg
to the folder haproxy.
global
log stdout format raw local0
daemon
# Default ciphers to use on SSL-enabled listening sockets.
# For more information, see ciphers(1SSL).
ssl-default-bind-ciphers kEECDH+aRSA+AES:kRSA+AES:+AES256:RC4-SHA:!kEDH:!LOW:!EXP:!MD5:!aNULL:!eNULL
resolvers docker_resolver
nameserver dns 127.0.0.11:53
defaults
log global
mode http
option httplog
option dontlognull
frontend http
bind *:80
mode http
# if this is an ACME request to proof the domain ownder, then redirect to nginx-certbot server
acl is_well_known path_beg -i /.well-known/
# else redirect the traffic to https
redirect scheme https code 301 if !is_well_known !{ ssl_fc }
use_backend letsencrypt if is_well_known
backend letsencrypt
server letsencrypt nginx-certbot:80 resolvers docker_resolver check init-addr none
frontend https
bind *:443 ssl crt /usr/local/etc/certs/
http-response set-header Strict-Transport-Security "max-age=16000000; includeSubDomains; preload;"
acl wordpress hdr(host) -i YOUR_DOMAINNAME
use_backend wordpress if wordpress
default_backend wordpress
backend wordpress
server wordpress wordpress:80
http-request add-header X-Forwarded-Proto https if { ssl_fc }
The above configuration will:
- Redirects HTTP to HTTPS then redirect to our application by default
- Handle letsencrypt HTTP requests for verifying the domain owner. These requests starts by /.well-known/ always and target a specific file that should be served in HTTP. That’s why we will use nginx to serve the target folder.
- HTTPS requests will be secured using the certificates in /usr/local/etc/certs/. At least one certificate should be present. This is why it is important to create a dummy certificate before running haproxy. Otherwise, if the folder /usr/local/etc/certs/ is empty, the haproxy will show errors in log.
Create a dummy certificate
Create the folder certs
at the root of your project.
myproject
|--proxy
|--certs
...
Create a self signed certificate using openssl.
$ sudo apt-get install openssl
$ openssl req -nodes -x509 -newkey rsa:2048 -keyout test.key -out test.crt -days 30
$ cat test.key test.crt > ./certs/test.pem
Run HAproxy
Create the folder webroot
at the root of your project. This is the folder where Letsencrypt will request the file to verify that you are the owner of the domain.
myproject
|--proxy
|-- certs
|-- haproxy
|-- webroot
|-- docker-compose.yml
...
Now that the configurations and all the necessary folders are ready. Create the docker-compose.yml
file on the root project directory.
version: "3.5"
services:
proxy:
image: haproxy:latest
restart: always
volumes:
- ./haproxy:/usr/local/etc/haproxy:ro
- ./certs:/usr/local/etc/certs:ro
ports:
- 80:80
- 443:443
nginx-certbot:
image: nginx
restart: always
container_name: nginx-certbot
volumes:
- ./webroot:/usr/share/nginx/html
Run the haproxy and the nginx.
$ docker-compose up -d
If you are enjoying this tutorial, feel free to leave a comment. It means me a lot to know that you are appreciating my work :)
Create the certificate using certbot
Certbot is the letsencrypt official tool for creating a signed certificate. By default, a production certificate is delivered. For test case, to use the option --staging
to avoid Letsencrypt rate limits.
Letsencrypt rate limits: 5 certificates per week. Official documentation
Create the script create-cert.sh
at your root project:
#!/bin/bash
set -e
echo "Starting create new certificate..."
if [ "$#" -lt 2 ]; then
echo "Usage: ... <domain> <email> [options]"
exit
fi
DOMAIN=$1
EMAIL=$2
OPTIONS=$3
docker run --rm \
-v $PWD/letsencrypt:/etc/letsencrypt \
-v $PWD/webroot:/webroot \
certbot/certbot \
certonly --webroot -w /webroot \
-d $DOMAIN \
--email $EMAIL \
--non-interactive \
--agree-tos \
$3
# Merge private key and full chain in one file and add them to haproxy certs folder
function cat-cert() {
dir="./letsencrypt/live/$1"
cat "$dir/privkey.pem" "$dir/fullchain.pem" > "./certs/$1.pem"
}
# Run merge certificate for the requested domain name
cat-cert $DOMAIN
Explanation :
- This script will take 3 arguments : domain name, email and options.
- Webroot option : tell the certbot to create the file for domain verification in the path /webroot
- Options : this is very important to add any additional option for certbot, example:
--staging
.
Run the script. Use –staging for test purposes. If your staging certificate is working, you can remove this option and create a production certificate later.
./create-cert yourdomain.com youremail.com --staging
If you have created successfully your certificate, don’t forget to remove the dummy test certificate created
certs/test.pem
.
Renew certificate
Letsencrypt certificates are valid only for 90 days. Therefore, it should be renewed always. I have done a script to renews all my certificates each month.
Create this script at the project proxy folder and call it renew-certs.sh
:
#!/bin/bash
set -e
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
cd $DIR
echo "$(date) About to renew certificates" >> /var/log/letsencrypt-renew.log
/usr/bin/docker run \
-i \
--rm \
--name certbot \
-v $PWD/letsencrypt:/etc/letsencrypt \
-v $PWD/webroot:/webroot \
certbot/certbot \
renew -w /webroot
echo "$(date) Cat certificates" >> /var/log/letsencrypt-renew.log
function cat-cert() {
dir="./letsencrypt/live/$1"
cat "$dir/privkey.pem" "$dir/fullchain.pem" > "./certs/$1.pem"
}
for dir in ./letsencrypt/live/*; do
if [[ "$dir" != *"README" ]]; then
cat-cert $(basename "$dir")
fi
done
echo "$(date) Reload haproxy" >> /var/log/letsencrypt-renew.log
docker service update --force proxy_proxy
echo "$(date) Done" >> /var/log/letsencrypt-renew.log
Create a crontask to run this renew script every month:
$ echo "0 0 1 * * your_project_path/renew-certs.sh" >> /etc/crontab
Source code
I know that it may be difficult to copy and create all the files using copy paste. Therefore, I have create a github repository to share this code with you.
I hope you have enjoyed this tutorial. If you have any ideas for a new project/tutorial you want to see, don’t hesitate to write it in the comment section below.
Also, I have launched my youtube channel, please subscribe
to get all my new updates