I’m not a very big fan of WordPress. In my honest opinion main problem (and benefit?) is that WordPress has many plugins.It is like two sided sword where on one side you have plugins for almost everything and on other side is quality of those plugins. Some plugin are good supported by their creators to ensure copatibility with latest version of Wordpress and sometimes just one plugin on your page blocking you from upgrade to a new version because the developer not updating it anymore.
Even worse is if your super-cool theme which you bought is using one if this plugins (besides another 20 which are needed to ensure your theme to run). Maybe you dear reader is one of those creators who using many plugins in you theme, I want to give u an advice to pay more attention which plugin you use as dependency in your theme.
Ok back to configuring Wordpress on docker. After we end, your server will run everything bellow with just one command.
- Deploy wordpress site
- Deploy and configure databse
- Generate SSL certificate.
What you will need? (Prerequisites)
- Ovn server (or VPS) with installed docker & docker-compose.
- Own domain (preferable hosted on Cloudflare)
- Configured DNS server which point address to your server
If you want host from home you have to have static external IP address, and properly setup NAT on your router. Another way but more advanced is create VPS with some cloud provider and create VPN network, then you can forward requests to your server from proxy to home server over VPN. Ok Back to docker…
If you dont have installed docker and/or docker-compose you can find it Here for Docker and here is docker-compose.
At first Networks:#
Let’s start by creating docker-compose file:
version: "3.9"
networks:
frontend:
external: true
wordpress_backend:
internal: true
We will have two networks:
- frontend: this will be used to comunicate from internet with our application
- wordpress_backend: network for communicate between wprdpress, db and nginx… It is not awailable from internet.
External networks are not created automaticaly with docker compose so create it manually:
docker network create frontend
Services#
version: "3.9"
networks:
frontend:
external: true
wordpress_backend:
internal: true
services:
# here goes services
Traefik#
At first, add Traefik service. This is our reverse proxy. It allows connecting to our app, upgrading HTTP to HTTPS and it can request SSL certificates for services behind him.
wordpress_traefik:
image: traefik
command:
--configFile=/traefik.yml
restart: unless-stopped
networks:
- frontend
- wordpress_backend
ports:
- "80:80"
- "443:443"
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.yml:/traefik.yml:ro
- ./tls_config.yml:/tls_config.yml:ro
- ./letsencrypt:/letsencrypt
environment:
- CF_API_EMAIL=<your-email>
- CF_API_KEY=<your-cloudflare-api-key>
I’m using DNS challenge to renew and generate certificates but there are more Supported provider.
Im using cloudflare for most of my domains, so if you want to configure traefik to use it as your DNS provider addd following code to your traefik.yml
file
# traefik.yml
# ...
certificatesresolvers:
le:
acme:
httpchallenge:
entrypoint: "web"
emaiL: "<change to your email>"
storage: "/letsencrypt/acme.json"
dnschallenge:
provider: "cloudflare"
# ...
Another important configuration is to disable unsupported versions of TLS and SSL. By default traefik has enabling to use each version of SSL and TLS. This preset can be changed by following code.
# tls_config.yml
tls:
options:
mytls:
sniStrict: true
minVersion: VersionTLS12
cipherSuites:
- TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
- TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
curvePreferences:
- CurveP521
- CurveP384
mintls13:
minVersion: VersionTLS13
With this config you can get “A+” rank for your server with SSL Labs test
Wordpress Web#
I using an FPM image for WordPress and as server I’m using nginx. First of all add another service configuration for your NGINX to your docker-compose.yml
file as follows
wordpress_proxy:
image: nginx:alpine
restart: unless-stopped
depends_on:
- wordpress_app
- wordpress_db
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
volumes_from:
- wordpress_app
- wordpress_traefik
networks:
- wordpress_backend
Next I need to tell Traefik some more about services I have in my docker-compose file (where can it find my server and how to connect to it). This can be ensured by adding labels to each service which have to be accessible from internet trough Traefik proxy. Here is example for my NGINX service.
# ... paste this after network from example above
labels:
- "traefik.enable=true"
- "traefik.http.routers.wordpress.entrypoints=web,websecure"
- "traefik.http.routers.wordpress.rule=Host(`<change to your web address>`)"
- "traefik.http.routers.wordpress.tls.certresolver=le"
- "traefik.http.routers.wordpress.tls.options=mytls@file"
- "traefik.http.routers.wordpress.service=wordpress"
- "traefik.http.services.wordpress.loadbalancer.server.port=80"
Nginx is used as HTTP server for PHP-FPM image which contains my Wordpress website. But before this you have to configure it. Create nginx.conf
file and add there following code.
# nginx.conf
user nginx;
events {
worker_connections 768;
}
http {
upstream backend {
server wordpress_app:9000;
}
include /etc/nginx/mime.types;
default_type application/octet-stream;
gzip on;
gzip_disable "msie6";
server {
listen 80;
root /var/www/html/;
index index.php index.html index.htm;
location / {
# try_files $uri $uri/ =404;
try_files $uri $uri/ /index.php?$args;
}
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
location = /favicon.ico {
log_not_found off;
access_log off;
}
location ~* ^.+\.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
access_log off; log_not_found off; expires max;
}
location ~ [^/]\.php(/|$) {
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
if (!-f $document_root$fastcgi_script_name) {
return 404;
}
# This is a robust solution for path info security issue and works with "cgi.fix_pathinfo = 1" in /etc/php.ini (default)
include fastcgi_params;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PHP_VALUE "upload_max_filesize=64m
post_max_size=64m";
fastcgi_pass wordpress_app:9000;
}
}
}
Wordpress#
Add WordPress service. I am using the FPM version. WordPress will need to access to the internet so add it to both networks frontend
for internet access and wordpress_backend
for the ability to connect to the database. We don’t need traefik here so disable it by add - "traefik.enable=false"
to labels.
wordpress_app:
image: wordpress:5-php8.0-fpm-alpine # wordpress:5-fpm-alpine
restart: unless-stopped
depends_on:
- wordpress_db
networks:
- wordpress_backend
- frontend
environment:
WORDPRESS_DB_HOST: wordpress_db
WORDPRESS_DB_USER: exampleuser
WORDPRESS_DB_PASSWORD: examplepass
WORDPRESS_DB_NAME: exampledb
volumes:
- wordpress:/var/www/html
labels:
- "traefik.enable=false"
Database#
WordPress needs to MySQL database to run. So add it (MariaDB is also supported). Environment Variables must correspond with the WordPress DB variable. WordPress will create all database tables at the first run.
wordpress_db:
image: mysql:8.0.21
restart: unless-stopped
command: --default-authentication-plugin=mysql_native_password
networks:
- wordpress_backend
environment:
MYSQL_DATABASE: exampledb
MYSQL_USER: exampleuser
MYSQL_PASSWORD: examplepass
MYSQL_RANDOM_ROOT_PASSWORD: '1'
volumes:
- db:/var/lib/mysql
Adminer#
This is mostly for development when you need to look inside the database.
adminer:
image: adminer
depends_on:
- wordpress_db
networks:
- frontend
- wordpress_backend
restart: always
Volumes#
Volumes to store your data. You can map host folders instead of volumes, then you don’t need the following lines of code. Your docker-compose file wi have the following structure.
version: "3.9"
networks:
# your networks
services:
# your services
# Add following content at the end
volumes:
wordpress:
driver: local
db:
driver: local
Folder sturcture#
Your folder has to contain the following files
docker-compose.yml
nginx.conf
tls_config.yml
traefik.yml
Start it#
Ok let’s start our Wordpress
docker-compose up -d
And navigate to your address. WordPress will ask you to create a new admin user and password.
Delete it#
If you need to delete it you can to do this with
docker-compose down -v
But warning: This also delete volumes with all of its data In production, I recommend using host folders mapping instead of volumes which docker-compose down -v
not deleting.
Updating#
To update the container you can run
docker-compose pull
docker-compose up -d
Which will download newer docker images and recreate containers.
Done#
And that’s your very own WordPress running in docker containers. If you have more questions you can contact me over email, my Mastodon or Matrix account.
Reply by Email