How to deploy WordPress with Docker on Linux
How to Deploy WordPress with Docker on Linux
Docker has revolutionized the way we deploy and manage applications, and WordPress is no exception. This comprehensive guide will walk you through the entire process of deploying WordPress using Docker on Linux systems, from basic setup to advanced configuration and troubleshooting.
Table of Contents
1. [Introduction](#introduction)
2. [Prerequisites](#prerequisites)
3. [Understanding Docker and WordPress](#understanding-docker-and-wordpress)
4. [Installing Docker on Linux](#installing-docker-on-linux)
5. [Setting Up WordPress with Docker Compose](#setting-up-wordpress-with-docker-compose)
6. [Advanced Configuration](#advanced-configuration)
7. [Security Best Practices](#security-best-practices)
8. [Performance Optimization](#performance-optimization)
9. [Backup and Maintenance](#backup-and-maintenance)
10. [Troubleshooting Common Issues](#troubleshooting-common-issues)
11. [Best Practices and Tips](#best-practices-and-tips)
12. [Conclusion](#conclusion)
Introduction
Deploying WordPress with Docker on Linux offers numerous advantages including consistency across environments, easy scalability, simplified dependency management, and enhanced security through containerization. Whether you're a developer looking to streamline your workflow or a system administrator managing multiple WordPress installations, this guide provides everything you need to successfully deploy and maintain WordPress using Docker containers.
By the end of this tutorial, you'll have a fully functional WordPress installation running in Docker containers, complete with a MySQL database, proper networking, and security configurations. You'll also understand how to customize, scale, and troubleshoot your deployment effectively.
Prerequisites
Before beginning this tutorial, ensure you have the following:
System Requirements
- A Linux server or desktop (Ubuntu 18.04+, CentOS 7+, Debian 9+, or similar)
- Minimum 2GB RAM (4GB recommended for production)
- At least 10GB free disk space
- Root or sudo access to the system
- Stable internet connection for downloading Docker images
Technical Knowledge
- Basic understanding of Linux command line
- Familiarity with text editors (nano, vim, or similar)
- Basic networking concepts
- Understanding of web servers and databases (helpful but not required)
Software Prerequisites
- Updated Linux system with package manager access
- SSH access (if working on a remote server)
- Domain name or IP address for external access (optional)
Understanding Docker and WordPress
What is Docker?
Docker is a containerization platform that packages applications and their dependencies into lightweight, portable containers. These containers can run consistently across different environments, making deployment and scaling much simpler.
Benefits of Using Docker for WordPress
1. Isolation: Each component runs in its own container
2. Consistency: Same environment across development, testing, and production
3. Scalability: Easy to scale individual components
4. Version Control: Easy rollbacks and updates
5. Resource Efficiency: Containers share the host OS kernel
6. Security: Enhanced isolation between applications
Docker Components for WordPress
A typical WordPress Docker deployment includes:
- WordPress Container: Runs the WordPress application
- MySQL/MariaDB Container: Handles database operations
- Nginx/Apache Container: Web server (optional, as WordPress container includes Apache)
- Volume Storage: Persistent data storage
- Network: Container communication
Installing Docker on Linux
Installing Docker on Ubuntu/Debian
First, update your package index and install required packages:
```bash
sudo apt update
sudo apt install apt-transport-https ca-certificates curl gnupg lsb-release
```
Add Docker's official GPG key:
```bash
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
```
Add the Docker repository:
```bash
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
```
Install Docker Engine:
```bash
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io
```
Installing Docker on CentOS/RHEL
Install required packages:
```bash
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
```
Install Docker:
```bash
sudo yum install docker-ce docker-ce-cli containerd.io
```
Post-Installation Steps
Start and enable Docker service:
```bash
sudo systemctl start docker
sudo systemctl enable docker
```
Add your user to the docker group (optional, for running Docker without sudo):
```bash
sudo usermod -aG docker $USER
```
Note: Log out and back in for group changes to take effect.
Verify Docker installation:
```bash
docker --version
docker run hello-world
```
Installing Docker Compose
Docker Compose simplifies multi-container deployments. Install it using:
```bash
sudo curl -L "https://github.com/docker/compose/releases/download/v2.20.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
```
Verify installation:
```bash
docker-compose --version
```
Setting Up WordPress with Docker Compose
Creating the Project Directory
Create a dedicated directory for your WordPress project:
```bash
mkdir ~/wordpress-docker
cd ~/wordpress-docker
```
Basic Docker Compose Configuration
Create a `docker-compose.yml` file:
```yaml
version: '3.8'
services:
wordpress:
image: wordpress:latest
container_name: wordpress_app
restart: unless-stopped
ports:
- "80:80"
- "443:443"
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: secure_password_here
WORDPRESS_DB_NAME: wordpress
volumes:
- wordpress_data:/var/www/html
depends_on:
- db
networks:
- wordpress_network
db:
image: mysql:8.0
container_name: wordpress_db
restart: unless-stopped
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: secure_password_here
MYSQL_ROOT_PASSWORD: root_password_here
volumes:
- db_data:/var/lib/mysql
networks:
- wordpress_network
volumes:
wordpress_data:
db_data:
networks:
wordpress_network:
driver: bridge
```
Environment Variables Security
For better security, create a `.env` file to store sensitive information:
```bash
.env file
MYSQL_ROOT_PASSWORD=your_strong_root_password
MYSQL_DATABASE=wordpress
MYSQL_USER=wordpress
MYSQL_PASSWORD=your_strong_wordpress_password
WORDPRESS_DB_HOST=db:3306
```
Update your `docker-compose.yml` to use environment variables:
```yaml
version: '3.8'
services:
wordpress:
image: wordpress:latest
container_name: wordpress_app
restart: unless-stopped
ports:
- "80:80"
- "443:443"
environment:
WORDPRESS_DB_HOST: ${WORDPRESS_DB_HOST}
WORDPRESS_DB_USER: ${MYSQL_USER}
WORDPRESS_DB_PASSWORD: ${MYSQL_PASSWORD}
WORDPRESS_DB_NAME: ${MYSQL_DATABASE}
volumes:
- wordpress_data:/var/www/html
depends_on:
- db
networks:
- wordpress_network
db:
image: mysql:8.0
container_name: wordpress_db
restart: unless-stopped
environment:
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
volumes:
- db_data:/var/lib/mysql
networks:
- wordpress_network
volumes:
wordpress_data:
db_data:
networks:
wordpress_network:
driver: bridge
```
Launching Your WordPress Site
Deploy the containers:
```bash
docker-compose up -d
```
Check container status:
```bash
docker-compose ps
```
View logs if needed:
```bash
docker-compose logs wordpress
docker-compose logs db
```
Accessing WordPress
Open your web browser and navigate to:
- `http://localhost` (if running locally)
- `http://your-server-ip` (if running on a remote server)
Complete the WordPress installation wizard by providing:
- Site title
- Admin username and password
- Admin email address
Advanced Configuration
Adding SSL/TLS with Let's Encrypt
Create an enhanced setup with Nginx reverse proxy and SSL:
```yaml
version: '3.8'
services:
nginx:
image: nginx:alpine
container_name: wordpress_nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
- ./nginx/ssl:/etc/nginx/ssl
- wordpress_data:/var/www/html
depends_on:
- wordpress
networks:
- wordpress_network
wordpress:
image: wordpress:latest
container_name: wordpress_app
restart: unless-stopped
expose:
- "80"
environment:
WORDPRESS_DB_HOST: ${WORDPRESS_DB_HOST}
WORDPRESS_DB_USER: ${MYSQL_USER}
WORDPRESS_DB_PASSWORD: ${MYSQL_PASSWORD}
WORDPRESS_DB_NAME: ${MYSQL_DATABASE}
volumes:
- wordpress_data:/var/www/html
depends_on:
- db
networks:
- wordpress_network
db:
image: mysql:8.0
container_name: wordpress_db
restart: unless-stopped
environment:
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
volumes:
- db_data:/var/lib/mysql
networks:
- wordpress_network
certbot:
image: certbot/certbot
container_name: wordpress_certbot
volumes:
- ./nginx/ssl:/etc/letsencrypt
- wordpress_data:/var/www/html
command: certonly --webroot --webroot-path=/var/www/html --email your-email@domain.com --agree-tos --no-eff-email -d your-domain.com
volumes:
wordpress_data:
db_data:
networks:
wordpress_network:
driver: bridge
```
Custom PHP Configuration
Create a custom PHP configuration file:
```bash
mkdir -p ./php/conf.d
```
Create `./php/conf.d/custom.ini`:
```ini
file_uploads = On
memory_limit = 256M
upload_max_filesize = 64M
post_max_size = 64M
max_execution_time = 600
```
Update your WordPress service in `docker-compose.yml`:
```yaml
wordpress:
image: wordpress:latest
container_name: wordpress_app
restart: unless-stopped
ports:
- "80:80"
environment:
WORDPRESS_DB_HOST: ${WORDPRESS_DB_HOST}
WORDPRESS_DB_USER: ${MYSQL_USER}
WORDPRESS_DB_PASSWORD: ${MYSQL_PASSWORD}
WORDPRESS_DB_NAME: ${MYSQL_DATABASE}
volumes:
- wordpress_data:/var/www/html
- ./php/conf.d/custom.ini:/usr/local/etc/php/conf.d/custom.ini
depends_on:
- db
networks:
- wordpress_network
```
Database Optimization
Optimize MySQL for WordPress by creating `./mysql/conf.d/mysql.cnf`:
```ini
[mysqld]
innodb_buffer_pool_size = 256M
innodb_log_file_size = 64M
innodb_file_per_table = 1
innodb_flush_method = O_DIRECT
key_buffer_size = 32M
max_allowed_packet = 64M
table_open_cache = 256
sort_buffer_size = 1M
net_buffer_length = 32K
read_buffer_size = 256K
read_rnd_buffer_size = 512K
myisam_sort_buffer_size = 8M
```
Update the database service:
```yaml
db:
image: mysql:8.0
container_name: wordpress_db
restart: unless-stopped
environment:
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
volumes:
- db_data:/var/lib/mysql
- ./mysql/conf.d:/etc/mysql/conf.d
networks:
- wordpress_network
```
Security Best Practices
Container Security
1. Use specific image tags instead of `latest`:
```yaml
services:
wordpress:
image: wordpress:6.3-apache
db:
image: mysql:8.0.34
```
2. Run containers as non-root users when possible:
```yaml
wordpress:
image: wordpress:6.3-apache
user: "1000:1000"
```
3. Limit container resources:
```yaml
wordpress:
image: wordpress:6.3-apache
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
```
Network Security
Create isolated networks and restrict external access:
```yaml
networks:
wordpress_network:
driver: bridge
internal: false
db_network:
driver: bridge
internal: true
```
File Permissions
Set proper file permissions for WordPress:
```bash
After containers are running
docker exec wordpress_app chown -R www-data:www-data /var/www/html
docker exec wordpress_app find /var/www/html -type d -exec chmod 755 {} \;
docker exec wordpress_app find /var/www/html -type f -exec chmod 644 {} \;
```
WordPress Security Headers
Add security headers via Nginx configuration:
```nginx
nginx/conf.d/default.conf
server {
listen 80;
server_name your-domain.com;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
location / {
proxy_pass http://wordpress:80;
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;
}
}
```
Performance Optimization
Caching with Redis
Add Redis caching to your setup:
```yaml
redis:
image: redis:7-alpine
container_name: wordpress_redis
restart: unless-stopped
volumes:
- redis_data:/data
networks:
- wordpress_network
volumes:
wordpress_data:
db_data:
redis_data:
```
Content Delivery Network (CDN)
Configure WordPress to use a CDN by adding environment variables:
```yaml
wordpress:
environment:
WORDPRESS_DB_HOST: ${WORDPRESS_DB_HOST}
WORDPRESS_DB_USER: ${MYSQL_USER}
WORDPRESS_DB_PASSWORD: ${MYSQL_PASSWORD}
WORDPRESS_DB_NAME: ${MYSQL_DATABASE}
WORDPRESS_CONFIG_EXTRA: |
define('WP_CACHE', true);
define('CDN_URL', 'https://your-cdn-domain.com');
```
Database Connection Pooling
For high-traffic sites, consider using ProxySQL for connection pooling:
```yaml
proxysql:
image: proxysql/proxysql:latest
container_name: wordpress_proxysql
restart: unless-stopped
volumes:
- ./proxysql/proxysql.cnf:/etc/proxysql.cnf
ports:
- "6032:6032"
- "6033:6033"
networks:
- wordpress_network
```
Backup and Maintenance
Automated Database Backups
Create a backup script `backup.sh`:
```bash
#!/bin/bash
Configuration
BACKUP_DIR="/backups"
DATE=$(date +%Y%m%d_%H%M%S)
CONTAINER_NAME="wordpress_db"
DB_NAME="wordpress"
DB_USER="wordpress"
DB_PASSWORD="your_password"
Create backup directory
mkdir -p $BACKUP_DIR
Create database backup
docker exec $CONTAINER_NAME mysqldump -u $DB_USER -p$DB_PASSWORD $DB_NAME > $BACKUP_DIR/wordpress_db_$DATE.sql
Create WordPress files backup
docker run --rm -v wordpress_data:/data -v $BACKUP_DIR:/backup alpine tar czf /backup/wordpress_files_$DATE.tar.gz -C /data .
Remove backups older than 7 days
find $BACKUP_DIR -name "*.sql" -mtime +7 -delete
find $BACKUP_DIR -name "*.tar.gz" -mtime +7 -delete
echo "Backup completed: $DATE"
```
Make it executable and add to crontab:
```bash
chmod +x backup.sh
crontab -e
Add this line for daily backups at 2 AM
0 2 * /path/to/backup.sh
```
Container Health Checks
Add health checks to your services:
```yaml
wordpress:
image: wordpress:latest
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
db:
image: mysql:8.0
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
```
Log Management
Configure log rotation and management:
```yaml
wordpress:
image: wordpress:latest
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
```
Troubleshooting Common Issues
Container Startup Issues
Problem: Containers fail to start
Solutions:
1. Check Docker service status:
```bash
sudo systemctl status docker
```
2. Verify Docker Compose file syntax:
```bash
docker-compose config
```
3. Check available disk space:
```bash
df -h
```
4. Review container logs:
```bash
docker-compose logs [service_name]
```
Database Connection Problems
Problem: WordPress cannot connect to database
Solutions:
1. Verify database container is running:
```bash
docker-compose ps db
```
2. Check database logs:
```bash
docker-compose logs db
```
3. Test database connectivity:
```bash
docker exec wordpress_app ping db
```
4. Verify environment variables:
```bash
docker exec wordpress_app env | grep WORDPRESS_DB
```
Permission Issues
Problem: File upload or plugin installation failures
Solutions:
1. Fix WordPress file permissions:
```bash
docker exec wordpress_app chown -R www-data:www-data /var/www/html
```
2. Check volume mounts:
```bash
docker inspect wordpress_app | grep -A 10 "Mounts"
```
Memory and Performance Issues
Problem: Slow performance or out-of-memory errors
Solutions:
1. Monitor resource usage:
```bash
docker stats
```
2. Increase PHP memory limit:
```ini
In custom.ini
memory_limit = 512M
```
3. Optimize database queries:
```bash
docker exec wordpress_db mysql -u root -p -e "SHOW PROCESSLIST;"
```
SSL Certificate Issues
Problem: SSL certificate problems with Let's Encrypt
Solutions:
1. Check certificate status:
```bash
docker exec wordpress_certbot certbot certificates
```
2. Renew certificates:
```bash
docker exec wordpress_certbot certbot renew
```
3. Verify domain DNS settings:
```bash
nslookup your-domain.com
```
Port Conflicts
Problem: Port already in use errors
Solutions:
1. Check what's using the port:
```bash
sudo netstat -tulpn | grep :80
```
2. Stop conflicting services:
```bash
sudo systemctl stop apache2 # or nginx
```
3. Use different ports in docker-compose.yml:
```yaml
ports:
- "8080:80"
```
Best Practices and Tips
Development vs Production
Development Environment:
- Use `latest` tags for easy updates
- Enable debug mode
- Mount source code as volumes for live editing
- Use simpler networking
Production Environment:
- Use specific version tags
- Implement proper logging and monitoring
- Use secrets management
- Enable SSL/TLS
- Implement backup strategies
- Use resource limits
Resource Management
1. Set appropriate resource limits:
```yaml
deploy:
resources:
limits:
cpus: '1.0'
memory: 1G
reservations:
cpus: '0.5'
memory: 512M
```
2. Monitor resource usage regularly:
```bash
docker stats --no-stream
```
3. Clean up unused resources:
```bash
docker system prune -a
docker volume prune
```
Version Control
1. Track your Docker configuration:
```bash
git init
echo ".env" >> .gitignore
git add docker-compose.yml
git commit -m "Initial WordPress Docker setup"
```
2. Use tagged releases:
```yaml
services:
wordpress:
image: wordpress:6.3.1-apache
```
Monitoring and Logging
1. Implement centralized logging:
```yaml
logging:
driver: syslog
options:
syslog-address: "tcp://your-log-server:514"
```
2. Set up monitoring with Prometheus:
```yaml
prometheus:
image: prom/prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus:/etc/prometheus
```
Scaling Considerations
1. Horizontal scaling with load balancer:
```yaml
wordpress1:
image: wordpress:latest
# configuration
wordpress2:
image: wordpress:latest
# configuration
loadbalancer:
image: nginx:alpine
# load balancer configuration
```
2. Database clustering for high availability:
```yaml
db-master:
image: mysql:8.0
# master configuration
db-slave:
image: mysql:8.0
# slave configuration
```
Security Hardening
1. Regular security updates:
```bash
Update base images regularly
docker-compose pull
docker-compose up -d
```
2. Scan for vulnerabilities:
```bash
docker scan wordpress:latest
```
3. Use Docker secrets for sensitive data:
```yaml
secrets:
db_password:
file: ./secrets/db_password.txt
services:
wordpress:
secrets:
- db_password
```
Conclusion
Deploying WordPress with Docker on Linux provides a robust, scalable, and maintainable solution for hosting WordPress sites. This comprehensive guide has covered everything from basic setup to advanced configuration, security hardening, and troubleshooting.
Key Takeaways
1. Docker simplifies WordPress deployment by providing consistent environments and easy dependency management
2. Docker Compose orchestrates multi-container applications effectively, making complex setups manageable
3. Security should be implemented at multiple layers, including container security, network isolation, and application-level protections
4. Regular maintenance and monitoring are essential for production deployments
5. Backup strategies should be implemented from day one to prevent data loss
Next Steps
After successfully deploying WordPress with Docker, consider these advanced topics:
1. Implement CI/CD pipelines for automated deployments
2. Set up monitoring and alerting with tools like Prometheus and Grafana
3. Explore container orchestration with Kubernetes for large-scale deployments
4. Implement advanced caching strategies with Redis and CDN integration
5. Set up development workflows with Docker for your team
Additional Resources
- [Official WordPress Docker Documentation](https://hub.docker.com/_/wordpress)
- [Docker Compose Documentation](https://docs.docker.com/compose/)
- [WordPress Security Best Practices](https://wordpress.org/support/article/hardening-wordpress/)
- [MySQL Docker Documentation](https://hub.docker.com/_/mysql)
Remember to keep your Docker images, WordPress installation, and plugins updated regularly to maintain security and performance. With proper setup and maintenance, your Dockerized WordPress deployment will provide reliable service while remaining easy to manage and scale.
This guide provides a solid foundation for WordPress deployment with Docker, but remember that each use case may require specific customizations. Always test configurations in a development environment before applying them to production systems.