π How to Run Wordpress in Docker
I'm not a very big fan of WordPress. IMO main problem (and benefit?) is that WordPress has many plugins. So you can find plugins for almost everything. But here are plugins in different quality and sometimes just one plugin on your page blocking you from upgrade to a new version because the developer not updating it anymore. On the other side is easy to set up a new page and start posting. You need to upload the page over FTP, create the database and run the installer. (I did this procedure very long ago.)
Nowadays when you can use composer to manage dependencies and deploy PHP apps with CI/CD it's a step back IMO... Maybe it's because I'm lazy but even with WordPress you can forget about uploading sites over FTP, and you can use Docker Image to deploy the page.
So with docker you can do just with one command:
- Deploy wordpress site
- Deploy and configure databse
- Generate SSL certificate.
What you will need?
- Ovn server (or VPS) with installed docker & docker-compose.
- Own web address
- 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.
Configure traefik:
# traefik.yml
api:
dashboard: true
insecure: true
log:
level: "DEBUG"
providers:
docker:
exposedbydefault: false
file:
filename: "/tls_config.yml"
watch: true
entrypoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"
certificatesresolvers:
le:
acme:
httpchallenge:
entrypoint: "web"
emaiL: "<change to your email>"
storage: "/letsencrypt/acme.json"
dnschallenge:
provider: "cloudflare"
accesslog: true
serversTransport:
insecureSkipVerify: true
Disable TLSv1: by default traefik has enabled it.
# 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 ged "A+" rank for your server with SSL Labs test
Wordpress Web
I using an FPM image for WordPress so I need NGINX. Add to your services, and configure traefik with labels.
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
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"
And configure Nginx:
# 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.