CUSTOMIZED: https://github.com/bengarrett/devtidbits.com/tree/master/post_5017/example.com
https://devtidbits.com/2019/11/02/customise-wordpress-on-docker/
CUSTOMIZED: https://github.com/bengarrett/devtidbits.com/tree/master/post_5017/example.com
https://devtidbits.com/2019/11/02/customise-wordpress-on-docker/
In this guide, let’s go through the process of installing and configuring WordPress on a Docker container using docker-compose. The primary goals are to install a bare-bones install with the ability to customise wp-config.php
, php.ini
and my.cnf
. There is an expectation that you are familiar with Docker, WordPress, PHP and MySQL configuration files.
Install WordPress on Docker
On the system hosting Docker, create a working directory for the WordPress installation.
# Create a working directory``mkdir` `example.com` `# Change the current working directory``cd` `example.com
Inside the working directory create a docker-compose.yml
configuration file for editing. I use Visual Studio Code with the Docker extension, as shown below, but any editor is fine.
# An example command to launch Visual Studio Code``code docker-compose.yml
Go to the official page for the WordPress Docker image on Docker Hub.
https://hub.docker.com/_/wordpress
Scroll down on the Description tab until you reach via docker stack deploy or docker-compose.
PULL WORDPRESS::::::::::::
docker pull wordpress
version: '3.1'
services:
wordpress:
image: wordpress
restart: always
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
db:
image: mysql:5.7
restart: always
environment:
MYSQL_DATABASE: exampledb
MYSQL_USER: exampleuser
MYSQL_PASSWORD: examplepass
MYSQL_RANDOM_ROOT_PASSWORD: '1'
volumes:
- db:/var/lib/mysql
volumes:
wordpress:
db:
docker-compose up -d
Visual Studio Code editing docker-compose.yml
Copy the code block for the Example stack.yml into your editor with docker-compose.yml
open and save the file.
Back in the terminal, create and start the newly saved Docker composition.
# Stop and remove the containers
docker-compose down
# Delete both the wordpress and db file volumes
docker volume rm examplecom_db examplecom_wordpress
# Rebuild, recreate, start and attach the container services
docker-compose up
Once ready, the containers return this feedback.
wordpress:
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_NAME: myblogdb
WORDPRESS_DB_USER: wpinternal
WORDPRESS_DB_PASSWORD: stencil-running-cargo-savage
db:
environment:
MYSQL_DATABASE: myblogdb
MYSQL_USER: wpinternal
MYSQL_PASSWORD: stencil-running-cargo-savage
A new WordPress installation should be ready at http://localhost:8080. Fill out all the relevant form of information and you’ll be at the WordPress administration page. Congratulations, WordPress is installed!
WordPress Install
If you have any conflicts or issues with port 8080. Edit docker-compose.yml
and change the 8080:80
ports configuration in the WordPress container to a different value such as 8500:80
and run docker-compose up
again. You’ll also have to visit the new URL http://localhost:8500.
Resetting your WordPress Docker container
If at any time you wish to revert your container to a fresh install you’ll need to remove the storage volumes with these commands. Note, this cannot be undone.
# Stop and remove the containers
docker-compose down
# Delete the db file volume
docker volume rm examplecom_db
# Rebuild, recreate, start and attach the container services
docker-compose up
Improving docker-compose.yml
Renaming the database name, its user and password
The sample docker-compose.yml
configuration is minimal and is not suitable for production. So the first thing you may want to do is replace the environment database name, user and password values for both the WordPress and DB containers. As our database user does not have significant permissions, this process requires the deletion of the existing database volume and any of its data.
WORDPRESS_DB_...
values must match their MYSQL_...
environment values.
wordpress:
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_NAME: myblogdb
WORDPRESS_DB_USER: wpinternal
WORDPRESS_DB_PASSWORD: stencil-running-cargo-savage
db:
environment:
MYSQL_DATABASE: myblogdb
MYSQL_USER: wpinternal
MYSQL_PASSWORD: stencil-running-cargo-savage
Now we need to rebuild the MySQL database with these new values.
# Stop and remove the containers
docker-compose down
# Delete the db file volume
docker volume rm examplecom_db
# Rebuild, recreate, start and attach the container services
docker-compose up
To test the account and password changes run the following.
# Log into the database with the MySQL command-line client
docker-compose exec db mysql --user=wpinternal --password=stencil-running-cargo-savage myblogdb
Then in the mysql>
prompt.
show tables;
quit;
Note that show tables;
only return data if the WordPress installation is complete.
mysql> show tables;
+-----------------------+
| Tables_in_myblogdb |
+-----------------------+
| wp_commentmeta |
| wp_comments |
| wp_links |
| wp_options |
| wp_postmeta |
| wp_posts |
| wp_term_relationships |
| wp_term_taxonomy |
| wp_termmeta |
| wp_terms |
| wp_usermeta |
| wp_users |
+-----------------------+
12 rows in set (0.00 sec)
mysql> quit;
Bye
Add metadata labels
Labels are an optional means to apply metadata to Docker containers, images, volumes and networks. Generally, I use a minimal set of description labels for my containers but ignore the other options.
Labels use a distinctive syntax where the reverse domain name followed by the metadata name makes up the key. You can read more about this at Docker object labels but here are a couple of examples.
"com.example.description=A description for example.com"
"com.devtidbits.description=A description for this blog, The Developer's Tidbits"
In the docker-compose.yml
I’ll add some labels to the containers and volumes.
version: "3.1"
services:
wordpress:
image: wordpress
labels:
- "com.example.description=Example WordPress site"
restart: always
ports:
- 8080:80
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wpinternal
WORDPRESS_DB_PASSWORD: stencil-running-cargo-savage
WORDPRESS_DB_NAME: myblogdb
volumes:
- wordpress:/var/www/html
db:
image: mysql:5.7
labels:
- "com.example.description=Example WordPress database"
restart: always
environment:
MYSQL_DATABASE: myblogdb
MYSQL_USER: wpinternal
MYSQL_PASSWORD: stencil-running-cargo-savage
MYSQL_RANDOM_ROOT_PASSWORD: "1"
volumes:
- db:/var/lib/mysql
volumes:
wordpress:
labels:
- "com.example.description=WordPress install"
db:
labels:
- "com.example.description=MySQL datastore"
To apply labels to the volumes, they need to be deleted and rebuilt, resulting in the loss of their data.
# Stop and remove the containers
docker-compose down
# Delete both the wordpress and db file volumes
docker volume rm examplecom_db examplecom_wordpress
# Rebuild, recreate, start and attach the container services
docker-compose up
# Inspect the new volumes
docker volume inspect examplecom_db examplecom_wordpress
[
{
"CreatedAt": "2019-10-30T02:48:25Z",
"Driver": "local",
"Labels": {
"com.docker.compose.project": "examplecom",
"com.docker.compose.version": "1.24.1",
"com.docker.compose.volume": "db",
"com.example.description": "MySQL datastore"
},
"Mountpoint": "/var/lib/docker/volumes/examplecom_db/_data",
"Name": "examplecom_db",
"Options": null,
"Scope": "local"
},
{
"CreatedAt": "2019-10-30T02:48:25Z",
"Driver": "local",
"Labels": {
"com.docker.compose.project": "examplecom",
"com.docker.compose.version": "1.24.1",
"com.docker.compose.volume": "wordpress",
"com.example.description": "WordPress install"
},
"Mountpoint": "/var/lib/docker/volumes/examplecom_wordpress/_data",
"Name": "examplecom_wordpress",
"Options": null,
"Scope": "local"
}
]
Long-form syntax
The docker-compose.yml
sample given on the WordPress docker hub page is intentionally compact. But for readability, I like to expand these configurations using the long-form syntax that was introduced in compose format 3.2. Firstly we need to update the version of docker-compose.yml
, which is 3.1. You may as well update it to the newest version at the time of writing, 3.7.
version: "3.7"
The two configurations in our compose file we can express in long-form are ports and volumes.
Ports
ports:
- 8080:80
In the long-form syntax.
ports:
- target: 80
published: 8080
protocol: tcp
Volumes
The WordPress volume.
volumes:
- wordpress:/var/www/html
volumes:
- type: volume
source: wordpress
target: /var/www/html
The database volume.
volumes:
- db:/var/lib/mysql
volumes:
- type: volume
source: db
target: /var/lib/mysql
WordPress configurations – wp-config.php
One of the most important files for any WordPress installation is wp-config.php
. In this Docker container though we can embed any configurations directly into docker-compose.yml
. A complete list of these configurations is at the WordPress support article https://wordpress.org/support/article/editing-wp-config-php/.
It’s important to note that most of the required database connection configurations are irrelevant because these are in the container’s environment values. Any additional configurations can be added using the WORDPRESS_CONFIG_EXTRA
variable. For example, these extra configs may be required and set to true
in a production environment.
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wpinternal
WORDPRESS_DB_PASSWORD: stencil-running-cargo-savage
WORDPRESS_DB_NAME: myblogdb
WORDPRESS_CONFIG_EXTRA: |
# Require SSL for admin and logins
define( 'FORCE_SSL_ADMIN', false );
# Disable plugin and theme update and installation
define( 'DISALLOW_FILE_MODS', false );
# Disable the plugin and theme editor
define( 'DISALLOW_FILE_EDIT', false );
Any changes to the WORDPRESS_CONFIG_EXTRA
variable requires the deletion of the existing wp-config.php
file that is on the WordPress volume. Otherwise, the changes are not applied.
wordpress_1 | WARNING: environment variable "WORDPRESS_CONFIG_EXTRA" is set, but "wp-config.php" already exists
wordpress_1 | The contents of this variable will _not_ be inserted into the existing "wp-config.php" file.
To delete an existing wp-config.php
in the container.
# Run the container in detached mode
docker-composeaup --detach
# Run an optional test shell command on the wordpress container
docker-compose exec wordpress ls
# Delete the wp-config.php
docker-compose exec wordpress rm wp-config.php
# Restart the container to generate a new wp-config.php
docker-compose down && docker-compose up --detach
# View the content of the new wp-config.php
docker-compose exec wordpress cat wp-config.php
The output from cat wp-config.php should reveal the following appended additions.
// WORDPRESS_CONFIG_EXTRA
# Require SSL for admin and logins
define( 'FORCE_SSL_ADMIN', false );
# Disable plugin and theme update and installation
define( 'DISALLOW_FILE_MODS', false );
# Disable the plugin and theme editor
define( 'DISALLOW_FILE_EDIT', false );
Add configurations to PHP
WordPress is a PHP: Hypertext Preprocessor application, an old open-sourced language for the web that was very popular in the late 1990s and 2000s. PHP has its methods of configuration directives in the form of php.ini files.
Fix the 2 MB WordPress upload limit
Out of the box PHP and so also WordPress has a hard 2 MB upload limit for file forms. It means any images, videos or imported data uploads have a size limit when using WordPress. We can quickly fix that by creating our PHP configuration and binding it to the WordPress container.
WordPress 2 MB maximum upload file size
Create a new PHP configuration file, and name it docker-uploads.ini
. Add the following configuration then save the changes.
# Allow HTTP file uploads
file_uploads = On
# Maximum size of an uploaded file
upload_max_filesize = 64M
# Maximum size of form post data
post_max_size = 64M
Update the docker-compose.yml
to bind the docker-uploads.ini
to the wordpress container and then restart the WordPress container.
volumes:
- type: volume
source: wordpress
target: /var/www/html
- type: bind
source: ./docker-uploads.ini
target: /usr/local/etc/php/conf.d/docker-uploads.ini
Visit the Upload New Media page to confirm the changes are active, http://localhost:8080/wp-admin/media-new.php
WordPress 64 MB maximum upload file size
Add configurations to Apache
The WordPress container usages a Debian installation of Apache HTTP Server to serve the web application. This combination uses a unique directory structure for Apache’s configuration files, but thankfully we can still bind our configurations to this setup.
Fix the Apache “could not reliably determine the server’s fully qualified domain name” notice
One problem we currently have in the log files that is fixable is this warning from Apache.
# Hostname and port that the server uses to identify itself
ServerName localhost
Create a new Apache configuration file, and name it docker-servername.conf
. Add the following configuration then save the changes.
Update the docker-compose.yml
to bind the docker-servername.conf
to the WordPress container and then rebuild.
volumes:
- type: volume
source: wordpress
target: /var/www/html
- type: bind
source: ./uploads.ini
target: /usr/local/etc/php/conf.d/uploads.ini
- type: bind
source: ./docker-servername.conf
target: /etc/apache2/conf-enabled/docker-servername.conf# Rebuild our container``docker-compose down && docker-compose up --build
The Apache warning for the fully qualified domain name is gone.
Add configurations to MySQL
Fix the MySQL “TIMESTAMP with implicit DEFAULT value is deprecated” warning
Out of the box, MySQL 5.7 throws this warning on startup. We can quickly fix this by binding our own MySQL configuration file to the DB container.
db_1 | [Warning] TIMESTAMP with implicit DEFAULT value is deprecated. Please use --explicit_defaults_for_timestamp server option (see documentation for more details).
Create a new MySQL configuration file, and name it docker-fixes.cnf
. Add the following configuration then save the changes.
[mysqld]
explicit_defaults_for_timestamp=on
Update the docker-compose.yml
to bind the docker-fixes.cnf
to the DB container and then restart.
volumes:
- type: volume
source: db
target :/var/lib/mysql
- type: bind
source: ./docker-fixes.cnf
target: /etc/mysql/conf.d/docker-fixes.cnfdocker-compose down && docker-compose up
The TIMESTAMP with implicit DEFAULT value is deprecated warning should now not appear.
Add Adminer, a web interface for MySQL
For an optional, lightweight web interface for the MySQL database, you can attach an Adminer container to the composition. It also uses port 8080, which conflicts with our WordPress container, so I’ve swapped it to port 8070.
Update the docker-compose.yml
to add the Adminer container to the composition and then restart.
services:
adminer:
image: adminer
restart: always
ports:
- target: 8080
published: 8070
protocol: tcp
Visit http://localhost:8070 and use the username, password and database values saved to the MYSQL_DB
… environment values to sign-in.
Adminer login
Congratulations, now you know how to customise all the components your WordPress Docker installation. The final result for the docker-compose.yml
is shown below and is also available at https://github.com/bengarrett/devtidbits.com.
Get Wordpress::
docker pull wordpress
touch docker-fixes.cnf
nano docker-fixes.cnf
[mysqld]
explicit_defaults_for_timestamp=on
touch docker-servername.conf
nano docker-servername.conf
# Hostname and port that the server uses to identify itself
ServerName localhost
touch docker-uploads.ini
nano docker-uploads.ini
# Allow HTTP file uploads
file_uploads = On
# Maximum size of an uploaded file
upload_max_filesize = 64M
# Maximum size of form post data
post_max_size = 64M
# Memory limit
memory_limit = 768M
#ExecutionTime
max_execution_time = 3000
# Input Time
max_input_time = 3500
# maximum number of input variables allowed per request
max_input_vars = 10000
# Trick for long posts
ini_set(‘pcre.recursion_limit’,20000000);
ini_set(‘pcre.backtrack_limit’,10000000);
# this lets you upload any file type
define(‘ALLOW_UNFILTERED_UPLOADS’, true);
touch docker-compose.yml
nano docker-compose.yml
version: “3.7”
services:
wordpress:
image: wordpress
labels:
– “com.example.description=Example WordPress site”
restart: always
ports:
– target: 80
published: 8080
protocol: tcp
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wpinternal
WORDPRESS_DB_PASSWORD: stencil-running-cargo-savage
WORDPRESS_DB_NAME: myblogdb
WORDPRESS_CONFIG_EXTRA: |
# Require SSL
define( ‘FORCE_SSL_ADMIN’, false);
# Disable plugin and theme update and installation
define( ‘DISALLOW_FILE_MODS’, false );
# Disable the plugin and theme editor
define( ‘DISALLOW_FILE_EDIT’, false )
volumes:
– type: volume
source: wordpress
target: /var/www/html
– type: bind
source: ./docker-uploads.ini
target: /usr/local/etc/php/conf.d/docker-uploads.ini
– type: bind
source: ./docker-servername.conf
target: /etc/apache2/conf-enabled/docker-servername.conf
db:
image: mysql:5.7
labels:
– “com.example.description=Example WordPress database”
restart: always
environment:
MYSQL_DATABASE: myblogdb
MYSQL_USER: wpinternal
MYSQL_PASSWORD: stencil-running-cargo-savage
MYSQL_RANDOM_ROOT_PASSWORD: “1”
volumes:
– type: volume
source: db
target: /var/lib/mysql
– type: bind
source: ./docker-fixes.cnf
target: /etc/mysql/conf.d/docker-fixes.cnf
adminer:
image: adminer
restart: always
ports:
– target: 8080
published: 8070
protocol: tcp
volumes:
wordpress:
labels:
– “com.example.description=WordPress install”
db:
labels:
– “com.example.description=MySQL datastore”
PULL WORDPRESS::::::::::::
docker pull wordpress
RUN YAML::
docker-compose up -d
8070 – admind
8080 – wp