Thinking. Writing. Philosophising.

Email Github LinkedIn

Docker Compose: WordPress on MySQL and NGINX with Certbot

Posted on December 15, 2021 — 5 Minutes Read

Abstracting away the complexity and intricacy of the underlying compute and network infrastructure for agile development and scalable product is the ongoing direction that has led computing and networking from a hardware-centric architecture to a software-defined design that responds rapidly to the ever-changing business needs. Whether it is the transition from hardware-defined infrastructure, built on top of purpose-built equipment and point-to-point circuits, to an overlay Software Defined Network (SDN) that is transport-agnostic, application-centric and that routes network traffic with respect to the real-time network status and the service levels required by the respective applications; or the transformation from hardware-centric computing, built with physical CPU, memory and storage on a specific instruction set architecture, in a physical data centre or colocation, to a software-defined, container-based and on-demand application deployment in a microservice design on the cloud; the overarching goal is to free development from the burden of what lies beneath and allow it to focus on that which is still to be reached.

Tapping into the modularity and scalability of containers, what follows will be a multi-container app, that creates a WordPress website on a MySQL database and an NGINX web server, with Certbot by the Electronic Frontier Foundation (EFF) for obtaining and renewing a signed SSL/TLS certificate on a given root domain from Let’s Encrypt, a non-profit Certificate Authority by the Internet Security Research Group (ISRG). The app is orchestrated with Docker Compose for rapid and modular deployment that fits in any microservice architecture, and is shared on Github for reference and further development. With Docker Compose, deployment is as simple as:

  1. Download a copy of the app;
  2. Create the environment variables for the MySQL root password and the WordPress database settings; and
  3. Docker Compose to start the app.

Git Clone

Download a copy of the app with git clone. Be sure to pass the --recurse-submodules argument to initialise and update each submodule in the repository.

$ git clone --recurse-submodules https://github.com/kurtcms/docker-compose-wordpress-nginx-mysql /app/docker-compose-wordpress-nginx-mysql/

Environment Variables

Docker Compose expects the MySQL root password, the WordPress database name, username and password as environment variables in a .env file in the same directory.

Be sure to create the .env file.

$ nano /app/docker-compose-wordpress-nginx-mysql/.env

And define the variables accordingly.

# MySQL root password

# WordPress database name, username and password

Docker Compose

With Docker Compose, the app may be provisioned with a single command.


Install Docker and Docker Compose with the Bash script that comes with app.

$ chmod +x /app/docker-compose-wordpress-nginx-mysql/docker-compose/docker-compose.sh \
    && /app/docker-compose-wordpress-nginx-mysql/docker-compose/docker-compose.sh

SSL/TLS Certificate

A dummy SSL/TLS certificate and private key will need to be created for NGINX to start service, and for Certbot to subsequently obtain a signed SSL/TLS certificate from Let’s Encrypt by answering a HTTP-01 challenge.

Create the dummy certificate before obtaining a signed one with the Bash script that comes with app.

$ chmod +x /app/docker-compose-wordpress-nginx-mysql/certbot/certbot.sh \
    && /app/docker-compose-wordpress-nginx-mysql/certbot/certbot.sh

Enter the root domain and the email address for registration and recovery when prompted.

Up and Down

Start the containers with Docker Compose.

$ docker-compose -f /app/docker-compose-wordpress-nginx-mysql/docker-compose.yml up -d

Stopping the containers is as simple as a single command.

$ docker-compose -f /app/docker-compose-wordpress-nginx-mysql/docker-compose.yml down

Backup and Restore

With the Docker containers up and running, WordPress will be reachable at the localhost address on TCP port 443 i.e. HTTPS, to which TCP port 80 i.e. HTTP will be redirected. Follow the instructions on screen to set up WordPress. Alternatively the MySQL database and WordPress content can be restored from an existing backup.

Docker Volume

The MySQL database, the WordPress content as well as the signed SSL/TLS certificate and its corresponding private key are stored in separate Docker volume under /var/lib/docker/volumes/.

└── lib/
    └── docker/
        └── volumes/
            ├── docker-compose-wordpress-nginx-mysql_certbot/
            ├── docker-compose-wordpress-nginx-mysql_mysql/
            └── docker-compose-wordpress-nginx-mysql_wordpress/


MySQL comes with a tool for logical backup. It produces a set of SQL statements that can be executed to reproduce the original database object definitions and table data.

Back up an existing MySQL database in a container.

$ docker exec -it mysql mysqldump \
    --user root \
    --password \
    kurtcms_org > kurtcms_org.sql

Restore a database from an existing backup.

$ docker exec -it mysql mysql \
    --user root \
    --password \
    kurtcms_org < kurtcms_org.sql


WordPress plugins and themes, as well as media files uploaded by users are housed under wp-content in the WordPress directory. Backing up and restoring them is as simple as making a copy of this directory and moving it to the WordPress container.

Making a copy of the wp-content directory.

$ cp -r /var/lib/docker/volumes/docker-compose-wordpress-nginx-mysql_wordpress/_data/wp-content \

Restoring it from an existing backup.

$ cp -r /app/docker-compose-wordpress-nginx-mysql/wp-content/ \

SSL/TLS Certificate Renewal

Certbot is instructed by Docker Compose to attempt a SSL/TLS certificate renewal every 12 hours, which should be more than adequate considering the certificate is valid for 90 days.

NGINX is instructed to reload its configuration every 24 hours to ensure the renewed certificate will come into effect at most 12 hours after a renewal, which should also be well in advance of an impending expiry.

Edit the docker-compose.yml should these intervals need to be adjusted.

$ nano /app/docker-compose-wordpress-nginx-mysql/docker-compose.yml

Modify the values as appropriate.

    command: "/bin/sh -c 'while :; do sleep 24h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"

    entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"


Together with a microservice architecture, immutable infrastructure, service meshes, and declarative APIs; containers are foundational to cloud-native applications that are modular and reusable in nature, and that scales to meet the ever-changing business needs.