WordPress con Docker Compose: Guía Completa Producción 2025
¿Quieres montar WordPress en minutos sin líos de Apache, PHP, MySQL a mano? Docker Compose lo hace trivial: un archivo YAML, un comando, y WordPress corriendo. En esta guía aprenderás desde setup básico hasta producción con HTTPS, backups automáticos, y optimización de performance.
En esta guía aprenderás:
- 3 métodos instalación: básico, producción, producción+ con Nginx
- Configuración segura con .env para secretos
- HTTPS con Let’s Encrypt (Caddy reverse proxy)
- Backups automáticos (database + archivos)
- Optimización performance (cache, PHP-FPM tuning)
- Migración desde WordPress existente
- Troubleshooting problemas comunes
🚀 Método 1: WordPress Básico (Testing/Desarrollo)
Setup mínimo para probar WordPress localmente.
Crear carpeta y archivos:
mkdir ~/wordpress-docker && cd ~/wordpress-docker
nano docker-compose.yml
docker-compose.yml básico:
version: '3.8'
services:
db:
image: mysql:8.0
container_name: wordpress_db
restart: always
environment:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_DATABASE: wordpress
MYSQL_USER: wpuser
MYSQL_PASSWORD: wppassword
volumes:
- db_data:/var/lib/mysql
wordpress:
image: wordpress:latest
container_name: wordpress
restart: always
ports:
- "8000:80"
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wpuser
WORDPRESS_DB_PASSWORD: wppassword
WORDPRESS_DB_NAME: wordpress
volumes:
- wp_data:/var/www/html
depends_on:
- db
volumes:
db_data:
wp_data:
Arrancar:
docker compose up -d
Acceder: http://localhost:8000
Completar wizard WordPress (idioma, admin user, password)
🔒 Método 2: Producción con Variables de Entorno
Mejor práctica: secretos en .env file separado.
Crear .env:
# Database
MYSQL_ROOT_PASSWORD=cambiar_esto_root_password_seguro
MYSQL_DATABASE=wordpress
MYSQL_USER=wpuser
MYSQL_PASSWORD=cambiar_esto_password_muy_seguro
# WordPress
WORDPRESS_DB_HOST=db:3306
WORDPRESS_DB_USER=wpuser
WORDPRESS_DB_PASSWORD=cambiar_esto_password_muy_seguro
WORDPRESS_DB_NAME=wordpress
# Domain
WORDPRESS_URL=https://tudominio.com
docker-compose.yml producción:
version: '3.8'
services:
db:
image: mysql:8.0
container_name: wordpress_db
restart: unless-stopped
env_file: .env
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
volumes:
- db_data:/var/lib/mysql
command: '--default-authentication-plugin=mysql_native_password'
wordpress:
image: wordpress:6.4-php8.2-apache
container_name: wordpress
restart: unless-stopped
env_file: .env
environment:
WORDPRESS_DB_HOST: ${WORDPRESS_DB_HOST}
WORDPRESS_DB_USER: ${WORDPRESS_DB_USER}
WORDPRESS_DB_PASSWORD: ${WORDPRESS_DB_PASSWORD}
WORDPRESS_DB_NAME: ${MYSQL_DATABASE}
WORDPRESS_CONFIG_EXTRA: |
define('WP_HOME', '${WORDPRESS_URL}');
define('WP_SITEURL', '${WORDPRESS_URL}');
define('WP_DEBUG', false);
define('WP_DEBUG_LOG', false);
volumes:
- wp_data:/var/www/html
depends_on:
- db
volumes:
db_data:
wp_data:
Notar:
- Versión específica WordPress (no :latest)
- Secrets en .env (no hardcoded)
restart: unless-stopped(auto-restart excepto manual stop)- MySQL authentication plugin (compatibilidad)
🌐 Método 3: Producción + HTTPS (Caddy Reverse Proxy)
WordPress con certificado SSL automático via Let’s Encrypt.
docker-compose.yml completo:
version: '3.8'
services:
db:
image: mysql:8.0
container_name: wordpress_db
restart: unless-stopped
env_file: .env
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
volumes:
- db_data:/var/lib/mysql
networks:
- wordpress
command: '--default-authentication-plugin=mysql_native_password'
wordpress:
image: wordpress:6.4-php8.2-apache
container_name: wordpress
restart: unless-stopped
env_file: .env
environment:
WORDPRESS_DB_HOST: ${WORDPRESS_DB_HOST}
WORDPRESS_DB_USER: ${WORDPRESS_DB_USER}
WORDPRESS_DB_PASSWORD: ${WORDPRESS_DB_PASSWORD}
WORDPRESS_DB_NAME: ${MYSQL_DATABASE}
WORDPRESS_CONFIG_EXTRA: |
define('WP_HOME', '${WORDPRESS_URL}');
define('WP_SITEURL', '${WORDPRESS_URL}');
define('FORCE_SSL_ADMIN', true);
if (strpos($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') !== false)
$_SERVER['HTTPS']='on';
volumes:
- wp_data:/var/www/html
networks:
- wordpress
depends_on:
- db
caddy:
image: caddy:latest
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
networks:
- wordpress
networks:
wordpress:
driver: bridge
volumes:
db_data:
wp_data:
caddy_data:
caddy_config:
Caddyfile:
tudominio.com {
reverse_proxy wordpress:80
}
Caddy obtiene certificado SSL automáticamente. Accede: https://tudominio.com
💾 Backups Automáticos
Backup Database + Archivos
Script backup completo: ~/scripts/backup-wordpress.sh
#!/bin/bash
BACKUP_DIR="/home/user/backups/wordpress"
DATE=$(date +%Y-%m-%d_%H-%M-%S)
WP_DIR="/home/user/wordpress-docker"
mkdir -p "$BACKUP_DIR"
# Backup MySQL
docker exec wordpress_db mysqldump -u root -p${MYSQL_ROOT_PASSWORD} wordpress | \
gzip > "$BACKUP_DIR/wordpress-db-${DATE}.sql.gz"
# Backup archivos WordPress (uploads, themes, plugins)
tar -czf "$BACKUP_DIR/wordpress-files-${DATE}.tar.gz" \
-C "$WP_DIR" wp_data
# Limpiar backups antiguos (30 días)
find "$BACKUP_DIR" -name "wordpress-*" -mtime +30 -delete
echo "Backup completado:"
echo " - wordpress-db-${DATE}.sql.gz"
echo " - wordpress-files-${DATE}.tar.gz"
Permisos y cron:
chmod +x ~/scripts/backup-wordpress.sh
# Backup diario 3am
crontab -e
0 3 * * * /home/user/scripts/backup-wordpress.sh >> /home/user/logs/wordpress-backup.log 2>&1
Restaurar desde Backup
# Parar contenedores
docker compose down
# Restaurar database
gunzip < /home/user/backups/wordpress/wordpress-db-2025-11-01.sql.gz | \
docker exec -i wordpress_db mysql -u root -p${MYSQL_ROOT_PASSWORD} wordpress
# Restaurar archivos
rm -rf wp_data
tar -xzf /home/user/backups/wordpress/wordpress-files-2025-11-01.tar.gz
# Reiniciar
docker compose up -d
⚡ Optimización Performance
PHP-FPM Tuning
Crear custom php.ini:
upload_max_filesize = 64M
post_max_size = 64M
memory_limit = 256M
max_execution_time = 300
max_input_vars = 3000
Montar en docker-compose.yml:
wordpress:
volumes:
- wp_data:/var/www/html
- ./custom-php.ini:/usr/local/etc/php/conf.d/custom.ini:ro
Redis Cache
Añadir Redis al stack:
services:
redis:
image: redis:alpine
container_name: wordpress_redis
restart: unless-stopped
networks:
- wordpress
Instalar plugin: Redis Object Cache (desde WordPress admin)
🔄 Migrar WordPress Existente a Docker
Paso 1: Exportar database WordPress antiguo
mysqldump -u usuario -p wordpress_viejo > wordpress_export.sql
Paso 2: Copiar archivos wp-content
tar -czf wp-content.tar.gz /var/www/html/wp-content
Paso 3: Arrancar WordPress Docker nuevo
docker compose up -d
Paso 4: Importar database
cat wordpress_export.sql | docker exec -i wordpress_db mysql -u root -p${MYSQL_ROOT_PASSWORD} wordpress
Paso 5: Copiar wp-content
docker cp wp-content.tar.gz wordpress:/var/www/html/
docker exec wordpress tar -xzf /var/www/html/wp-content.tar.gz -C /var/www/html/
docker exec wordpress chown -R www-data:www-data /var/www/html/wp-content
Paso 6: Actualizar URLs
Si dominio cambió, actualizar en MySQL:
docker exec -it wordpress_db mysql -u root -p${MYSQL_ROOT_PASSWORD} wordpress
UPDATE wp_options SET option_value='https://nuevodominio.com' WHERE option_name='siteurl';
UPDATE wp_options SET option_value='https://nuevodominio.com' WHERE option_name='home';
🔧 Troubleshooting
Error: "Error establishing database connection"
Causa: WordPress arranca antes que MySQL esté listo
Solución: Añadir healthcheck
db:
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
timeout: 20s
retries: 10
wordpress:
depends_on:
db:
condition: service_healthy
Permisos archivos incorrectos
Síntoma: No puedes subir media, instalar plugins
Solución:
docker exec wordpress chown -R www-data:www-data /var/www/html
docker exec wordpress find /var/www/html -type d -exec chmod 755 {} \;
docker exec wordpress find /var/www/html -type f -exec chmod 644 {} \;
WordPress muy lento
Causas:
- No hay cache (instalar Redis Object Cache)
- PHP memory_limit bajo (aumentar a 256M)
- Muchos plugins pesados (desactivar innecesarios)
📊 Comparativa: WordPress Docker vs Tradicional
| Aspecto | Docker | Tradicional (LAMP) |
|---|---|---|
| Setup tiempo | 5 minutos | 30-60 minutos |
| Configuración | docker-compose.yml | Apache/Nginx + PHP + MySQL manual |
| Portabilidad | ✅ Funciona igual en cualquier servidor | ⚠️ Depende del OS |
| Múltiples WordPress | ✅ Fácil (carpetas separadas) | ⚠️ Complejo (virtual hosts) |
| Updates | docker compose pull && restart | apt update && upgrade (puede romper) |
| Backups | Volúmenes fáciles de copiar | Carpetas dispersas (/var/www, /var/lib/mysql) |
| Rollback | ✅ Cambiar tag imagen | ⚠️ Difícil sin backup completo |
| Performance | Similar (overhead mínimo) | Similar |
🔗 Recursos Adicionales
❓ Preguntas Frecuentes
¿WordPress Docker es tan rápido como instalación tradicional?
Sí, performance casi idéntica. Docker overhead es <2%. Con optimizaciones (Redis cache, PHP tuning) puede ser incluso más rápido que LAMP tradicional mal configurado.
¿Puedo usar plugins y themes normales?
Sí, 100%. WordPress Docker es WordPress oficial. Todos los plugins/themes funcionan igual. Instalas desde admin panel como siempre.
¿Cómo actualizar WordPress en Docker?
Dos opciones: 1) Desde admin panel WordPress (update automático como siempre), 2) Cambiar tag imagen en docker-compose.yml (wordpress:6.4 → wordpress:6.5) y docker compose up -d. Opción 1 recomendada para minor updates, opción 2 para control total.
¿Qué pasa con mis datos si borro contenedores?
Datos persisten en volúmenes Docker (db_data, wp_data). Puedes borrar contenedores (docker compose down) y recrearlos (docker compose up -d) sin perder nada. Para borrar TODO incluyendo datos: docker compose down -v (cuidado!).
¿Puedo tener múltiples WordPress en mismo servidor?
Sí, fácil. Crea carpetas separadas (~/wp1, ~/wp2) cada una con su docker-compose.yml. Cambia puertos (8000, 8001) o usa reverse proxy (Caddy/Nginx) con dominios diferentes. Comparten host pero contenedores aislados.
¿Necesito saber Docker para usar esto?
No. Con copiar docker-compose.yml, editar .env con tus passwords, y correr docker compose up -d funciona. Para troubleshooting básico útil saber docker logs wordpress y docker exec -it wordpress bash.
¿WordPress Docker funciona en Windows/Mac?
Sí, Docker Desktop (Windows/Mac) soporta docker-compose. Funciona idéntico. Solo cambia rutas volúmenes (en Windows usar C:\Users\...). En producción recomendado Linux por performance.
¿Cómo hacer backup antes de actualizar WordPress?
Usa script backup mostrado arriba. Antes de update mayor: ./backup-wordpress.sh, verificar backup existe, luego actualizar. Si rompe algo, restaurar desde backup. Proceso tarda 2 minutos, previene dolores de cabeza.
¿Puedo usar MariaDB en vez de MySQL?
Sí, cambia imagen: image: mariadb:11. Variables entorno casi iguales. MariaDB suele ser más rápido que MySQL para WordPress. Recomendado si empiezas desde cero.
¿WordPress Docker consume más recursos que tradicional?
Overhead Docker: ~50-100 MB RAM extra. Irrelevante en servidores modernos. A cambio ganas: aislamiento, portabilidad, facilidad management. Trade-off vale totalmente la pena.
¿Cómo habilitar HTTPS si no tengo dominio?
Opciones: 1) Certificado self-signed (navegador dará warning, ok para desarrollo), 2) Cloudflare Tunnel (gratis, sin dominio propio necesario), 3) Comprar dominio (~$10/año). Para producción seria recomiendo opción 3.
¿WordPress Docker soporta Multisite?
Sí, habilitando Multisite en wp-config.php como WordPress tradicional. En WORDPRESS_CONFIG_EXTRA añade: define('WP_ALLOW_MULTISITE', true);. Luego Network Setup desde admin.
💡 Conclusión
WordPress Docker simplifica deployment masivamente: setup 5 minutos, backups triviales, múltiples sites fácil, updates sin miedo.
Recomendación según caso:
- 🧪 Testing/desarrollo local: Método 1 (básico), puerto 8000, sin HTTPS
- 🏢 Producción pequeña (1 site): Método 3 (Caddy HTTPS), backup cron diario
- 🚀 Múltiples WordPress: Caddy reverse proxy + carpeta por site
- ⚡ Alto tráfico: Método 3 + Redis cache + PHP tuning + CDN
Próximos pasos:
- Setup WordPress Docker método que elijas
- Configurar .env con passwords fuertes
- Completar wizard WordPress (admin user)
- Instalar tema + plugins básicos
- Configurar backup automático (script cron)
- Si producción: setup HTTPS con Caddy/Let's Encrypt
¿Ya tienes WordPress corriendo en Docker? Comparte tu experiencia en comentarios.

That’s my boy, great job Optimus Prime
Tyvm Megatron, have a nice day!