Serving Wordpress with Caddy 2 and Docker
I spent quite a bit of time figuring out how to serve multiple wordpress sites via docker using the excellent Caddy server.
Required Setup
I have this served on a Raspberry Pi 4, this is the setup for this. You will be able to do this with other linux derivatives.
- Raspberry Pi 4 – 2gb ram or above.
- 120GB M2 SSD in USB3 Enclosure
- Raspbian 64bit or Ubuntu 64 bit installed and booting off the SSD.
- Docker installed from the docker instructions. link
- docker-compose latest version. Install with.
> pip3 install docker-compose
- Create a user on the pi that is not “pi” and give that user “docker” group permission.
> sudo useradd -m <your-user>
> sudo passwd <your-user>
> sudo usermod -aG docker <your-user>
- Login as that user.
Site Name(s)
You should know what your site(s) name(s) will be, eg "my-family", "bobs-stuff".
You will need to use this site name in several places below where you see <site1>
Directory Structure
This is the directory structure that we will use from the users home directory.
docker
conf
caddy
sites-enabled
snippets
mariadb
data
caddy2
mariadb
Create these with the following commands:
mkdir -p ~/docker/conf/caddy/sites-enabled
mkdir -p ~/docker/conf/caddy/snippets
mkdir -p ~/docker/data/caddy2
mkdir -p ~/docker/data/mariadb
Configuration Files
docker-compose.yaml
Location for this file is ~/docker/docker-compose.yaml
Use this configuration.
~/docker/docker-compose.yaml
version: '3.7'
networks:
backend:
volumes:
# create a volume for each wordpress site
wordpress-<site1>-volume:
services:
mariadb:
image: mariadb:latest
container_name: mariadb
networks:
- backend
volumes:
- "./data/mariadb:/var/lib/mysql"
- "./conf/mariadb:/docker-entrypoint-initdb.d"
restart: always
ports:
- "3306:3306"
env_file:
- "./conf/mariadb.env"
healthcheck:
test: pidof mysqld || exit 1
interval: 120s
timeout: 10s
retries: 3
# repeat wordpress-site-name service for each wordpress site you
# create
wordpress-<site1>:
depends_on:
- mariadb
- caddy
image: wordpress:php7.4-fpm-alpine
container_name: wordpress-<site1>
restart: always
networks:
- backend
env_file:
- "./conf/wordpress-<site1>.env"
volumes:
- wordpress-<site1>-volume:/var/www/html
- "./conf/php.ini:/usr/local/etc/php/conf.d/custom.ini"
healthcheck:
test: pidof php-fpm || exit 1
interval: 120s
timeout: 10s
retries: 3
caddy:
image: caddy:2-alpine
container_name: caddy
restart: always
environment:
# change to your timezone
- "TZ=Pacific/Auckland"
networks:
- backend
ports:
- "80:80"
- "443:443"
- "2019:2019"
volumes:
# repeat these two volumes for each wordpress site.
- "wordpress-<site1>-volume:/usr/share/caddy/wordpress-<site1>"
- "./data/caddy2/data:/data/caddy"
- "./data/caddy2/config:/config"
healthcheck:
test: pidof caddy || exit 1
interval: 120s
timeout: 10s
retries: 3
adminer:
image: adminer
container_name: adminer
restart: always
depends_on:
- mariadb
networks:
- backend
ports:
- "8081:8080"
environment:
- "MYSQL_ROOT_PASSWORD=mariadb"
- "TZ=Pacific/Auckland"
healthcheck:
test: pidof php || exit 1
interval: 120s
timeout: 10s
retries: 3
mariadb.env
This file provides initial and continuing configuration for the mariadb docker container.
~/docker/conf/mariadb.env
MYSQL_ROOT_PASSWORD=<a-root-password>
MYSQL_USER=<a-username>
MYSQL_PASSWORD=<a-password>
TZ=Pacific/Auckland
Replace <a-root-password>
with a root password and <a-username>
, <a-password>
with a username and password for mariadb to use when creating the new database. Change TZ
to your timezone.
wordpress-<site1>.env
Each wordpress site will require its own ".env" file. The template is as follows.
~/docker/conf/wordpress-<site1>.env
WORDPRESS_DB_HOST=mariadb:3306
WORDPRESS_DB_TYPE=mysql
WORDPRESS_DB_PASSWORD=<a-wordpress-password>
WORDPRESS_DB_USER=wordpress
# note the use of underscore on the DB_NAME, dash is not valid
WORDPRESS_DB_NAME=wordpress_<site1>
WORDPRESS_FS_METHOD=direct
TZ=Pacific/Auckland
You will need to have a password to use for the wordpress user, all wordpress sites in my configuration use the same user and password, you can use separate ones if you want. We will create the database later.
pip.ini
PHP requires a configuration, this follows.
~/docker/conf/php.ini
memory_limit = 32M
upload_max_filesize = 24M
post_max_size = 32M
Caddyfile
The Caddyfile is the configuration file for Caddy. This one is quite simple as we will include templates and configurations for each site.
~/docker/conf/caddy/Caddyfile
import sites-enabled/*
Wordpress Caddy template
We use a template to configure Caddy to serve each wordpress site.
~/docker/conf/caddy/snippets/wordpress
# wordpress site
# args.0 = dns name
# args.1 = site name
{args.0} {
root * /usr/share/caddy/wordpress-{args.1}
log
@canonicalPath {
file {
try_files {path}/index.php
}
not {
path */
}
}
@phpFiles {
path *.php
}
route {
redir @canonicalPath {path}/ 308
try_files {path} {path}/index.php index.php
reverse_proxy @phpFiles {
to wordpress-{args.1}:9000
transport fastcgi {
split .php
root /var/www/html
}
}
respond /uploads/*.php 404
file_server
}
}
wordpress-sites configuration
You will need to create a configuration for each site in the following file.
~/docker/conf/caddy/sites-enabled/wordpress-sites
# repeat this line for each site
import /etc/caddy/snippets/wordpress <side-domain-name> <site1>
<site-domain-name>
is the domain name to serve on, for example www.example.co.nz
.
Starting Up
Now we are ready to start the services, for this we use docker-compose
cd ~/docker
docker-compose up -d
Docker will download the images, and start them all up. you can check the status of the containers with the following command
docker-compose ps
Which will show the following output, your sites will be listed with a container for each.
Name Command State Ports
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
adminer entrypoint.sh docker-php-e ... Up (healthy) 0.0.0.0:8081->8080/tcp,:::8081->8080/tcp
caddy caddy run --config /etc/ca ... Up (healthy) 0.0.0.0:2019->2019/tcp,:::2019->2019/tcp, 0.0.0.0:443->443/tcp,:::443->443/tcp, 0.0.0.0:80->80/tcp,:::80->80/tcp
mariadb docker-entrypoint.sh mysqld Up (healthy) 0.0.0.0:3306->3306/tcp,:::3306->3306/tcp
wordpress-site1 docker-entrypoint.sh php-fpm Up (healthy) 9000/tcp
Create Database
Wordpress needs a database to use, we have previously set variables that wordpress for each
site will use to store its data. We need those username,password and database name from the wordpress-<site1>.env
files.
For each site you do the following.
> cd ~/docker
# connect to mysql
> docker-compose exec mariadb mysql -u root --password=<a-root-password>
# create user/users for the wordpress sites to use
MariaDB [(none)]> CREATE USER wordpress IDENTIFIED BY '<a-wordpress-password>';
# create databases for each wordpress site and set access
MariaDB [(none)]> CREATE DATABASE wordpress_<site1>;
MariaDB [(none)]> GRANT ALL PRIVILEGES ON wordpress_<site1>.* TO "wordpress"@"%";
# finish
MariaDB [(none)]> FLUSH PRIVILEGES;
MariaDB [(none)]> EXIT;
Open wordpress
Use your browser and go to the site address you setup. If everything is OK, you will be presented with the wordpress setup page. You can now start creating your wordpress site.