Dockerized WordPress Architecture

For the currently running WordPress site, I envisioned and applied the architecture below.

All the components in the rectangle are Docker containers. NGINX container accepts HTTP(S) requests routes them to the WordPress container listening on port 80 only within Docker internal network. I could have exposed WordPress container listening to port 80 but it wasn’t going to be able to handle HTTPS requests, so I decided to have NGINX container to handle SSL cert.

version: "3.9"
    
services:
  db:
    image: mysql:5.7
    volumes:
      - ./db_data:/var/lib/mysql 
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: hogehogehogehoge
      MYSQL_DATABASE: mydatabase
      MYSQL_USER: buhibuhi
      MYSQL_PASSWORD: foobar
    networks:
      proxynet:
  phpmyadmin:
    depends_on:
      - db
    image: phpmyadmin/phpmyadmin
    restart: always
    ports:
      - '8080:80'
    environment:
      PMA_HOST: db
      MYSQL_ROOT_PASSWORD: hogehogehogehoge
    networks:
      proxynet:
  wordpress:
    image: wordpress:latest
    container_name: wordpress
    depends_on:
      - db
    restart: always
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: buhibuhi
      WORDPRESS_DB_PASSWORD: foobar
      WORDPRESS_DB_NAME: mydatabase
      WORDPRESS_DEBUG: 'true'
    volumes:
      - ./html:/var/www/html
      - ./wp-content:/var/www/html/wp-content
    networks:
      proxynet:
  reverse:
    image: nginx:latest
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/conf.d:/etc/nginx/conf.d
    ports:
      - "80:80"
      - "443:443"
    restart: always
    networks:
      proxynet:
volumes:
  db_data: {}
  wordpress: {}
networks:
  proxynet:

Paste the whole YAML to docker-compose.yaml file. There is some more steps to do before you can spin up the containers.

Let’s set up a firewall on the OS side. Ubuntu utilizes ufw. Execute the command to install it.

sudo apt install ufw

Let’s open a few necessary ports…

sudo ufw allow 80 # for NGINX http request redirect handling
sudo ufw allow 443 # for SSL (HTTPS) requests
sudo ufw allow 8080 # for myPHPAdmin access
sudo ufw allow 22 # for SSH access

You will need to get the firewall to start when the OS starts, so execute the following command.

sudo ufw enable

In the directory you created docker-compose.yaml file, create nginx/nginx.conf file with the following configuration.

user  nginx;
worker_processes  2;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
   worker_connections  1024;
   use epoll;
   accept_mutex off;
}

http {
   include       /etc/nginx/mime.types;
   proxy_set_header X-Real-IP $remote_addr;
   proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

   default_type  application/octet-stream;

   log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                     '$status $body_bytes_sent "$http_referer" '
                     '"$http_user_agent" "$http_x_forwarded_for"';

   access_log  /var/log/nginx/access.log  main;

   sendfile        on;
   #tcp_nopush     on;

   keepalive_timeout  65;

   client_max_body_size 300m;
   client_body_buffer_size 128k;

   gzip  on;
   gzip_http_version 1.0;
   gzip_comp_level 6;
   gzip_min_length 0;
   gzip_buffers 16 8k;
   gzip_proxied any;
   gzip_types text/plain text/css text/xml text/javascript application/xml application/xml+rss application/javascript application/json;
   gzip_disable "MSIE [1-6]\.";
   gzip_vary on;

   server {
       listen       80 default_server;
       listen       [::]:80 default_server;
       server_name  _;
       sendfile        on;
       #tcp_nopush     on;

       keepalive_timeout  65;

      client_max_body_size 300m;
      client_body_buffer_size 128k;

      gzip  on;
      gzip_http_version 1.0;
      gzip_comp_level 6;
      gzip_min_length 0;
      gzip_buffers 16 8k;
      gzip_proxied any;
      gzip_types text/plain text/css text/xml text/javascript application/xml application/xml+rss application/javascript application/json;
      gzip_disable "MSIE [1-6]\.";
      gzip_vary on;
      return 301 https://hayato-iriumi.net$request_uri;
   }
    include /etc/nginx/conf.d/*.conf;
}


You need another configuration file at nginx/conf.d/ssl.conf. As you could see, this file handles HTTPS requests with the certificate and routes the requests to the wordpress container listening to the port 80 within the Docker network. So the port 80 is not exposed to the host side.

upstream wordpress_upstream {
    server wordpress:80;
}

server {
    server_name hayato-iriumi.net;
    listen 443 ssl;
    ssl_certificate /etc/nginx/conf.d/ssl/certificate.crt;
    ssl_certificate_key /etc/nginx/conf.d/ssl/private.key;

    location / {
        proxy_set_header        Host $host:$server_port;
        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;
        resolver 127.0.0.11;
        proxy_redirect http:// https://;
        proxy_pass http://wordpress_upstream;
        # Required for new HTTP-based CLI
        proxy_http_version 1.1;
        proxy_request_buffering off;
        proxy_buffering off; # Required for HTTP-based CLI to work over SSL
    }
}


Obviously, you need to have your SSL cert ready. I have blogged about a free SSL solution here, so please refer to it for your SSL cert. Place the SSL cert files at the specified location in the ssl.conf file.

Finally, if you go back to the directory where you created docker-compose.yaml file, and execute docker-compose up -d, you will be able to see the WordPress initial UI.

If you run through the WordPress installation process, you are half way there. Once you run through the installation process, it creates necessary tables in the MySQL database. Once you have the basic data structure automatically created, you need to export the data from the following tables and import them in the new database. It is easy to export and import data using myPHPAdmin UI even if you don’t know anything about SQL. (Knowing basics of SQL helps when there are problems in importing data)

  • wp_posts
  • wp_terms
  • wp_termsrelationships
  • wp_comments
  • wp_commentmeta
  • wp_postmeta
  • wp_term_taxonomy

Your WordPress site still lacks the images. Backup all files under wp_contents/uploads and restore it at the wp_contents/uploads directory of docker-compose.yaml location. And what I had to do was to run the following command to be able to upload files from WordPress UI.

sudo chmod 777 wp_contents

This may be too open. I may tighten it up a little more later.

I put too much information alraedy in this blog post. I will write about Dockerizing WordPress more as I have some time.

Author: admin

A software engineer in greater Seattle area

Leave a Reply

Your email address will not be published. Required fields are marked *