I’m going to assume you have some experience with Docker and so will be glossing over alot of stuff.

First Up, Traefik

Traefik in this setup is being used as a reverse proxy which allows us to run multiple services on our server without having to expose a ton of ports, and it handles things like ssl and domain configuration for our services.

Create a new docker-compose.yml file

version: '3.8'

services:
 traefik:
   image: traefik:latest
   restart: always
   ports:
     - 80:80
     - 443:443
   command:
     - --accesslog
     - --log.level=INFO
     - --api.dashboard=true
     - --api.insecure=true
     - --providers.docker=true
     - --providers.docker.exposedbydefault=false
     - --entrypoints.web.address=:80
     - --entrypoints.web-secure.address=:443
     #- --certificatesresolvers.letsencrypt.acme.tlschallenge=true
     #- --certificatesresolvers.letsencrypt.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
     #- [email protected]
     #- --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
   volumes:
     - /var/run/docker.sock:/var/run/docker.sock
     - traefik:/letsencrypt
   labels:
     - traefik.enable=true
     - traefik.http.routers.api.rule=Host(`${TRAEFIK_URL}`)
     - traefik.http.routers.api.entrypoints=web
     - traefik.http.routers.api.service=api@internal
     #- traefik.http.services.traefik.loadbalancer.server.port=8080
     #- traefik.http.routers.api.entrypoints=web-secure
     #- traefik.http.routers.api.tls=true
     #- traefik.http.routers.api.tls.certresolver=letsencrypt

Next up WordPress

WordPress - Blogging platform

However I have added the additional configs, that I tent to add to a site to boost its performance, and I often test dev sites with these settings enabled so I know how a theme or plugin behaves before going public.

In the docker-compose.yml file add

wordpress:
    image: wordpress:latest
    restart: always
    depends_on:
      - mariadb
      - redis
    expose:
      - 80
      - 443
    environment:
      WORDPRESS_DB_HOST: mariadb
      WORDPRESS_DB_NAME: ${WP_DB_NAME}
      WORDPRESS_DB_USER: ${WP_DB_USER}
      WORDPRESS_DB_PASSWORD: ${WP_DB_PASSWORD}
      WORDPRESS_TABLE_PREFIX: ${WP_DB_PREFIX}
      WORDPRESS_CONFIG_EXTRA:
        define( 'WP_HOME', 'http://${WP_WWW_URL}/' );
        define( 'WP_SITEURL', 'http://${WP_WWW_URL}/' );
        define( 'WP_REDIS_HOST', 'redis' );
        define( 'WP_REDIS_PORT', 6379 );
        define( 'COMPRESS_CSS', true );
        define( 'COMPRESS_SCRIPTS', true );
        define( 'CONCATENATE_SCRIPTS', true );
        define( 'ENFORCE_GZIP', true );
        define( 'AUTOSAVE_INTERVAL', 120 );
        define( 'WP_POST_REVISIONS', 10);
        define( 'EMPTY_TRASH_DAYS', 30 );
        define( 'IMAGE_EDIT_OVERWRITE', true );
        define( 'DISALLOW_FILE_EDIT', true );
    volumes:
      - wordpress:/var/www/html
      - ./wordpress/wp-content:/var/www/html/wp-content
      - ./wordpress.ini:/usr/local/etc/php/conf.d/wordpress.ini
    labels:
      - traefik.enable=true
      - traefik.http.routers.wordpress.rule=Host(`${WP_URL}`, `${WP_WWW_URL}`)
      - traefik.http.routers.wordpress.entrypoints=web
      #- traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https
      #- traefik.http.routers.wordpress.middlewares=redirect-to-https@docker
      #- traefik.http.routers.wordpress-secure.rule=Host(`${WP_URL}`, `${WP_WWW_URL}`)
      #- traefik.http.routers.wordpress-secure.entrypoints=web-secure
      #- traefik.http.routers.wordpress-secure.tls.certresolver=letsencrypt
expose:
  - 80
  - 443

This is refering to the default internal port of the docker image and is not publicly accessible, in this use case it mainly serves to document the port being used.

volumes:
  - wordpress:/var/www/html
  - ./wordpress/wp-content:/var/www/html/wp-content
  - ./wordpress.ini:/usr/local/etc/php/conf.d/wordpress.ini

Volumes are where your wordpress content is stored. ./ Denotes a local folder on the machine, in this case wp-content

The first declaration is for the entire wordpress install.

Last line allows for custom php settings to be loaded.

labels:
  - traefik.enable=true
  - traefik.http.routers.wordpress.rule=Host(`${WP_URL}`, `${WP_WWW_URL}`)
  - traefik.http.routers.wordpress.entrypoints=web
  #- traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https
  #- traefik.http.routers.wordpress.middlewares=redirect-to-https@docker
  #- traefik.http.routers.wordpress-secure.rule=Host(`${WP_URL}`, `${WP_WWW_URL}`)
  #- traefik.http.routers.wordpress-secure.entrypoints=web-secure
  #- traefik.http.routers.wordpress-secure.tls.certresolver=letsencrypt

labels pass information about the service to traefik so that it can be proxied, services that do not specify a label arent proxied by traefik. The parts that are commented out are for https.

In the same folder as the docker-compose.yml file, create a new file called wordpress.ini and paste the following

file_uploads = On
upload_max_filesize = 128M
post_max_size = 128M
memory_limit = 256M
max_execution_time = 300

Next up MYSQL and Adminer

WordPress can’t function without a database, so lets set that up. Adminer is effectively the same as phpmyadmin, just much lighterweight.

So in the docker-compose.yml file add

mariadb:
    image: mariadb:latest
    restart: always
    expose:
      - 3306
    environment:
      MYSQL_DATABASE: ${WP_DB_NAME}
      MYSQL_USER: ${WP_DB_USER}
      MYSQL_PASSWORD: ${WP_DB_PASSWORD}
      MYSQL_RANDOM_ROOT_PASSWORD: '1'
    volumes:
      - mariadb:/var/lib/mysql

  adminer:
    image: adminer:latest
    restart: always
    depends_on:
      - mariadb
    expose:
      - 8080
    labels:
      - traefik.enable=true
      - traefik.http.routers.adminer.rule=Host(`${ADMINER_URL}`)
      - traefik.http.routers.adminer.entrypoints=web
      #- traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https
      #- traefik.http.routers.adminer.middlewares=redirect-to-https@docker
      #- traefik.http.routers.adminer-secure.rule=Host(`${ADMINER_URL}`)
      #- traefik.http.routers.adminer-secure.entrypoints=web-secure
      #- traefik.http.routers.adminer-secure.tls.certresolver=letsencrypt

Next create a .env file and add the following

# WordPress
WP_DB_NAME=wp-dev-db
WP_DB_PREFIX=wp_
WP_DB_USER=wp-dev-user
WP_DB_PASSWORD=wp-dev-pass

# Traefik Domains
WP_URL=wpdev.test
WP_WWW_URL=www.wpdev.test
TRAEFIK_URL=traefik.wpdev.test
ADMINER_URL=adminer.wpdev.test

Finally lets add Redis

Redis is a database cache, to put it as simply as possible.

In the docker-compose.yml file add

redis:
    image: redis:latest
    restart: always
    expose:
      - 6379
    command:
      - redis-server
      - --save 60 1
      - --loglevel warning
      - --maxmemory 128mb
      - --maxmemory-policy allkeys-lru
    volumes:
      - redis:/var/lib/redis
      - redis:/data
      #- ./redis.conf:/usr/local/etc/redis/redis.conf

./redis.conf:/usr/local/etc/redis/redis.conf

If you want to customize redis beyond the commands that I have added you can load a custom config file and uncomment out this line. Ensure that the file is in the same folder as the docker-compose file.

Finally at the bottom you decalre all your enabled Volumes

volumes:
  traefik:
  wordpress:
  mariadb:
  redis:

At the end your docker-compose file should look like this

version: '3.8'

services:
  traefik:
    image: traefik:latest
    restart: always
    ports:
      - 80:80
      - 443:443
    command:
      - --accesslog
      - --log.level=INFO
      - --api.dashboard=true
      - --api.insecure=true
      - --providers.docker=true
      - --providers.docker.exposedbydefault=false
      - --entrypoints.web.address=:80
      - --entrypoints.web-secure.address=:443
      #- --certificatesresolvers.letsencrypt.acme.tlschallenge=true
      #- --certificatesresolvers.letsencrypt.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
      #- [email protected]
      #- --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - traefik:/letsencrypt
    labels:
      - traefik.enable=true
      - traefik.http.routers.api.rule=Host(`${TRAEFIK_URL}`)
      - traefik.http.routers.api.entrypoints=web
      - traefik.http.routers.api.service=api@internal
      #- traefik.http.services.traefik.loadbalancer.server.port=8080
      #- traefik.http.routers.api.entrypoints=web-secure
      #- traefik.http.routers.api.tls=true
      #- traefik.http.routers.api.tls.certresolver=letsencrypt

  wordpress:
    image: wordpress:latest
    restart: always
    depends_on:
      - mariadb
      - redis
    expose:
      - 80
      - 443
    environment:
      WORDPRESS_DB_HOST: mariadb
      WORDPRESS_DB_NAME: ${WP_DB_NAME}
      WORDPRESS_DB_USER: ${WP_DB_USER}
      WORDPRESS_DB_PASSWORD: ${WP_DB_PASSWORD}
      WORDPRESS_TABLE_PREFIX: ${WP_DB_PREFIX}
      WORDPRESS_CONFIG_EXTRA:
        define( 'WP_HOME', 'http://${WP_WWW_URL}/' );
        define( 'WP_SITEURL', 'http://${WP_WWW_URL}/' );
        define( 'WP_REDIS_HOST', 'redis' );
        define( 'WP_REDIS_PORT', 6379 );
        define( 'COMPRESS_CSS', true );
        define( 'COMPRESS_SCRIPTS', true );
        define( 'CONCATENATE_SCRIPTS', true );
        define( 'ENFORCE_GZIP', true );
        define( 'AUTOSAVE_INTERVAL', 120 );
        define( 'WP_POST_REVISIONS', 10);
        define( 'EMPTY_TRASH_DAYS', 30 );
        define( 'IMAGE_EDIT_OVERWRITE', true );
        define( 'DISALLOW_FILE_EDIT', true );
    volumes:
      - wordpress:/var/www/html
      - ./wordpress/wp-content:/var/www/html/wp-content
      - ./wordpress.ini:/usr/local/etc/php/conf.d/wordpress.ini
    labels:
      - traefik.enable=true
      - traefik.http.routers.wordpress.rule=Host(`${WP_URL}`, `${WP_WWW_URL}`)
      - traefik.http.routers.wordpress.entrypoints=web
      #- traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https
      #- traefik.http.routers.wordpress.middlewares=redirect-to-https@docker
      #- traefik.http.routers.wordpress-secure.rule=Host(`${WP_URL}`, `${WP_WWW_URL}`)
      #- traefik.http.routers.wordpress-secure.entrypoints=web-secure
      #- traefik.http.routers.wordpress-secure.tls.certresolver=letsencrypt

  mariadb:
    image: mariadb:latest
    restart: always
    expose:
      - 3306
    environment:
      MYSQL_DATABASE: ${WP_DB_NAME}
      MYSQL_USER: ${WP_DB_USER}
      MYSQL_PASSWORD: ${WP_DB_PASSWORD}
      MYSQL_RANDOM_ROOT_PASSWORD: '1'
    volumes:
      - mariadb:/var/lib/mysql

  adminer:
    image: adminer:latest
    restart: always
    depends_on:
      - mariadb
    expose:
      - 8080
    labels:
      - traefik.enable=true
      - traefik.http.routers.adminer.rule=Host(`${ADMINER_URL}`)
      - traefik.http.routers.adminer.entrypoints=web
      #- traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https
      #- traefik.http.routers.adminer.middlewares=redirect-to-https@docker
      #- traefik.http.routers.adminer-secure.rule=Host(`${ADMINER_URL}`)
      #- traefik.http.routers.adminer-secure.entrypoints=web-secure
      #- traefik.http.routers.adminer-secure.tls.certresolver=letsencrypt

  redis:
    image: redis:latest
    restart: always
    expose:
      - 6379
    command:
      - redis-server
      - --save 60 1
      - --loglevel warning
      - --maxmemory 128mb
      - --maxmemory-policy allkeys-lru

    volumes:
      - redis:/var/lib/redis
      - redis:/data
      #- ./redis.conf:/usr/local/etc/redis/redis.conf

volumes:
  traefik:
  wordpress:
  mariadb:
  redis:

Then you go to your terminal and type

docker-compose up -d

You can now visit WordPress at the url you set, and proceed through the installation.

To get Redis working with WordPress, you need to download the Redis Object Cache plugin.

Simply goto Plugins > Add New and search for Redis Object Cache and Install. Then go to Settings > Redis > Enable Object Cache you should see

Screenshot