Deploy WordPress with Docker and Traefik 2

I’ve written an article of using docker and Traefik to deploy WordPress a year and a half ago. There have been so many changes in the past 18 months, some information on my previous article seems to be inadequate or even wrong now. My plan is to rewrite all my Traefik related articles and use Traefik 2 with SSL enabled by default.

From this article, I will stop copying Traefik configuration files in each article. You can find the default configuration here.

Let me start with the docker-compose.yml file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
version: '3.7'

services:
db:
image: mariadb
container_name: wp-db
volumes:
- db-data:/var/lib/mysql
networks:
- default
restart: always
environment:
MYSQL_ROOT_PASSWORD: supersecretpassword
MYSQL_DATABASE: db
MYSQL_USER: dbuser
MYSQL_PASSWORD: dbpassword

wordpress:
depends_on:
- db
image: wordpress:latest
container_name: wordpress
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_NAME: db
WORDPRESS_DB_USER: dbuser
WORDPRESS_DB_PASSWORD: dbpassword
volumes:
- ./wp-data:/var/www/html/wp-content
networks:
- proxy
- default
restart: always
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
- "traefik.http.routers.wordpress-secure.entrypoints=websecure"
- "traefik.http.routers.wordpress-secure.rule=Host(`blog.yourdomain`)"
# - "traefik.http.routers.wordpress-secure.service=wordpress-service"
# - "traefik.http.services.wordpress-service.loadbalancer.server.port=80"

volumes:
db-data:
name: wp-db-data
networks:
proxy:
external: true

Service : DB

Image:

There are two database images you can choose from, MySQL:5.7 or MariaDB. As the password authentication method changed in MySQL 8, if you really want to use MySQL, please choose version 5.7 or you need extra command[1] listed below.

1
2
image: mysql:8
command: '--default-authentication-plugin=mysql_native_password'

I don’t bother with extra settings so I will stick with MariaDB.

Volume:

I create named volume called wp-db-data[2] here. We use named volume for persistent data. You won’t lost your data when you upgrade WordPress nor even remove the container.

Network:

I add this database service to default network to avoid unrelated containers from accessing this container. Docker use the folder name as prefix when it creates default network. For example my docker-compose file is in ~/wordpressfolder, docker will create default network as wordpress_default.

Environment

I set root password, database name, username and password here.

Please do NOT use such simple username/password in production environment

Service : WordPress

depends_on:

WordPress will run after db. If we stop both containers, WordPress will be stopped before db.

Image:

I used the latest version of WordPress official image.

Environment

DB-HOST will be the port 3306 of db service. The other variables are just database name, username and password. Please make sure you use the same detail as we defined in db service.

Volumes

I mount /var/www/html from WordPress container to our local folder ./wp-data. The benefit of doing that is we can modify files on our server directly. For example if we need to upload pictures, plugins or themes, we can put it in this folder straight away. In case your website is down due to bad plugin updates, you can also delete this plugin from here. There is no need to docker exec -it <mycontainer> bash any more.

Network

I expose WordPress container to two networks. One is proxy, it is for Traefik to find this container; the other one is default which will link WordPress and DB together in the same network.

Label

This is the interesting part.

According to official Docker Service documentation,

  1. If a label defines a router (e.g. through a router Rule) and a label defines a service (e.g. implicitly through a loadbalancer server port value), but the router does not specify any service, then that service is automatically assigned to the router.
  2. If a label defines a router (e.g. through a router Rule) but no service is defined, then a service is automatically created and assigned to the router.

In summary, if a user does not want to link router to a service, nor define any service labels. Traefik will take care of everything including creating service and assigning service to router (host name).

Now we just need to run docker-compose up -d, WordPress should be up in no time.

Thanks for reading, I hope this article can be of any help. Let me know if you have any questions.


  1. https://github.com/docker-library/wordpress/issues/313 ↩︎

  2. Please note we must define this named container at the end of docker-compose file. I also named this volume wp-db-data, if we don’t use that, docker will add the folder name as prefix to your volume name. for example wordpress_db-data ↩︎