Automating WordPress Deployment with Docker Compose 04/2024
Introduction
WordPress is a free and open-source Content Management System (CMS) built on a MySQL database with PHP processing. Thanks to its extensible plugin architecture and templating system, most of its administration can be done through the web interface. This is a reason why WordPress is a popular choice when creating different types of websites, from blogs to product pages to eCommerce sites. However, setting up a new web host environment can be time-consuming especially if you need to do it often. Docker Compose manages to simplify the installation process to a single deployment command greatly reducing the time and effort required.
In this blog, I will build a multi-container WordPress testing installation. containers will include a MySQL database, an Nginx web server, Wp-cli , and WordPress itself.
Docker Compose
Let's start! For this localhost deployment, we're using a .env file as a convenient way to manage environment variables across multiple Docker Compose services or containers. we're also going to use wp-cli for automating the first installation of WordPress. in fact this site is developed and designed by me using WordPress!
# Database #
MYSQL_DATABASE=wordpress
MYSQL_ROOT_PASSWORD=devops
MYSQL_USER=wordpress
MYSQL_PASSWORD=devops
# Site Settings #
SITE_URL=localhost
SITE_TITLE=test
SITE_ADMIN_USER=test
SITE_ADMIN_PASSWORD=test
SITE_ADMIN_EMAIL=test@test.com
Let's break down the services We're using in the docker-compose.yml file
Mysql:
version: '3'
services:
db:
image: mysql:5.7
container_name: db
restart: unless-stopped
env_file: .env
environment:
- MYSQL_DATABASE=${MYSQL_DATABASE}
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
- MYSQL_USER=${MYSQL_USER}
- MYSQL_PASSWORD=${MYSQL_PASSWORD}
volumes:
- dbdata:/var/lib/mysql
command: '--default-authentication-plugin=mysql_native_password'
networks:
- app-network
Services
Content #1
Mysql
- db: MySQL database service responsible for storing WordPress data.
WordPress:
wordpress:
build:
context: ./docker/wordpress
depends_on:
- db
container_name: wordpress
restart: unless-stopped
env_file: .env
environment:
- WORDPRESS_DB_HOST=db:3306
- WORDPRESS_DB_USER=${MYSQL_USER}
- WORDPRESS_DB_PASSWORD=${MYSQL_PASSWORD}
- WORDPRESS_DB_NAME=${MYSQL_DATABASE}
volumes:
- wordpress:/var/www/html
networks:
- app-network
WordPress Dockerfile:
FROM wordpress:6.3-fpm
RUN docker-php-ext-install mysqli pdo pdo_mysql
RUN apt-get update && apt-get install -y sendmail
COPY php.ini /opt
COPY php.ini /usr/local/etc/php/conf.d/php.ini
Services
Content #1
WordPress
- wordpress: the wordpress offical php-fpm version.
WordPress service built from a custom Dockerfile located in the ./docker/wordpress directory.
WordPress php.ini file:
file_uploads = On
max_input_vars = 4000
upload_max_filesize = 100M
post_max_size = 100M
memory_limit = 512M
max_execution_time = 1200
max_input_time = 600
allow_url_fopen = on
allow_url_include = on
Webserver:
webserver:
depends_on:
- wordpress
image: nginx:latest
container_name: webserver
restart: unless-stopped
ports:
- "80:80"
volumes:
- wordpress:/var/www/html
- ./nginx-conf:/etc/nginx/conf.d
networks:
- app-network
Services
Content #1
Webserver
- webserver: Nginx service serving as a reverse proxy for the WordPress application.
Nginx Conf file:
server {
listen 80;
listen [::]:80;
server_name localhost;
index index.php index.html index.htm;
root /var/www/html;
client_max_body_size 100M;
fastcgi_read_timeout 300;
proxy_read_timeout 300;
location ~ /.well-known/acme-challenge {
allow all;
root /var/www/html;
}
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass wordpress:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
location ~ /\.ht {
deny all;
}
location = /favicon.ico {
log_not_found off; access_log off;
}
location = /robots.txt {
log_not_found off; access_log off; allow all;
}
location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ {
expires max;
log_not_found off;
}
}
listen
Defines the port to listen on. In this case, it's listening on port 80 for both IPv4 and IPv6 connections.
server_name
- Specifies the hostname of the server. In this case, it's set to
localhost
.
index
- Defines the default files to serve when a directory is requested.
root
Sets the root directory for serving files.
Php parameters
client_max_body_size
: Sets the maximum allowed size of the client request body.
fastcgi_read_timeout
: Defines the maximum time for reading a response from the FastCGI server.
proxy_read_timeout
: Defines the maximum time for reading a response from the proxied
location
Defines rules for handling different URI patterns.
Wp-cli:
Wp-cli is a powerful tool for managing WordPress installations from the command line. It provides a wide range of commands to perform various tasks such as installing WordPress, managing plugins and themes, importing and exporting content, and interacting with the database.
wp-cli:
build:
context: ./docker/wp-cli
depends_on:
- db
container_name: wp-cli
restart: "no"
env_file: .env
command: sleep infinity
environment:
- WORDPRESS_DB_HOST=db:3306
- WORDPRESS_DB_USER=${MYSQL_USER}
- WORDPRESS_DB_PASSWORD=${MYSQL_PASSWORD}
- WORDPRESS_DB_NAME=${MYSQL_DATABASE}
volumes:
- wordpress:/var/www/html
networks:
- app-network
Services
Content #1
WordPress
- wp-cli: service for managing WordPress installations and configurations.
Entrypont bash script for automation wordpress first installation, managing plugins, themes.
#!/bin/bash
LOCK_FILE=/var/www/html/.wp_installed
if [ ! -f "$LOCK_FILE" ]; then
# Run WordPress installation commands
until mysqladmin ping -h"db" -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" --silent; do
echo "MySQL server is not ready yet..."
sleep 2
done
wp core install --path=/var/www/html --url=${SITE_URL} --title=${SITE_TITLE} --admin_user=${SITE_ADMIN_USER} --admin_password=${SITE_ADMIN_PASSWORD} --admin_email=${SITE_ADMIN_EMAIL}
wp rewrite structure '/%postname%/'
wp user update ${SITE_ADMIN_USER} --admin_color=modern
wp plugin install elementor --activate --path=/var/www/html
wp plugin install woocommerce --activate --path=/var/www/html
wp plugin install /opt/plugins/elementor-pro_3.18.2.zip --activate --path=/var/www/html
wp theme install blocksy --activate
wp plugin install all-in-one-wp-migration --activate --path=/var/www/html
# Create a lock file to indicate that WordPress is installed
touch "$LOCK_FILE"
fi
sleep infinity
Following this, it proceeds to install the requested plugins, themes, and activate them. In this instance, we are installing the "Blocksy" theme and ecommerce plugins for an ecommerce site. I have developed various wp-cli scripts tailored to different types of sites, including an LMS site (Learning Management System), Ecommerce, appointment, and others. This systematic approach enables me to automate site deployment, aligning precisely with my clients' requirements. Moreover, it significantly reduces the time spent manually searching for and activating each plugin, particularly beneficial when the WordPress site necessitates a substantial number of plugins.
The script starts by defining the path to a lock file (LOCK_FILE) located at /var/www/html/.wp_installed. This lock file serves as an indicator that WordPress has been installed. It then checks if the lock file exists. If it doesn't exist, it proceeds with the WordPress installation commands.
The script waits until the MySQL server is ready by continuously pinging it with the mysqladmin command. This ensures that the database is available before proceeding with the installation.
The script uses wp core install command to install WordPress with specified parameters from our .env file such as site URL, site title, admin username, password, and email.
It then updates the permalink structure using the wp rewrite structure command. Next, it updates the admin user's color scheme with wp user update command. for my favorite color sheme for the wordpress backend 🙂