GRMBL

In my mind, in the Castle where I am the King

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. 

'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

Share this post

Leave a Reply