Installing multiple WordPress using Docker

Looking to install and run multiple containerized WordPress sites on a single host machine?

Let’s do it.

WordPress has 2 components: the actual WordPress installation files and the database. As a bonus we have Certbot, which gives us free HTTPS. We put each in it’s own container.

Since it’s a multi-container setup I like to use Docker Compose.

The folder structure

Create 3 empty folders inside a parent folder:

  • my-site (will contain a WordPress installation)
  • nginx (will contain Nginx)
  • certs (will contain shared files between WordPress and Nginx)
mkdir -p /docker/my-site
mkdir -p /docker/nginx
mkdir -p /docker/certs

WordPress

Each of our WordPress installations will be in a folder of their own. Let’s start off with the first one, which we will set up inside my-site folder.

Create the WordPress docker-compose.yml file with your favourite editor:

vim /docker/my-site/docker-compose.yml

The file will configure 3 containers:

  • WordPress (the wordpress installation files)
  • Database (the database for wordpress)
  • Certbot (for fetching the HTTPS certificate)
# WordPress containers definition /docker/my-site/docker-compose.yml
version: '3.5'

services:
  wordpress:
    image: wordpress
    restart: always
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: wpuser
      WORDPRESS_DB_PASSWORD: wppass
      WORDPRESS_DB_NAME: wpdb
    volumes:
      - ./volume_wp:/var/www/html
    networks:
      - my_net
  db:
    image: mysql:5.7
    restart: always
    environment:
      MYSQL_DATABASE: wpdb
      MYSQL_USER: wpuser
      MYSQL_PASSWORD: wppass
      MYSQL_RANDOM_ROOT_PASSWORD: '1'
    volumes:
      - ./volume_db:/var/lib/mysql
    networks:
      - my_net
  certbot:
    image: certbot/certbot
    volumes:
      - ../certs/conf:/etc/letsencrypt
      - ../certs/www:/var/www/certbot
    command: certonly --webroot -w /var/www/certbot --email ME@EXAMPLE.ORG -d EXAMPLE.FI -d WWW.EXAMPLE.FI --agree-tos

networks:
  my_net:
    external: true

Note: Edit the certbot command to include your email and domain(s).

Certbot has two volumes.

  • ../certs/conf will contain the fetched HTTPS certificates
  • ../certs/www is Certbot’s webroot which is served by Nginx

In addition to the services, the above configuration includes an external network definition, my_net. The purpose of my_net is to enable communication between the WordPress and Nginx environments.

Nginx, the web server

Create the Nginx docker-compose.yml file with your favourite editor:

vim /docker/nginx/docker-compose.yml

The file defines a single container:

  • Nginx (for serving the wordpress site content)
YAML
# Nginx container definition at /docker/nginx/docker-compose.yml
version: '3.5'

services:
  nginx:
    image: nginx
    restart: always
    ports:
      - 80:80
      - 443:443
    volumes:
      - ./conf.d:/etc/nginx/conf.d:ro
      - ../certs/www:/var/www/certbot:ro
      - ../certs/conf:/etc/nginx/ssl:ro
    networks:
      - my_net

networks:
  my_net:
    external: true

Note Nginx environment is attached to the same my_net network as is the WordPress environment.

A few words about the 3 mounted volumes:

  • ./conf.d will contain site configurations Nginx should serve
  • ../certs/www is used by certbot as the web root
  • ../certs/conf is where the HTTPS certificates are saved to

Next, create an initial Nginx conf for our WordPress site, using your favourite editor:

mkdir /docker/nginx/conf.d
vim /docker/nginx/conf.d/my-site.conf
Nginx
# /docker/nginx/conf.d/my-site.conf
#
# Initial conf used for fetching the HTTPS cert.
#
server {
  listen 80;

  server_name EXAMPLE.FI;

  location ~ /.well-known/acme-challenge {
    root /var/www/certbot;
  }
}

Note: Replace the example domain with your own domain.

Start your Nginx server:

cd /docker/nginx
docker compose up -d

Fetch the HTTPS certs by running the certbot service:

cd /docker/my-site
docker compose up certbot

Great! Now you should have HTTPS certificates in /docker/certs/conf.

Check the certs were created, and we’re ready to enable HTTPS.

Replace the initial Nginx conf with the HTTPS enabled conf:

vim /docker/nginx/conf.d/my-site.conf
Nginx
# /docker/nginx/conf.d/my-site.conf
#
# Conf for the WordPress site.
#
server {
  listen 80;

  server_name EXAMPLE.FI WWW.EXAMPLE.FI;

  return 301 https://$host$request_uri;
}

server {
  listen 443 ssl;

  server_name EXAMPLE.FI;

  return 301 https://WWW.EXAMPLE.FI$request_uri;

  ssl_certificate /etc/nginx/ssl/live/EXAMPLE.FI/fullchain.pem;
  ssl_certificate_key /etc/nginx/ssl/live/EXAMPLE.FI/privkey.pem;
}

server {
  listen 443 ssl;

  server_name WWW.EXAMPLE.FI;

  location ~ /.well-known/acme-challenge {
    root /var/www/certbot;
  }

  location / {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_pass http://wordpress/;
  }

  ssl_certificate /etc/nginx/ssl/live/EXAMPLE.FI/fullchain.pem;
  ssl_certificate_key /etc/nginx/ssl/live/EXAMPLE.FI/privkey.pem;
}

Note: Replace the EXAMPLE domains with your own domain.

Launch it!

Fire up WordPress:

cd /docker/my-site
docker compose up -d

Tip: To see possible errors inside the containers you can either use docker compose logs or drop the -d param above.

Without -d the containers are launched on the foreground. To exit hit CTRL+C, which will also exit your newly set up services. Use -d to launch into the background.

Reload the Nginx process to enable your newly edited my-site.conf:

docker exec -it nginx-nginx-1 nginx -s reload

(If the above fails because your container is not named nginx-nginx-1 use docker ps to find the correct name.)

Open your domain on your favourite browser. You should be greeted by the WordPress installer.

Adding multiple WordPress sites

So far we have a single WordPress up and running. How to add more? It’s easy:

# Create the docker compose configuration for the second WordPress
mkdir /docker/my-second-site
vim /docker/my-second-site/docker-compose.yml

# Create the initial nginx conf for fetching the HTTPS cert
vim /docker/nginx/conf.d/my-second-site.conf
docker exec -it nginx-nginx-1 nginx -s reload

# Launch the certbot service, which will fetch the HTTPS cert and exit
cd /docker/my-second-site
docker compose up certbot

# Edit the nginx conf to the HTTPS enabled version
vim /docker/nginx/conf.d/my-second-site.conf
docker exec -it nginx-nginx-1 nginx -s reload

# Launch your second WordPress!
cd /docker/my-second-site
docker compose up -d

Alright, second WordPress up and running!

There are far fewer steps when setting up your second (or third, or fourth) WordPress. The details for the steps are as in the steps before.

Enjoy!

Katso myös