CMS

How to Deploy Highly Available WordPress in Docker Swarm Behind Caddy v2.3

Caddy's reverse_proxy is capable of serving any FastCGI application, making it perfectly suited for deploying highly available WordPress using PHP-FPM.

Rajasekhar Gundala··6 min read

Caddy’s reverse_proxy is capable of serving any FastCGI application, making it an excellent choice for PHP apps like wordpress:php7.4-fpm-alpine. Caddy effortlessly proxies requests to a PHP FastCGI server such as PHP-FPM.

In this post, I will show you how to deploy a highly available WordPress instance in a Docker Swarm Cluster using Docker Compose, sitting securely behind a Caddy web server. Previously, I demonstrated how to deploy WordPress behind Traefik, but Caddy offers a much simpler configuration for PHP environments.

Prerequisites

Ensure you fulfill the following requirements before proceeding with the deployment:

  1. A Docker Swarm Cluster configured with GlusterFS for persistent storage.
  2. Caddy deployed as an ingress reverse proxy to expose microservices externally.
  3. A running MariaDB database stack to host the application databases.

Introduction to WordPress

WordPress is open-source software you can use to create a beautiful website, blog, or app.

WordPress is used by more than 60 million websites, powering over 33% of the top 10 million sites on the web. It is undeniably the most popular Content Management System (CMS) in the world.

Built on PHP and MySQL, WordPress features a robust plugin architecture and a highly customizable template system (Themes). While originally created for blogging, it has evolved to support media galleries, membership sites, learning management systems (LMS), and e-commerce stores.

WordPress Features

WordPress combines simplicity for users and publishers with under-the-hood complexity for developers. This makes it incredibly flexible while remaining easy to use.

There are thousands of plugins that extend what WordPress does, meaning its actual functionality is nearly limitless. Best of all, we are free to use, modify, or extend the code for commercial projects without licensing fees.

Here are some of the features that make WordPress an enterprise standard:

  1. Flexibility and Extensibility
  2. Simplicity and Ease of Use
  3. Responsive out-of-the-box Themes
  4. High Performance when properly cached
  5. Manage on the Go via Mobile Apps
  6. High Security with active community patching

Persisting WordPress Data with GlusterFS

Containers are fast to deploy and make efficient use of system resources. However, their filesystems are ephemeral. If a container restarts, local data is lost.

To ensure our media uploads, themes, and plugins survive restarts, we will use GlusterFS. I previously set up a replicated GlusterFS volume to ensure data is mirrored across all nodes in the cluster.

GlusterFS Replicated Volume

The volume is mounted across all nodes. When data is written to the /mnt partition, it is instantly replicated to the other nodes in the cluster.

If any node fails, the application automatically restarts on another node without losing data.

For WordPress, we need to map the container’s /var/www/html volume to our persistent storage.

Create a folder named wordpress-caddy in the /mnt directory:

cd /mnt
sudo mkdir -p wordpress-caddy

Watch the video below for a complete guide on setting up a GlusterFS Replicated Volume.


Prepare the Deployment Environment

We will use Docker Compose to define our deployment environment.

Navigate to the /opt directory on your Swarm manager node and create the configuration directory for WordPress:

cd /opt
sudo mkdir -p wordpress
cd wordpress
sudo touch wordpress.yml

Understanding Caddy’s PHP FastCGI Directive

Caddy’s reverse proxy is fully capable of serving FastCGI applications. The php_fastcgi directive is a convenient shorthand that proxies requests directly to a PHP FastCGI server such as PHP-FPM, automatically handling index routing and path splitting.

FPM (FastCGI Process Manager) is an alternative PHP FastCGI implementation featuring advanced process management, graceful restarts, and stdout/stderr logging—making it highly useful for heavy-loaded sites.

I am going to utilize Caddy’s php_fastcgi directive to deploy WordPress using the highly optimized wordpress:php7.4-fpm-alpine Docker container.

Caddyfile Configuration for WordPress

The Caddyfile is a highly readable configuration format for the Caddy web server.

Caddyfile is easy to write, easy to understand, and expressive enough for almost all use cases.

Here is the production-ready Caddyfile block required to expose WordPress securely alongside PHP-FPM. Learn more about writing Caddyfiles here.

{
    email you@example.com
    cert_issuer acme
}

wordpress.example.com {
    log {
        output file /var/log/caddy/wordpress.log {
            roll_size 20mb
            roll_keep 2
            roll_keep_for 6h
        }
        format console
        level error
    }
    
    # Block access to hidden files and vulnerable endpoints
    @disallowed {
        path /xmlrpc.php
        path *.sql
        path /wp-content/uploads/*.php
    }
    rewrite @disallowed '/index.php'

    root * /var/www/html
    php_fastcgi wordpress:9000
    file_server
    encode gzip zstd
}

Full Stack Deployment (Combined Docker Compose)

Here is the combined docker-compose.yml file deploying both Caddy and the WordPress FPM container. It utilizes Docker Secrets to securely pass the database credentials to the WordPress instance.

version: "3.7"

services:
  caddy:
    image: tuneitme/caddy
    ports:
      - "80:80"
      - "443:443"
    networks:
      - caddy
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - /mnt/caddydata:/data
      - /mnt/caddyconfig:/config
      - /mnt/caddylogs:/var/log/caddy
      - /mnt/wordpress-caddy:/var/www/html
    deploy:
      placement:
        constraints:
          - node.role == manager
      replicas: 1
      update_config:
        parallelism: 2
        delay: 10s
      restart_policy:
        condition: on-failure

  wordpress:
    image: wordpress:php7.4-fpm-alpine
    depends_on:
      - maindb
    volumes:
      - /mnt/wordpress-caddy:/var/www/html
    secrets:
      - wordpress_db_host
      - wordpress_db_user
      - wordpress_db_name
      - mysql_db_password
    environment:
      - WORDPRESS_DB_HOST_FILE=/run/secrets/wordpress_db_host
      - WORDPRESS_DB_USER_FILE=/run/secrets/wordpress_db_user
      - WORDPRESS_DB_NAME_FILE=/run/secrets/wordpress_db_name
      - WORDPRESS_DB_PASSWORD_FILE=/run/secrets/mysql_db_password
    networks:
      - caddy
    ports:
      - "9000:9000"
    deploy:
      placement:
        constraints: [node.role == worker]
      replicas: 1
      update_config:
        parallelism: 2
        delay: 10s
      restart_policy:
        condition: on-failure

secrets:
  wordpress_db_host:
    file: ./wordpress_db_host.txt
  wordpress_db_user:
    file: ./wordpress_db_user.txt
  wordpress_db_name:
    file: ./wordpress_db_name.txt
  mysql_db_password:
    file: ./mysql_db_password.txt

volumes:
  caddydata:
    driver: "local"
  caddyconfig:
    driver: "local"
  caddylogs:
    driver: "local"
  wordpress-caddy:
    driver: "local"

networks:
  caddy:
    external: true

Ensure you have created the .txt files containing your database secrets in the same directory as your docker-compose.yml file before deploying.

Deploy WordPress to Docker Swarm

Deploy the stack to your Swarm using the following command:

docker stack deploy --compose-file wordpress.yml wp

In Docker Swarm, whatever you deploy via compose is called a “stack,” and it contains multiple “services” as defined in your file.

Access and Install WordPress

Open your browser and navigate to wordpress.example.com. It will automatically redirect securely to https://wordpress.example.com/wp-admin/install.php (ensure you replace example.com with your actual domain).

Ensure you have configured a DNS A-Record or CNAME pointing wordpress.example.com to your Swarm ingress load balancer.


Reference Images from the Deployment:

WordPress Installation - Language Selection

WordPress Installation - Basic Information

WordPress Installation Success

WordPress Login Screen

Caddy WordPress Site

The deployment of a highly available WordPress instance behind Caddy in our Docker Swarm cluster is complete! Using the PHP-FPM image ensures that our CMS is optimized for heavy loads.

Stay tuned for more application deployments in our Docker Swarm Cluster! Let me know your feedback or thoughts by commenting below.

Share
Written by
Rajasekhar Gundala

Senior Infrastructure & Web Platform Leader.

Continue reading

Weekly Engineering Notes.

A weekly digest on infrastructure, observability, Rust, and the open web. No spam, just technical signals.

Free. Unsubscribe in one click.