Docker Swarm is a popular infrastructure tool for running websites and web applications online. Here’s how to deploy WordPress Multisite on Docker Swarm.

Server racks.
Server racks. Source.

This post extends https://blog.budhajeewa.com/deploy-wordpress-on-docker-swarm/. We’ll refer to that as the “parent post” in this post.

Setup WordPress

Follow the parent post, and get a WordPress stack up and running.

Allow Multisite Feature

Open the docker-compose.yml and add the highlighted lines to it.

version: '3'
services:
 wordpress:
  image: wordpress:5.1.1-php7.1-apache
  depends_on:
  - mariadb
  volumes:
  - ./volumes/wordpress/content:/var/www/html/wp-content
  environment:
   WORDPRESS_DB_HOST: mariadb:3306
   WORDPRESS_DB_PASSWORD: root
   WORDPRESS_CONFIG_EXTRA: |
    /* Multisite */
    define('WP_ALLOW_MULTISITE', true );
  ports:
  - 8001:80
 mariadb:
  image: mariadb:10.4.4
  volumes:
  - ./volumes/mariadb/data:/var/lib/mysql
  environment:
   MYSQL_ROOT_PASSWORD: root

Then run make deploy (This is facilitate by https://blog.budhajeewa.com/docker-swarm-deploying-web-apps-and-sites/#easy-redeployment-with-a-makefile.). Wait till the the site is back up, and continue with the rest of the instructions.

Network Setup

In the WordPress dashboard, go to Tools → Network Setup, choose the address style, fill in network details, and click “Install”. It will then present you with some codes to be added to the wp-config.php and some code to replace current content of.htaccess file with.

Update wp-config.php

As you’re running a Docker Container based on an Docker Image for WordPress, you don’t really have to edit the file manually, and the changes will be lost sooner or later, as Docker Containers are volatile. We can use the environment variable WORDPRESS_CONFIG_EXTRA to add the configuration changes.

What I have shown below is what was given to me by WordPress; yours may change. Be sure to use your own code, as given by WordPress. I’ve highlighted the newly added lines.

version: '3'
services:
 wordpress:
  image: wordpress:5.1.1-php7.1-apache
  depends_on:
  - mariadb
  volumes:
  - ./volumes/wordpress/content:/var/www/html/wp-content
  environment:
   WORDPRESS_DB_HOST: mariadb:3306
   WORDPRESS_DB_PASSWORD: root
   WORDPRESS_CONFIG_EXTRA: |
    /* Multisite */
    define('WP_ALLOW_MULTISITE', true );
    define('MULTISITE', true);
    define('SUBDOMAIN_INSTALL', true);
    define('DOMAIN_CURRENT_SITE', 'example.com');
    define('PATH_CURRENT_SITE', '/');
    define('SITE_ID_CURRENT_SITE', 1);
    define('BLOG_ID_CURRENT_SITE', 1);
  ports:
  - 8001:80
 mariadb:
  image: mariadb:10.4.4
  volumes:
  - ./volumes/mariadb/data:/var/lib/mysql
  environment:
   MYSQL_ROOT_PASSWORD: root

Update .htaccess

Look, Apaches!
Look, Apaches! Source.

Unfortunately, there’s no quick environment variable to replace the content of the .htaccess file. So we have to create a file, and mount it at the proper location.

In the com-example/volumes/wordpress/ directory, create a file named htaccess (No period at the start of the file name, as we don’t want to turn that into a hidden file.), and add the following content into it.

RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]

# add a trailing slash to /wp-admin
RewriteRule ^wp-admin$ wp-admin/ [R=301,L]

RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
RewriteRule ^(wp-(content|admin|includes).*) $1 [L]
RewriteRule ^(.*\.php)$ $1 [L]
RewriteRule . index.php [L]

Then open the docker-compose.yml file and add the highlighted line to it.

version: '3'
services:
 wordpress:
  image: wordpress:5.1.1-php7.1-apache
  depends_on:
  - mariadb
  volumes:
  - ./volumes/wordpress/content:/var/www/html/wp-content
  - ./volumes/wordpress/htaccess:/var/www/html/.htaccess
  environment:
   WORDPRESS_DB_HOST: mariadb:3306
   WORDPRESS_DB_PASSWORD: root
   WORDPRESS_CONFIG_EXTRA: |
    /* Multisite */
    define('WP_ALLOW_MULTISITE', true );
    define('MULTISITE', true);
    define('SUBDOMAIN_INSTALL', true);
    define('DOMAIN_CURRENT_SITE', 'example.com');
    define('PATH_CURRENT_SITE', '/');
    define('SITE_ID_CURRENT_SITE', 1);
    define('BLOG_ID_CURRENT_SITE', 1);
  ports:
  - 8001:80
 mariadb:
  image: mariadb:10.4.4
  volumes:
  - ./volumes/mariadb/data:/var/lib/mysql
  environment:
   MYSQL_ROOT_PASSWORD: root

We now mount the outside volumes/wordpress/htaccess file to the Docker Container’s mount point /var/www/html/.htaccess.

Above is how your docker-compose.yml would ultimately look.

Support Any Subdomain and Main Domain in NGINX

You only have to do this if you choose sub domains as the domain style for your child sites.

As instructed in https://blog.budhajeewa.com/deploy-wordpress-on-docker-swarm/#map-a-domain-to-the-wordpress-installation, our WordPress Stack’s NGINX configuration currently looks like following:

server {
        listen 80;
        server_name example.com;
 
        location / {
                proxy_pass http://host.public.ip.address:8001;
                proxy_set_header Host $host;
        }
}

It only supports the main domain, you can get it to support the main domain as well as any sub domains of it, by prepending a period to the server_name value. This is how the configuration file would look like after the change. Changed line is highligted.

server {
        listen 80;
        server_name .example.com;
 
        location / {
                proxy_pass http://host.public.ip.address:8001;
                proxy_set_header Host $host;
        }
}

Restart NGINX, and you should be able to get a response from WordPress if you visit sub-domain.example.com. If that is working, you can proceed to create the child sites you need.