Programming

【Docker】Optimizing a Slow WordPress Container for Better Performance

Overview

Have you experienced sluggish performance when running WordPress in Docker on your local environment?

I encountered this issue myself when I set up a WordPress environment. When I first launched it, it took about 16 seconds to display a page. (This was unacceptable for work.)

Currently, I have optimized it to load pages in around 6 seconds, achieving a 10-second reduction in load time. (For reference, my Mac has an M3 chip, 8GB of RAM, and 512GB of storage.)

In this article, I will share the steps I took to optimize my WordPress container.

1. Revising the Container Configuration

The first major improvement came from reviewing the container structure. Initially, I used docker-compose to launch both the WordPress and MySQL containers. Below is the previous compose.yaml configuration:

services
  db:
    image: mariadb:10.5.24
    platform: linux/amd64
    container_name: wordpress_db
    volumes:
      - app-data:/var/lib/mysql
    restart: always
    expose:
      - 3306
    ports:
      - 3306:3306
    environment:
      MYSQL_ROOT_PASSWORD: <root_password>
      MYSQL_DATABASE: <database_name>
      MYSQL_USER: <user_name>
      MYSQL_PASSWORD: <user_password>
    networks:
      - app-net

  wordpress:
    depends_on:
      - db
    image: wordpress:6.7.1-php8.1-apache
    container_name: wordpress_app
    volumes:
      - type: bind
        source: "./"
        target: "/var/www/html"
      - ./php.ini-development:/usr/local/etc/php/php.ini
    ports:
      - 8000:80
    restart: always
    environment:
      WORDPRESS_DB_HOST: wordpress_db:3306
      WORDPRESS_DB_USER: <user_user>
      WORDPRESS_DB_PASSWORD: <user_password>
      WORDPRESS_DEBUG: true
    networks:
      - app-net

volumes:
    app-data:
networks:
  app-net:
    driver: bridge
Switching to an Nginx and PHP-FPM Setup

In the new compose.yaml, I replaced the Apache image in the WordPress container with an Nginx and PHP-FPM setup. The updated configuration is as follows:

services:
  db:
    image: mariadb:10.5.24
    platform: linux/amd64
    container_name: minguru_db
    volumes:
      - db_data:/var/lib/mysql
    restart: always
    expose:
      - 3306
    ports:
      - 3306:3306
    environment:
      MYSQL_ROOT_PASSWORD: <root_password>
      MYSQL_DATABASE: <database_name>
      MYSQL_USER: <user_name>
      MYSQL_PASSWORD: <user_password>
    networks:
      - app-net

  wordpress:
    depends_on:
      - db
    image: wordpress:6.7.1-php8.1-fpm-alpine
    container_name: wordpress_db
    volumes:
      - ./:/var/www/html
    restart: always
    environment:
      WORDPRESS_DB_HOST: wordpress_db:3306
      WORDPRESS_DB_USER: <user_user>
      WORDPRESS_DB_PASSWORD: <user_password>
      WORDPRESS_DEBUG: true
    networks:
      - app-net

  nginx:
    image: nginx:1.27.3-alpine
    container_name: minguru_nginx
    restart: unless-stopped
    networks:
      - app-net
    depends_on:
      - wordpress
    ports:
      - "8000:80"
    volumes:
      - ./:/var/www/html
      - ./nginx.conf:/etc/nginx/conf.d/default.conf

volumes:
    db_data:
networks:
  app-net:
    driver: bridge

Below is the nginx.conf file used in this setup:

# default.conf
server {
    listen 80;
    listen [::]:80;
    server_name $host;
    index index.php index.html index.htm;
    root /var/www/html;
    server_tokens off;

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass wordpress_app:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }

    location ~ /\.ht {
        deny all;
    }

    location = /favicon.ico {
        log_not_found off; # access_log off;
    }

    location = /favicon.svg {
        log_not_found off; # access_log off;
    }

    location = /robots.txt {
        log_not_found off; # access_log off# allow all;
    }

    location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ {
        expires max;
        log_not_found off;
    }
}

By making these changes, the page load time improved from 16 seconds to 12 seconds, a 4-second improvement! However, it was still too slow.

2. Switching to OrbStack

I had been using Docker Desktop without major issues, but I noticed several articles mentioning that OrbStack is a more lightweight and faster alternative. So, I decided to make the switch.

OrbStack - Official Site

After switching to OrbStack, the page load time dropped from 12 seconds to just 6 seconds—a significant improvement!
I found 6 seconds to be an acceptable load time for my environment, so I decided to stop optimizing further for now (also, I was short on time).

Other Experiments

1. Disabling PHP File Binding

In the compose.yaml, the WordPress container was configured to bind PHP files via volume mount. I tested running the container without binding PHP files, copying them directly into the container instead. However, there was no noticeable improvement in page load time.

2. Adjusting Docker Desktop Resource Settings

I also tried increasing the memory limit and other resource settings in Docker Desktop, but this had no impact on performance.

Perhaps on a higher-spec machine, these settings would have more effect, but in my case, there was no improvement.

Conclusion

The key optimizations that led to improved WordPress performance were:

  • Switching from Apache to Nginx + PHP-FPM (Reduced load time from 16s → 12s)
  • Replacing Docker Desktop with OrbStack (Further reduced load time from 12s → 6s)

For anyone experiencing sluggish performance with WordPress in Docker, I highly recommend trying OrbStack and revising the container structure!

Sponsored Link

  • Author
プロフィール画像

kaz

Full-stack Engineer specializing in Backend/Frontend/Cloud Infrastructure | Digital Nomad since June 2023, traveling the world | Sharing programming tips and insights | Posting travel updates on X

-Programming
-,