Technology Encyclopedia Home >How to Deploy WordPress with Docker Compose — A Containerized Setup

How to Deploy WordPress with Docker Compose — A Containerized Setup

I've deployed WordPress the traditional way many times: install PHP, install MySQL, configure Apache or Nginx, upload files, fight with permissions. It works, but it's fiddly and the setup is hard to replicate exactly.

Docker Compose changed how I think about WordPress deployments. The entire stack — WordPress, MySQL, Nginx — lives in a docker-compose.yml file. Spinning it up on a new server is one command. Migrating to a different server means copying one file and the data volume. Updating WordPress means pulling a new image.

The trade-off is understanding Docker basics before you start. If you're already using Docker for other things, this approach makes WordPress deployment dramatically cleaner.

This guide deploys WordPress with Docker Compose on Ubuntu 22.04, with Nginx as the reverse proxy and HTTPS.

I run this on Tencent Cloud Lighthouse. Docker Compose makes WordPress deployments portable — the same docker-compose.yml runs identically on any Ubuntu server. Worth noting: if you want WordPress running in under 10 minutes without Docker, Lighthouse offers a pre-configured WordPress application image that sets up the entire stack automatically. This Docker approach is better if you want containerization, easy migration, or plan to run other services alongside WordPress on the same server. The snapshot feature also lets you back up the entire Docker environment (containers + volumes) in one operation.


Table of Contents

  1. Docker WordPress vs Traditional LAMP Install
  2. Prerequisites
  3. Part 1 — Server Setup
  4. Part 2 — Create the Docker Compose Stack
  5. Part 3 — Configure Nginx Reverse Proxy
  6. Part 4 — Enable HTTPS
  7. Part 5 — Complete WordPress Setup
  8. Part 6 — Backups
  9. Part 7 — Multiple WordPress Sites on One Server
  10. The Gotcha: WordPress URL After HTTPS
  11. Useful Docker Compose Commands

  • Key Takeaways
  • Use the appropriate Lighthouse application image to skip manual installation steps where available
  • Lighthouse snapshots provide one-click full-server backup before major changes
  • OrcaTerm browser terminal lets you manage the server from any device
  • CBS cloud disk expansion handles growing storage needs without server migration
  • Console-level firewall + UFW = two independent protection layers

Docker WordPress vs Traditional LAMP Install {#docker-vs-lamp}

Docker Compose Traditional LAMP
Setup time 5 minutes 20–30 minutes
Migration Copy one directory + restore DB Complex multi-step
Isolation Complete container isolation Shared system PHP/MySQL
Multiple sites Separate stacks, no conflicts PHP version conflicts possible
Updates docker compose pull && up -d apt upgrade + plugin updates
Resource usage Slightly higher overhead Slightly more efficient

Docker Compose is particularly useful when you're running multiple WordPress sites on the same server — each gets its own isolated stack.


Prerequisites {#prerequisites}

Requirement Notes
Cloud server Tencent Cloud Lighthouse Ubuntu 22.04
Docker + Compose We'll install these
Nginx For reverse proxy and SSL
A domain name For HTTPS

Part 1 — Server Setup {#part-1}

ssh ubuntu@YOUR_SERVER_IP
sudo apt update && sudo apt upgrade -y

# Install Docker
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER
newgrp docker

sudo apt install -y nginx
sudo ufw allow ssh
sudo ufw allow 'Nginx Full'
sudo ufw enable

Part 2 — Create the Docker Compose Stack {#part-2}

mkdir -p ~/apps/myblog && cd ~/apps/myblog

Create .env:

nano .env
MYSQL_ROOT_PASSWORD=generate_strong_root_password
MYSQL_DATABASE=wordpress
MYSQL_USER=wordpress
MYSQL_PASSWORD=generate_strong_wp_password

# WordPress table prefix (change for security)
WORDPRESS_TABLE_PREFIX=wp_

# WordPress admin settings (used during auto-install if configured)
WORDPRESS_SITE_URL=https://yourdomain.com
chmod 600 .env

Create docker-compose.yml:

version: '3.8'

services:
  wordpress-db:
    image: mysql:8.0
    container_name: myblog-db
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      - wordpress_db:/var/lib/mysql
    command: '--default-authentication-plugin=mysql_native_password'

  wordpress:
    image: wordpress:php8.2-fpm-alpine
    container_name: myblog-wp
    restart: unless-stopped
    environment:
      WORDPRESS_DB_HOST: wordpress-db
      WORDPRESS_DB_NAME: ${MYSQL_DATABASE}
      WORDPRESS_DB_USER: ${MYSQL_USER}
      WORDPRESS_DB_PASSWORD: ${MYSQL_PASSWORD}
      WORDPRESS_TABLE_PREFIX: ${WORDPRESS_TABLE_PREFIX}
    volumes:
      - wordpress_files:/var/www/html
      - ./php-custom.ini:/usr/local/etc/php/conf.d/custom.ini:ro
    depends_on:
      - wordpress-db

volumes:
  wordpress_db:
  wordpress_files:

Create a PHP config override:

cat > php-custom.ini << 'EOF'
upload_max_filesize = 64M
post_max_size = 64M
memory_limit = 256M
max_execution_time = 300
EOF

Start the stack:

docker compose up -d
docker compose logs -f wordpress
# Wait for "WordPress not found in /var/www/html — copying now..."

Part 3 — Configure Nginx Reverse Proxy {#part-3}

This WordPress stack uses wordpress:php8.2-fpm-alpine — the PHP-FPM variant. Nginx needs to serve static files and proxy PHP requests to PHP-FPM inside the container.

First, find the WordPress files volume path:

docker volume inspect myblog_wordpress_files | grep Mountpoint
# Note the path, e.g.: /var/lib/docker/volumes/myblog_wordpress_files/_data
sudo nano /etc/nginx/sites-available/myblog
upstream wordpress_fpm {
    server 127.0.0.1:9000;
}

server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;

    root /var/lib/docker/volumes/myblog_wordpress_files/_data;
    index index.php index.html;

    client_max_body_size 64m;

    access_log /var/log/nginx/myblog_access.log;
    error_log  /var/log/nginx/myblog_error.log;

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

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_pass wordpress-container:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }

    location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff|woff2)$ {
        expires 30d;
        add_header Cache-Control "public";
    }

    location ~ /\.ht {
        deny all;
    }
}

Simpler alternative: use the wordpress:latest (Apache) image which handles PHP internally, and Nginx just proxies HTTP:

# In docker-compose.yml, change WordPress image:
wordpress:
  image: wordpress:latest    # Apache + PHP
  ports:
    - "8080:80"              # Expose port
# Nginx just proxies to port 8080
location / {
    proxy_pass http://127.0.0.1:8080;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

This is simpler and works well for most setups. The FPM approach gives better performance but requires more Nginx configuration.

sudo ln -s /etc/nginx/sites-available/myblog /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

Part 4 — Enable HTTPS {#part-4}

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

Part 5 — Complete WordPress Setup {#part-5}

Visit http://yourdomain.com. The WordPress installation wizard appears:

  1. Select language
  2. Enter site title, admin username, password, email
  3. Click Install WordPress

Log in to the admin at https://yourdomain.com/wp-admin.


Part 6 — Backups {#part-6}

nano ~/backup_wordpress.sh
#!/bin/bash
BACKUP_DIR=~/backups/wordpress
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR

source ~/apps/myblog/.env

# Database backup
docker exec myblog-db mysqldump \
  -u root -p"$MYSQL_ROOT_PASSWORD" wordpress | \
  gzip > $BACKUP_DIR/wp_db_$DATE.sql.gz

# WordPress files backup
docker run --rm \
  -v myblog_wordpress_files:/data \
  -v $BACKUP_DIR:/backup \
  alpine tar czf /backup/wp_files_$DATE.tar.gz -C /data .

find $BACKUP_DIR -mtime +7 -delete
echo "WordPress Docker backup complete: $DATE"
chmod +x ~/backup_wordpress.sh
(crontab -l; echo "0 3 * * * ~/backup_wordpress.sh") | crontab -

Part 7 — Multiple WordPress Sites on One Server {#part-7}

Each site gets its own docker-compose.yml in a separate directory, with unique port numbers:

# Site 1: myblog (port 8081)
~/apps/myblog/docker-compose.yml  → ports: "8081:80"

# Site 2: anotherblog (port 8082)
~/apps/anotherblog/docker-compose.yml → ports: "8082:80"

Each has its own Nginx server block pointing to its port. No PHP version conflicts, no database conflicts, completely isolated.


The Gotcha: WordPress URL After HTTPS {#gotcha}

After enabling HTTPS with Certbot, WordPress may redirect to http:// or show mixed content warnings. This happens because WordPress stores the site URL in the database.

Fix via WP-CLI inside the container:

docker exec myblog-wp wp option update siteurl https://yourdomain.com --allow-root
docker exec myblog-wp wp option update home https://yourdomain.com --allow-root

Or update via wp-config.php (add before require_once):

define('WP_SITEURL', 'https://yourdomain.com');
define('WP_HOME', 'https://yourdomain.com');

To access wp-config.php in the Docker volume:

docker exec -it myblog-wp bash
nano /var/www/html/wp-config.php

Useful Docker Compose Commands {#commands}

# From ~/apps/myblog directory

# Start/stop/restart
docker compose up -d
docker compose down
docker compose restart

# View logs
docker compose logs -f
docker compose logs -f wordpress

# Update WordPress image
docker compose pull
docker compose up -d --remove-orphans

# WordPress CLI
docker exec myblog-wp wp plugin list --allow-root
docker exec myblog-wp wp plugin update --all --allow-root
docker exec myblog-wp wp core update --allow-root

# Database shell
docker exec -it myblog-db mysql -u wordpress -p wordpress

Troubleshooting {#troubleshooting}

Issue Likely Cause Fix
Connection refused Service not running or wrong port Check systemctl status SERVICE and verify firewall rules
Permission denied Wrong file ownership or permissions Check file ownership with ls -la and use chown/chmod to fix
502 Bad Gateway Backend service not running Restart the backend service; check logs with journalctl -u SERVICE
SSL certificate error Certificate expired or domain mismatch Run sudo certbot renew and verify domain DNS points to server IP
Service not starting Config error or missing dependency Check logs with journalctl -u SERVICE -n 50 for specific error
Out of disk space Logs or data accumulation Run df -h to identify usage; clean logs or attach CBS storage
High memory usage Too many processes or memory leak Check with htop; consider upgrading instance plan if consistently high
Firewall blocking traffic Port not open in UFW or Lighthouse console Open port in Lighthouse console firewall AND sudo ufw allow PORT

Frequently Asked Questions {#faq}

Can I migrate an existing WordPress Docker site to a cloud server?
Yes. Export your content and database from the current host, import them on the new server, then update your DNS to point to the new IP. Update any hardcoded URLs in the database or configuration files.

How do I keep WordPress Docker updated securely?
Enable automatic minor updates where available, and manually update major versions after testing on a staging environment. Take a Lighthouse snapshot before any significant update.

Do I need a CDN for my WordPress Docker site?
For international audiences, yes. A CDN like EdgeOne caches static assets at edge nodes worldwide, improving load times for visitors far from your server region.

What causes WordPress Docker to be slow on a VPS?
Usually: missing caching plugin/configuration, unoptimized images, too many plugins, or insufficient server RAM. Start with a caching layer (Redis or built-in cache) and image optimization.

How do I secure the admin panel?
Change the default admin URL if possible, use a strong unique password, enable two-factor authentication, and consider restricting admin access by IP in your Nginx configuration.

Launch your WordPress site in Docker today:
👉 Tencent Cloud Lighthouse — Docker-ready Ubuntu VPS
👉 View current pricing and promotions
👉 Explore all active deals and offers