Run WordPress as a Docker Stack
Table of Contents
Here I will show you how to run a full blown WordPress instance in a Docker (compose) Stack. All you need is to create the docker-compose.yml
(compose.yml
) file and run the wps-deploy.sh
script. My setup contains all the important components you will need to run a WordPress Site. These are the main components of the docker stack:
Preparation via wps-deploy.sh
With the help of the wps-deploy.sh
script all the needed directory structure, config files and log files are getting created.
Main features of the script:
- directory structure
- config directory
- data directory
- log directory
- create config files for the docker containers
- mariadb_my.conf
- redis.conf
- php-conf.ini
- apache2-security.conf
- create .env file for
docker-compose.yml
#!/bin/bash echo "" echo "*******************************************************" echo "Welcome to the WPS (wordpress stack) deploy script!" echo "*******************************************************" echo "" ############################################################## # prepare stack dir ############################################################## # write wordpress stack path into variable # please without the trailing slash # example /opt/docker/stacks/wp01-draft read -p "Enter stack path (without trailing slash): " WPS_PATH echo "[x] WPS root dir: '$WPS_PATH'" echo "" echo "" ############################################################## # create needed dirs ############################################################## echo "*******************************************************" echo "> Creating directories!" echo "*******************************************************" echo "" # for config files mkdir -p $WPS_PATH/config echo "[x] dir created: '$WPS_PATH/config'" # for data mkdir -p $WPS_PATH/data echo "[x] dir created: '$WPS_PATH/data'" # for redis data mkdir -p $WPS_PATH/data/redis echo "[x] dir created: '$WPS_PATH/data/redis'" # for mariadb data mkdir -p $WPS_PATH/data/mariadb echo "[x] dir created: '$WPS_PATH/data/mariadb'" # for wordpress data mkdir -p $WPS_PATH/data/wordpress echo "[x] dir created: '$WPS_PATH/data/wordpress'" # for log files mkdir -p $WPS_PATH/log echo "[x] dir created: '$WPS_PATH/log'" echo "" echo "" ############################################################## # create config files ############################################################## echo "*******************************************************" echo "> Creating configuration files!" echo "*******************************************************" # create apache2 config file cd $WPS_PATH/config echo "" { echo "# ./config/apache2_security.conf" echo "# APACHE configuration: /etc/apache2/conf-enabled/security.conf" echo "ServerTokens Prod" echo "ServerSignature Off" echo "TraceEnable Off" } | sudo tee -a apache2_security.conf > /dev/null echo "[x] file created: '$WPS_PATH/config/apache2_security.conf'" # create mariadb config file cd $WPS_PATH/config { echo "# MARIADB configuration" echo "# ./config/mariadb_my.cnf" echo "[server]" echo "log_error=/var/log/mysql/server.log" } | sudo tee -a mariadb_my.cnf > /dev/null echo "[x] file created: '$WPS_PATH/config/mariadb_my.cnf'" # add redis config file { echo "# REDIS configuration" echo "# ./config/redis_redis.conf" echo "logfile /var/log/redis/server.log" } | sudo tee -a redis_redis.conf > /dev/null echo "[x] file created: '$WPS_PATH/config/redis_redis.conf'" # create php (wordpress) config file { echo "; PHP configuration" echo "; ./config/wordpress_php-conf.ini" echo "file_uploads = On" echo "memory_limit = 300M" echo "upload_max_filesize = 100M" echo "post_max_size = 100M" echo "max_execution_time = 600" echo "" echo "; Disable PHP Error Reporting" echo "display_errors = Off" echo "log_errors = On" echo "" echo "; Error log file location" echo "error_log = /var/log/php/error.log" } | sudo tee -a wordpress_php-conf.ini > /dev/null echo "[x] file created: '$WPS_PATH/config/wordpress_php-conf.ini'" echo "" echo "" ############################################################## # prepare log files ############################################################## echo "*******************************************************" echo "> Preparing log files!" echo "*******************************************************" cd $WPS_PATH/log touch mariadb_server.log echo "" echo "[x] touched file: '$WPS_PATH/log/mariadb_server.log'" touch redis_server.log echo "[x] touched file: '$WPS_PATH/log/redis_server.log'" touch wordpress_apache2-access.log echo "[x] touched file: '$WPS_PATH/log/wordpress_apache2-access.log'" touch wordpress_apache2-error.log echo "[x] touched file: '$WPS_PATH/log/wordpress_apache2-error.log'" touch wordpress_php-error.log echo "[x] touched file: '$WPS_PATH/log/wordpress_php-error.log'" echo "" echo "" chmod 666 * ############################################################## # create .env file ############################################################## echo "*******************************************************" echo "> Creating .env file for docker-compose.yml!" echo "*******************************************************" cd $WPS_PATH # define COMPOSE_PROJECT_NAME # example: wp01-example.com echo "" read -p "Define variable COMPOSE_PROJECT_NAME (wpxx_example-com): " DEPLOY_COMPOSE_PROJECT_NAME sudo sh -c "echo 'COMPOSE_PROJECT_NAME=$DEPLOY_COMPOSE_PROJECT_NAME' >> .env" #echo "COMPOSE_PROJECT_NAME=$DEPLOY_COMPOSE_PROJECT_NAME" | sudo tee -a .env # define WP_INSTANCE # example: wp01 read -p "Define variable WP_INSTANCE (wp0x): " DEPLOY_WP_INSTANCE sudo sh -c "echo 'WP_INSTANCE=$DEPLOY_WP_INSTANCE' >> .env" #echo "WP_INSTANCE=$DEPLOY_WP_INSTANCE" | sudo tee -a .env # define WORDPRESS_DB_NAME # example: WORDPRESS_DB_NAME=8b420bb0_wp-db # write uuid into variable UUID_1=$(uuidgen) # take the first part of the uuid into a different variable UUID_1_1=${UUID_1:0:8} sudo su -c "echo 'WORDPRESS_DB_NAME=${UUID_1_1}_wp-db' >> .env" #echo "WORDPRESS_DB_NAME=${UUID_1_1}_wp-db" | sudo tee -a .env # define WORDPRESS_TABLE_PREFIX # example: WORDPRESS_TABLE_PREFIX=wp_ read -p "Define variable WORDPRESS_TABLE_PREFIX (wp_): " DEPLOY_WORDPRESS_TABLE_PREFIX sudo su -c "echo 'WORDPRESS_TABLE_PREFIX=$DEPLOY_WORDPRESS_TABLE_PREFIX' >> .env" # define WORDPRESS_DB_USER # example: WORDPRESS_DB_USER=bacd36315127_wp-db-user UUID_2=$(uuidgen) # take the first part of the uuid into a different variable UUID_2_1=${UUID_2:0:8} sudo su -c "echo 'WORDPRESS_DB_USER=${UUID_2_1}_wp-db-user' >> .env" #echo "WORDPRESS_DB_NAME=${UUID_2_1}_wp-db" | sudo tee -a .env # define WORDPRESS_DB_PASSWORD # example: WORDPRESS_DB_PASSWORD=018e46a5-c070-74a5-aa01-9fcd67629a87 UUID_3=$(uuidgen) sudo su -c "echo 'WORDPRESS_DB_PASSWORD=$UUID_3' >> .env" #echo "WORDPRESS_DB_PASSWORD=$UUID_3" | sudo tee -a .env echo "" # define MYSQL_ROOT_PASSWORD # example: MYSQL_ROOT_PASSWORD=018e46a6-0c86-7717-9755-96b4b0a5ee71 UUID_4=$(uuidgen) sudo su -c "echo 'MYSQL_ROOT_PASSWORD=$UUID_4' >> .env" #echo "MYSQL_ROOT_PASSWORD=$UUID_4" | sudo tee -a .env echo "" # define REDIS_PASSWORD # example: REDIS_PASSWORD=ebe2c513-5e58-4d4b-8ec2-cf017a92475a UUID_5=$(uuidgen) sudo su -c "echo 'REDIS_PASSWORD=$UUID_5' >> .env" #echo "REDIS_PASSWORD=$UUID_5" | sudo tee -a .env echo "" # define WP_URL read -p "Define variable WP_URL (example.com): " DEPLOY_WP_URL sudo su -c "echo 'WP_URL=https://$DEPLOY_WP_URL' >> .env" #echo "WP_URL=https://$DEPLOY_WP_URL" | sudo tee -a .env # define WP_TITLE read -p "Define variable WP_TITLE (Organisation Name): " DEPLOY_WP_TITLE sudo su -c "echo 'WP_TITLE=$DEPLOY_WP_TITLE' >> .env" #echo "WP_TITLE=$DEPLOY_WP_TITLE" | sudo tee -a .env # define WP_ADMIN # example: WP_ADMIN=wp-admin-khp read -p "Define variable WP_ADMIN (wp-admin-orgname): " DEPLOY_WP_ADMIN sudo su -c "echo 'WP_ADMIN=$DEPLOY_WP_ADMIN' >> .env" #echo "WP_ADMIN=$DEPLOY_WP_ADMIN" | sudo tee -a .env # define WP_ADMIN_PASS # example: WP_ADMIN_PASS=018e58b2-d2bd-7d70-8cd0-07a346d2042a UUID_6=$(uuidgen) DEPLOY_WP_ADMIN_PASS=$UUID_6 sudo su -c "echo 'WP_ADMIN_PASS=$DEPLOY_WP_ADMIN_PASS' >> .env" #echo "WP_ADMIN_PASS=$UUID_6" | sudo tee -a .env # define WP_ADMIN_EMAIL # example: WP_ADMIN_EMAIL=admin@domain.com read -p "Define variable WP_ADMIN_EMAIL: " DEPLOY_WP_ADMIN_EMAIL sudo su -c "echo 'WP_ADMIN_EMAIL=$DEPLOY_WP_ADMIN_EMAIL' >> .env" #echo "WP_ADMIN_EMAIL=$DEPLOY_WP_ADMIN_EMAIL" | sudo tee -a .env echo "" ############################################################## # create syntax for basic wordpress install via wp-cli ############################################################## echo "" echo "*******************************************************" echo "> Basic wordpress install via WP-CLI!" echo "*******************************************************" echo "" echo "Name of the hosting instance: $DEPLOY_WP_INSTANCE" echo "Name of the docker compose project: $DEPLOY_COMPOSE_PROJECT_NAME" echo "" echo "Please connect to the wp-cli container via the following syntax:" echo "sudo docker exec -it ${DEPLOY_COMPOSE_PROJECT_NAME}_wp-cli /bin/bash" echo "" echo "Wordpress Language: 'de_DE':" echo "wp core install --path=\"/var/www/html\" --url=\"https://$DEPLOY_WP_URL\" --title=\"$DEPLOY_WP_TITLE\" --admin_user=\"$DEPLOY_WP_ADMIN\" --admin_password=\"$DEPLOY_WP_ADMIN_PASS\" --admin_email=\"$DEPLOY_WP_ADMIN_EMAIL\" --locale=\"de_DE\" --skip-email" echo "" echo "Wordpress Language: 'en_US':" echo "wp core install --path=\"/var/www/html\" --url=\"https://$DEPLOY_WP_URL\" --title=\"$DEPLOY_WP_TITLE\" --admin_user=\"$DEPLOY_WP_ADMIN\" --admin_password=\"$DEPLOY_WP_ADMIN_PASS\" --admin_email=\"$DEPLOY_WP_ADMIN_EMAIL\" --skip-email" echo "" echo "Thanks for running this deployment script. HAVE FUN!" echo "" echo ""
To run the script you have to add the execution right (x) and give sudo permissions. Here is how to do this:
sudo +x wps-deploy.sh sudo ./wps-deploy.sh
Here you can see how the script looks like while running. For this run we just use dummy data.
output of the script....
Data structure and config files
To give you a look and feel about the used config files please see the following examples.
Data structure inside the root folder of the docker compose stack.
. ├── config │ ├── apache2_security.conf │ ├── mariadb_my.cnf │ ├── redis_redis.conf │ └── wordpress_php-conf.ini ├── data │ ├── mariadb │ ├── redis │ └── wordpress ├── docker-compose.yml ├── .env ├── log │ ├── mariadb_server.log │ ├── redis_server.log │ ├── wordpress_apache2-access.log │ ├── wordpress_apache2-error.log │ └── wordpress_php-error.log └── wps-deploy.sh
apache2_security.conf
# /etc/apache2/conf-enabled/security.conf ServerTokens Prod ServerSignature Off TraceEnable Off
mariadb_my.cnf
Manages additional MariaDB configuration for the redis container.
# MariaDB configuration # ./config/mariadb_my.cnf [server] log_error=/var/log/mysql/server.log
redis_redis.conf
Manages additional redis configuration for the redis container.
# REDIS configuration # ./config/redis_redis.conf logfile /var/log/redis/server.log
wordpress_php-conf.ini
Manages additional PHP configuration for the WordPress container.
; PHP configuration ; ./config/wordpress_php-conf.ini file_uploads = On memory_limit = 300M upload_max_filesize = 100M post_max_size = 100M max_execution_time = 600 ; Disable PHP Error Reporting display_errors = Off log_errors = On ; Error log file location error_log = /var/log/php/error.log
.env
Provides data for docker-compose.yml
stack configuration .
COMPOSE_PROJECT_NAME=wp01_yourdomain-org WP_INSTANCE=wp01 WORDPRESS_DB_NAME=e07d2057_wp-db WORDPRESS_TABLE_PREFIX=wp_ WORDPRESS_DB_USER=542ba348_wp-db-user WORDPRESS_DB_PASSWORD=dac7133d-5b1a-4774-8fa5-3a653b175579 MYSQL_ROOT_PASSWORD=1d908589-3a8a-4d84-ab78-41c530d02a16 REDIS_PASSWORD=5ddafb51-326f-4b72-90d0-bdff2c41861d WP_URL=https://yourdomain.org WP_TITLE=yourdomain.org WP_ADMIN=wp-admin-user WP_ADMIN_PASS=60af0226-a07d-4095-8eb5-41c33a2c577b WP_ADMIN_EMAIL=admin@yourdomain.org
Docker stack via docker-compose.yml
Cause Security is one of my main concerns I like to always use the latest
tag for my docker images. So far I did not had and any issues with breaking stuff.
--- #version: "3.8" name: ${COMPOSE_PROJECT_NAME} services: # ******************************************* # WORDPRESS (website) # reverse-proxy port: 127.0.0.1:65180 (HTTP) # ******************************************* wordpress: image: wordpress:latest container_name: ${COMPOSE_PROJECT_NAME}_wp-app hostname: ${WP_INSTANCE}_wp-app restart: unless-stopped ports: - "127.0.0.1:65180:80" volumes: - ./data/wordpress:/var/www/html # WordPress data dir - ./config/wordpress_php-conf.ini:/usr/local/etc/php/conf.d/conf.ini # PHP config file - ./config/apache2_security.conf:/etc/apache2/conf-enabled/security.conf # Apache2 config file - ./log/wordpress_php-error.log:/var/log/php/error.log # PHP error log - ./log/wordpress_apache2-error.log:/var/log/apache2/error.log # Apache2 error log - ./log/wordpress_apache2-access.log:/var/log/apache2/access.log # Apache2 access log environment: TZ: Europe/Vienna WORDPRESS_DB_HOST: mariadb WORDPRESS_DB_NAME: ${WORDPRESS_DB_NAME} WORDPRESS_DB_USER: ${WORDPRESS_DB_USER} WORDPRESS_DB_PASSWORD: ${WORDPRESS_DB_PASSWORD} depends_on: - mariadb - redis # ******************************************* # WORDPRESS (CLI) # ******************************************* wpcli: image: wordpress:cli container_name: ${COMPOSE_PROJECT_NAME}_wp-cli hostname: ${WP_INSTANCE}_wp-cli volumes: #- ./config/wordpress_php-conf.ini:/usr/local/etc/php/conf.d/conf.ini - ./data/wordpress:/var/www/html # WordPress data dir user: "33" # default wpcli user, username "xfs" with the ID "33" #entrypoint: wp # comand executed every start tty: true # Enable terminal interaction stdin_open: true # Keep STDIN open even if not attached command: /bin/bash environment: TZ: Europe/Vienna HOME: /tmp WORDPRESS_DB_HOST: mariadb WORDPRESS_DB_NAME: ${WORDPRESS_DB_NAME} WORDPRESS_DB_USER: ${WORDPRESS_DB_USER} WORDPRESS_DB_PASSWORD: ${WORDPRESS_DB_PASSWORD} depends_on: - mariadb - redis - wordpress # ******************************************* # REDIS # ******************************************* redis: image: redis:latest container_name: ${COMPOSE_PROJECT_NAME}_redis hostname: ${WP_INSTANCE}_redis restart: unless-stopped expose: - 6379 volumes: - ./dаta/redis:/data # Redis data dir - ./config/redis_redis.conf:/usr/local/etc/redis/redis.conf # Redis config - ./log/redis_server.log:/var/log/redis/server.log command: redis-server /usr/local/etc/redis/redis.conf # Enable additional redis config environment: TZ: Europe/Vienna REDIS_PORT: 6379 REDIS_PASSWORD: ${REDIS_PASSWORD} # ******************************************* # PHPMYADMI # reverse-proxy port: 127.0.0.1:65181 (HTTP) # ******************************************* phpmyadmin: image: phpmyadmin:latest container_name: ${COMPOSE_PROJECT_NAME}_pma hostname: ${WP_INSTANCE}_pma restart: unless-stopped ports: - "127.0.0.1:65181:80" environment: TZ: Europe/Vienna PMA_HOST: mariadb PMA_PORT: 3306 PMA_ARBITRARY: 1 MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} UPLOAD_LIMIT: 100M depends_on: - mariadb # ******************************************* # DATABASE # ******************************************* mariadb: image: mariadb:latest container_name: ${COMPOSE_PROJECT_NAME}_mariadb hostname: ${WP_INSTANCE}_mariadb restart: unless-stopped volumes: #- ./wp-data:/docker-entrypoint-initdb.d - ./data/mariadb:/var/lib/mysql # Mariadb data dir - ./log/mariadb_server.log:/var/log/mysql/server.log # Mariadb server log - ./config/mariadb_my.cnf:/etc/mysql/my.cnf # Mariadb config file environment: TZ: Europe/Vienna MYSQL_DATABASE: ${WORDPRESS_DB_NAME} MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} MYSQL_USER: ${WORDPRESS_DB_USER} MYSQL_PASSWORD: ${WORDPRESS_DB_PASSWORD} command: - '--character-set-server=utf8mb4' - '--collation-server=utf8mb4_unicode_520_ci'
Manage docker stack
After creating the docker-compose.yml
(or compose.yml
) file you can start the docker stack with the following command.
sudo docker compose up -d
To check the logs of the docker stack please use this command.
sudo docker compose logs -ft
To check the resource consumption of the docker containers within the docker stack use the following command.
sudo docker compose stats
WP-CLI Syntax
If you don’t want to do the basic installation process via the web-base WordPress wizard you can do it via the terminal and the WP-CLI syntax. First you have to connect to the WP-CLI docker container.
sudo docker exec -it <your-docker-compose-name>_wp-cli /bin/bash
Installing WordPress via WP-CLI.
wp core install --path="/var/www/html" --url="www.yourdomain.org" --title="NEW PAGE TITLE" --admin_user="wp-admin-user" --admin_password="55e3807b-0bf1-42e5-b579-690284038d6a" --admin_email="admin@yourdomain.org" --skip-email
Post deployment tasks
After deploying the docker stack and doing the basic WordPress installation we have to do some post deployment tasks.
WordPress (wp-config.php)
If you do decided to use the redis component of the stack you have to put some basic configuration inside the wp-config.php
file.
/* REDIS CONFIG */ // Docker hostname or IP address of your Redis server define('WP_REDIS_HOST', 'redis'); // Port number of your Redis server define('WP_REDIS_PORT', '6379'); // Redis server password (from docker-compose.yml via .env file) define('WP_REDIS_PASSWORD', getenv('REDIS_PASSWORD')); // change the prefix and database for each site (optional) define( 'WP_REDIS_PREFIX', 'wp01' ); // 0-15 define( 'WP_REDIS_DATABASE', 0 ); // reasonable connection and read+write timeouts (optional) define( 'WP_REDIS_TIMEOUT', 1 ); define( 'WP_REDIS_READ_TIMEOUT', 1 );
Redefine memory limits for WordPress. This step is completly optional. The default WP_MEMORY_LIMIT
value is 40MB.
/* redefine MEMORY LIMITS */ define('WP_MEMORY_LIMIT', '256M'); define('WP_MAX_MEMORY_LIMIT', '512M');