This WordPress site is hosted on a cloud-vm at Hetzner. The cloud-vm is configured as a docker host. Multiple websites are hosted, each running in their own docker container (some running an additional MySQL database server in a separate container). An Nginx reverse-proxy runs in its own container, taking care of the routing of the different domains to their respective websites. Certbot, also running in a separate container, takes care of the https-certificates (using Let’s Encrypt).
The whole setup is supprisingly simple, just using 2 configuration files. And apart from the docker-host itself, no additional software needs to be preinstalled.
A 'docker-compose up'
pulls in the docker images and fires up all the containers!
The docker-compose.yml
file looks as follows:
version: '3.1'
services:
proxy:
image: nginx
ports:
- 80:80
- 443:443
volumes:
- ./proxy/nginx.conf:/etc/nginx/nginx.conf:ro
- ./proxy/certs:/certs:ro
- ./certbot/www:/var/www/certbot:ro
- ./certbot/conf:/etc/nginx/ssl:ro
certbot:
image: certbot/certbot:latest
volumes:
- ./certbot/www:/var/www/certbot:rw
- ./certbot/conf:/etc/letsencrypt:rw
site1:
image: nginx
volumes:
- ./site1/html:/usr/share/nginx/html
restart: always
site2:
image: wordpress
restart: always
environment:
WORDPRESS_DB_HOST: db2
WORDPRESS_DB_USER: some-user
WORDPRESS_DB_PASSWORD: some-password
WORDPRESS_DB_NAME: some-name
volumes:
- ./site2/html:/var/www/html
db2:
image: mysql:5.7
restart: always
environment:
MYSQL_DATABASE: some-name
MYSQL_USER: some-user
MYSQL_PASSWORD: some-password
MYSQL_RANDOM_ROOT_PASSWORD: '1'
volumes:
- ./db2/db:/var/lib/mysql
site3:
image: wordpress
restart: always
environment:
WORDPRESS_DB_HOST: db3
WORDPRESS_DB_USER: some-user
WORDPRESS_DB_PASSWORD: some-password
WORDPRESS_DB_NAME: some-name
volumes:
- ./site3/html:/var/www/html
db3:
image: mysql:5.7
restart: always
environment:
MYSQL_DATABASE: some-name
MYSQL_USER: some-user
MYSQL_PASSWORD: some-password
MYSQL_RANDOM_ROOT_PASSWORD: '1'
volumes:
- ./db3/db:/var/lib/mysql
The nginx.conf
file (for the reverse-proxy) looks as follows:
user nginx;
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
keepalive_timeout 65;
#ssl_certificate /certs/cert.crt;
#ssl_certificate_key /certs/cert.key;
ssl_session_cache builtin:1000 shared:SSL:10m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
ssl_prefer_server_ciphers on;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
client_max_body_size 10M;
server {
listen 80;
server_name munnik.xyz wildhunt.nl munnik.net www.munnik.net;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name wildhunt.nl;
ssl_certificate /etc/nginx/ssl/live/wildhunt.nl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/live/wildhunt.nl/privkey.pem;
location / {
proxy_pass http://site1;
}
}
server {
listen 443 ssl;
server_name munnik.xyz;
ssl_certificate /etc/nginx/ssl/live/munnik.xyz/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/live/munnik.xyz/privkey.pem;
location / {
proxy_pass http://site2;
}
}
server {
listen 443 ssl;
server_name munnik.net www.munnik.net;
ssl_certificate /etc/nginx/ssl/live/munnik.net/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/live/munnik.net/privkey.pem;
location / {
proxy_pass http://site3;
}
}
}
To facilitate uploading of files larger than 2 MB, a small tweak is required in the docker-compose.yml
file:
site2:
image: wordpress
restart: always
environment:
WORDPRESS_DB_HOST: db2
WORDPRESS_DB_USER: some-user
WORDPRESS_DB_PASSWORD: some-password
WORDPRESS_DB_NAME: some-name
volumes:
- ./site2/html:/var/www/html
- ./site2/uploads.ini:/usr/local/etc/php/conf.d/uploads.ini:ro
This overrides the php settings in the container with the values in the upload.ini
file:
file_uploads = On
memory_limit = 500M
upload_max_filesize = 500M
post_max_size = 500M
max_execution_time = 600