WordPress and Docker

As I continue exploring my homelab, I’m discovering more fascinating projects that I’ve always wanted to explore. One project in particular that caught my interest is deploying WordPress within a Docker environment, especially leveraging Traefik’s capabilities as a reverse proxy.

I’ve always admired WordPress for its open-source foundation, strong community support, and vast selection of themes and plugins. Previously, I deployed WordPress using the traditional approach, setting it up on a single server with Apache. While this method worked fine, I knew there had to be a better, more efficient way.

Having recently gained more experience with Docker, I realised how straightforward and powerful deploying WordPress containers can be. Docker simplifies setup, making it quick to get a functional WordPress site running. Coupled with Traefik, which handles the reverse proxy tasks, I can easily expose WordPress securely and automatically obtain free SSL certificates from Let’s Encrypt.

Using Docker and Traefik together streamlines the deployment process and enhances scalability, security, and maintainability. Now, I can quickly experiment with various themes, plugins, and configurations without worrying about impacting my main environment. It’s exciting to see how much flexibility and control this setup provides compared to the traditional method.

Shifting to a Docker-based WordPress deployment with Traefik has significantly improved my homelab experience, opening up numerous possibilities for exploration, learning, and experimentation.

Deploying WordPress with Docker and Traefik

Previously, I deployed Traefik as a reverse proxy on my Docker instance to handle routing for various services hosted on the same Docker environment. This Traefik deployment served as the foundational infrastructure for my current WordPress project.

Security and Environment Management

For optimal security practices, I leveraged Docker environment files (.env) to securely store sensitive configuration details, including credentials for both the WordPress application and the MySQL database service. By externalising sensitive information into environment files, I ensured that no sensitive data was directly embedded within Docker Compose files, greatly enhancing security.

Network and Routing Configuration

To enable seamless routing by Traefik, I ensured that the WordPress container was connected to the same Docker network as Traefik, named proxy. Within the Docker Compose file, I explicitly configured labels for Traefik, specifying the domain under which my WordPress site would be available.

Database Management and phpMyAdmin

Since migrating a legacy WordPress database was necessary, I also included a phpMyAdmin container for convenient database management. Given the inherent security risks of exposing database management tools directly to the internet, I deliberately avoided applying Traefik labels to the phpMyAdmin container. Although it remained within the proxy network for internal communication with the MySQL service, I exposed it only locally on port 9078. This careful approach ensured secure, isolated database management, limiting access exclusively to the host machine.

SSL Implementation with Let’s Encrypt

This project successfully achieved secure and encrypted connections by integrating SSL certificates from Let’s Encrypt, ensuring safe access to the WordPress site.

#Defining a service
services:
  wordpress:
    depends_on:
      - db
    image: wordpress
    restart: always
    security_opt:
      - no-new-privileges:true # helps to increase security
    env_file:
      - .env # store other secrets e.g., dashboard password
    # Define a network
    networks:
      - proxy
    # Environment variables for wordpress
    environment:
      WORDPRESS_DB_HOST: ${WORDPRESS_DB_HOST}
      WORDPRESS_DB_USER: ${WORDPRESS_DB_USER}
      WORDPRESS_DB_PASSWORD: ${WORDPRESS_DB_PASSWORD}
      WORDPRESS_DB_NAME: ${WORDPRESS_DB_NAME}
    #Defining volumes
    volumes:
      - ./:/var/www/html
      
    #Labels for traefik
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.blog.entrypoints=http"
      - "traefik.http.routers.blog.rule=Host(`blog.sanjumathew.uk`)"
      - "traefik.http.middlewares.blog-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.routers.blog.middlewares=blog-https-redirect"
      - "traefik.http.routers.blog-secure.entrypoints=https"
      - "traefik.http.routers.blog-secure.rule=Host(`blog.sanjumathew.uk`)"
      - "traefik.http.routers.blog-secure.tls=true"
      - "traefik.http.routers.blog-secure.service=blog"
      - "traefik.http.services.blog.loadbalancer.server.port=80"
      - "traefik.docker.network=proxy"
  #Phpmyadmin service
  phpmyadmin:
    depends_on:
      - db
    image: phpmyadmin
    restart: always
    security_opt:
      - no-new-privileges:true # helps to increase security
    env_file:
      - .env # store other secrets e.g., dashboard password
    # Environment variables for phpmyadmin
    environment:
       A_HOST: ${PMA_HOST}
       UPLOAD_LIMIT: ${UPLOAD_LIMIT}
    # Define a network
    networks:
      - proxy
    #Define the port for phpmyadmin
    ports:
      - 9078:80

  #Mysql service
  db:
    image: mysql:8.0
    restart: always
    # Environment variables for Mysql
    environment:
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    #Defining volumes
    volumes:
      - ./db:/var/lib/mysql
    # Define a network
    networks:
      - proxy
    security_opt:
      - no-new-privileges:true # helps to increase security
    env_file:
      - .env # store other secrets e.g., dashboard password

networks:
  proxy:
    external: true
volumes:
  db:
Back to top arrow