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.ymlruns 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.
- Key Takeaways
| 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.
| 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 |
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
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..."
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
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
Visit http://yourdomain.com. The WordPress installation wizard appears:
Log in to the admin at https://yourdomain.com/wp-admin.
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 -
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.
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
# 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
| 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 |
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.
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