Skip to content

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.