For the past few days, I’ve been converting a site from Django to WordPress.
For various reasons, I needed to use Nginx which means avoiding the standard WordPress Docker image which comes with Apache.
Services Required
- Cloudflare – this will manage your HTTPS – turn on HTTPS in the settings in Flexible mode. Cloudflare’s flexible ssl means the connection between Coudflare and your server is always over http.
- Nginx – you’re going to install Nginx from the latest image and copy in a standard Nginx config file from a build directory. The configuration should have the site listening on port 80. Cloudflare will manage the HTTPS for you.
- WordPress FPM – this image uses the FastCGI implementation of PHP and in benchmarks is quicker than the standard Apache and PHP bundle.
- Database – use whatever database you prefer – I went with MariaDB – this configuration is outside this guide – there are hundreds of articles on how to get this set up.
I was hosting a few different services and had a single docker-compose.yml file for the services.
Nginx
nginx:
container_name: nginx
build:
context: nginx/
dockerfile: Dockerfile
restart: always
ports:
- "80:80"
- "443:443"
depends_on:
- wordpress-fpm
networks:
- nginx_nw
volumes:
- static_volume:/site/static
- wp_data:/var/www/html
It was important for me that I used the latest nginx image to keep onto of any vulnerabilities. You’ll note that I have a custom Dockerfile
Nginx Dockerfile
FROM nginx:latest
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
The above simple Dockerfile copies the nginx.conf file to the /etc/nginx folder of the Docker container to host the site.
Nginx Config
I ran into a lot of problems with the Nginx configuration, particularly with the encryption and HTTPs. The complexing factor here was the Cloudflare encryption – I was getting very confused about where the encryption was ending and which ports should have been opened.
The most important factor I found was configuring nginx to listen on port 80, not 443. This will allow Cloudflare to handle the HTTPS and avoids having the complexity of Ngnix/Letsencrypt handling the SSL and Certificates.
I ended up with a configuration similar to the below:
server {
listen 80 http2;
server_name site.com;
index index.php index.html index.htm;
root /var/www/html;
location / {
try_files $uri $uri/ /index.php?$args;
}
# pass the PHP scripts to FastCGI server listening on wordpress-fpm:9000
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass wordpress-fpm:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
}
Note that the PHP file requests are passed off to the WordPress-FPM image, which is listening on port 9000. Let’s take a look at that now..
WordPress Docker Compose
There are two types of docker images for WordPress – the latest and default uses the php base image and installs Apache and WordPress into the image – therefore, you only need a database to get a site up and running. I didn’t want to use Nginx as a reverse proxy for Apache, so chose the second image, WordPress FPM.
WordPress FPM uses the FastCGI implementation for PHP and does not come bundled with a web server. According to some reports I read, WordPressFPM is faster than using the standard image.
WordPress FPM Docker Compose
wordpress-fpm:
container_name: wordoress-fpm
build:
context: build/
dockerfile: Dockerfile
volumes:
- wp_data:/var/www/html
- ./uploads.ini:/usr/local/etc/php/conf.d/uploads.ini
restart: always
environment:
- WORDPRESS_DB_HOST=
- WORDPRESS_DB_USER=
- WORDPRESS_DB_PASSWORD=
- WORDPRESS_DB_NAME=
networks:
- nginx_nw
- web_nw
expose:
- 9000
You’ll see from this configuration, we’re also using a Dockerfile for some settings. This Dockerfile is optional, but I used it to install the PHP-Redis extension. A future blog post will cover the use of Redis for caching.
FROM wordpress:fpm
RUN pecl install redis && docker-php-ext-enable redis
You’ll see the code above installs redid and the php extension required to get redis caching enabled. More to come on this.