I’m quite early into writing blog posts so I thought what better of a topic to choose than how I got this blog running in the first place.
I use a Raspberry Pi running Ubuntu on my home internet connection to host this blog. Before hosting this I used it as a Pi-hole which I already ran as a docker container so I added WordPress and the associated database as additional containers.
So, we’re going to assume that you already have a Ubuntu host setup that you want to run your new blog on and you already have Docker installed. If you’re unfamiliar with Docker I have a beginners guide to Docker you can read.
Installing WordPress & Mariadb
We’ll be using the official WordPress Docker image and a mariadb image based on jsurf/rpi-raspbian for this. I could not find a way to use the offical mariadb image for the database as it refused to run on the arm64 architecture hence we’re using this image instead.
I personally prefer using docker compose for my contains as I find it easier to bring them up and down as required along with having a nice YAML file to refer back to on how it’s setup. So here is the docker-compose file:
version: '3.1' services: wordpress: image: wordpress restart: always ports: - 80:80 environment: - WORDPRESS_DB_HOST: db - WORDPRESS_DB_USER: - WORDPRESS_DB_PASSWORD: - WORDPRESS_DB_NAME: volumes: - wordpress:/var/www/html db: image: jsurf/rpi-mariadb restart: always environment: - MYSQL_DATABASE: - MYSQL_USER: - MYSQL_PASSWORD: - MYSQL_ROOT_PASSWORD: volumes: - db:/var/lib/mysql volumes: wordpress: db:
Note: Enter your own custom values for the DB_USER, DB_PASSWORD, DB_NAME etc. They’re all self explanatory but if you’re confused check the links to the container images above for the documentation.
Then from the folder you bring the containers up by running
sudo docker compose up -d
This will bring up both containers and because we add -d to the end it will stay running in the background.
And there it is, simple as that we now have our WordPress blog running great…however it’s not very secure, were running it over port 80 so there’s no SSL certificate in place. That’s where Traefik and LetsEncrypt come in.
Securing your WordPress install with Traefik & LetsEncrypt
So now WordPress is up and running lets get it secure. In case you’ve never heard of either of these technologies lets give you a quick overview.
Traefik Proxy is a web application proxy that allows us to host multiple websites behind a single proxy server. This means should we want to host more web facing sites in the future we dont have to keep opening up new ports for each container and do some funky NAT translation, we can simple put the site behind Traefik and let that handle all the web requests.
Let’s Encrypt is an open certificate authority that allows anyone to generate a free SSL certificate. Traefik Proxy has native support for Let’s Encrypt so it’s a nice easy way to get our site out on the internet and secure.
Ok the preamble is out the way, lets get in to setting it up.
Create the Traefik Docker container
The first thing we’re going to do is make our new docker-compose.yaml file. I’ll make a new folder for this called traefik and put it in there, then populate it as follows
version: '3' services: traefik: restart: always image: traefik:v2.9 command: --api.insecure=true --providers.docker networks: - proxy ports: - "8080:8080" - "443:443" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro networks: proxy: external: true
Note: If you get an error around the network proxy not existing run sudo docker network create proxy so it exists before bringing the container up
Once you bring the container up you can confirm it’s running by going to http://localhost:8080/dashboard
Ok so we’ve confirmed it works, good start. Now lets add some more bits to get it ready for LetsEncrypt. First we need to make a new folder called letsencrypt in the same folder as the docker-compose.yml file
Then we modify our docker-compose.yml file:
version: '3' services: traefik: image: traefik:v2.9 command: --api.insecure=true --providers.docker=true --providers.docker.exposedbydefault=false --entrypoints.websecure.address=:443 --certificatesresolvers.myresolver.acme.tlschallenge=true --certificatesresolvers.myresolver.acme.email=youremail@example.com --certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json restart: always networks: - proxy ports: - "8080:8080" - "443:443" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - ./letsencrypt:/letsencrypt networks: proxy: external: true
What do these all these commands mean?
- api.insecure – Enable the webui
- providers.docker – Tell Traefik that we are using docker and we want it to be aware of the containers
- providers.docker.exposedbydefault – Should containers be exposed through traefik by default?
- entrypoints.websecure.address – What port do you want to expose sites through?
- certificatesresolvers.myresolve.acme.tlschallenge – Enable the TLS challenge for LetsEncrypt
- certificatesresolvers.myresolver.acme.email – The email address your LetsEncrypt certificates should be associated with.
- certificatesresolvers.myresolver.acme.storage – Where should LetsEncrypt store the certificates.
Ok now we have Traefik running locally and it’s setup to have LetsEncrypt certificates deploying.
Now we need to go back to our wordpress container. We’ll be adding in some options for labels, these are used in conjunction with Traefik to let it know that this WordPress site is one that we want to be included.
We’re also going to be adding the network sections as we now have a new network that we want the WordPress and Traefik containers to be able to communicate through.
version: '3.1' services: wordpress: image: wordpress restart: always networks: - proxy environment: WORDPRESS_DB_HOST: db WORDPRESS_DB_USER: WORDPRESS_DB_PASSWORD: WORDPRESS_DB_NAME: volumes: - wordpress:/var/www/html labels: - "traefik.enable=true" - "traefik.http.routers.wordpress.rule=Host(`yourdomain.com`)" - "traefik.http.routers.wordpress.entrypoints=websecure" - "traefik.http.routers.wordpress.tls.certresolver=myresolver" db: image: jsurf/rpi-mariadb restart: always networks: - proxy environment: MYSQL_DATABASE: MYSQL_USER: MYSQL_PASSWORD: MYSQL_ROOT_PASSWORD: volumes: - db:/var/lib/mysql volumes: wordpress: db: networks: proxy: external: true
Remember to put your actual domain name in above.
And there we go. One WordPress site hosted behind Traefik that’s configured to use LetsEncrypt for it’s SSL certificate.
Gotchas
One simple issue I came across when running this the first time was:
Unable to obtain ACME certificate for domains \"exampledomain.com\": unable to generate a certificate for the domains [exampledomain.com]: error: one or more domains had a problem:\n[exampledomain.com] acme: error: 400 :: urn:ietf:params:acme:error:connection :: 82.26.194.172: Timeout during connect (likely firewall problem)\n" ACME CA="https://acme-v02.api.letsencrypt.org/directory" routerName=wordpress@docker rule="Host(exampledomain.com)" providerName=myresolver.acme
This was caused by not allowing port 443 access from the internet via my router. Once I opened this up everything worked perfectly.
References
- https://doc.traefik.io/traefik/user-guides/docker-compose/acme-tls/
Leave a Reply