Cómo instalar WordPress con Docker Compose: guía del stack completo
Instala WordPress con Docker Compose. Stack completo con MySQL 8.4, phpMyAdmin, SSL de Cloudflare, copias de seguridad automáticas y caché con Redis. Guía de configuración lista para producción.
Actualizado Publicado 30 min de lectura

Esta guía te lleva paso a paso por la instalación de WordPress con Docker Compose, cubriendo un stack completo listo para producción: MySQL 8.4 LTS, phpMyAdmin, copias de seguridad automáticas de la base de datos, caché de objetos Redis opcional y Cloudflare Tunnels para SSL. Todo funciona en cinco contenedores gestionados por un único archivo compose.yaml en cualquier VPS Linux o mini PC como servidor doméstico.
Necesitarás un VPS Linux. Recomiendo Hetzner por su relación precio-rendimiento, la mejor en Europa, o Hostinger si prefieres almacenamiento NVMe a precio económico. Si usas WooCommerce, también puedes desplegar el dashboard de administración de productos Woo junto a WordPress en Docker para una gestión de productos más rápida.
Los precios de los VPS subieron en todos los proveedores en 2026 — si estás reconsiderando un servidor alquilado, mira qué cambió y cuándo gana un mini PC.
Esto es lo que obtienes al seguir esta guía:
- Contenedor de WordPress (versión de PHP fijada, volumen persistente)
- Base de datos MySQL 8.4 LTS (con soporte hasta 2032)
- phpMyAdmin para la gestión de la base de datos
- Copias de seguridad automáticas de la base de datos con rotación
- Caché de objetos Redis para consultas más rápidas a la base de datos
- SSL mediante Cloudflare Tunnels (plan gratuito)
Usaré
latesten algunas etiquetas de imagen más abajo, pero puedes fijar versiones exactas si prefieres despliegues predecibles. La única excepción es MySQL. Más sobre esto en el paso 3.
Stack de WordPress con Docker Compose: configuración paso a paso
1. Requisitos previos para tu stack de WordPress en Docker
- Un VPS Linux con 2GB+ de RAM (4GB recomendados para WordPress + Redis + phpMyAdmin)
- Docker Engine 24+ y Docker Compose V2 (el plugin
docker compose, no eldocker-composeindependiente) - Un nombre de dominio apuntando a tu servidor (o usa acceso directo por IP)
- Una cuenta de Cloudflare (plan gratuito) para Tunnels y SSL
- Puertos 5010 y 5011 disponibles (o elige los tuyos)
Sintaxis de Docker Compose V2
Esta guía usa la sintaxis de Docker Compose V2. El docker-compose independiente (V1) está obsoleto y ya no recibe actualizaciones. Todos los ejemplos usan el nombre compose.yaml. Si todavía estás en V1, actualiza con apt install docker-compose-plugin o consulta la documentación de tu distribución.
Dockge es opcional pero recomendado. Te da una interfaz web para gestionar tus stacks de compose sin tener que entrar por SSH al servidor. Consulta Instalar Dockge - Gestor de Docker Compose para Self-Hosting para la configuración completa. Si prefieres un servidor doméstico en lugar de un VPS, echa un vistazo a los mejores mini PC para servidor doméstico. Un GMKtec M5 Ultra funciona bien para este tipo de stack.
Familiarízate con estos comandos esenciales de Docker antes de continuar. Te ayudarán a resolver problemas.
2. Crea el directorio del proyecto y los archivos de configuración
Los bind-mounts de Docker se comportan mal cuando el archivo de origen no existe. Docker crea un directorio en lugar de un archivo, y tu configuración de PHP no se cargará. Crea primero los archivos de configuración:
# Navega hasta donde vivirá el stack
cd /opt/stacks/wordpress
# Crea el directorio de configuración y los archivos vacíos
mkdir -p config
touch ./config/wp_php.ini
touch ./config/pma_php.ini
touch ./config/pma_config.php
# Ajusta el propietario para que los contenedores puedan escribir en los volúmenes
# WordPress se ejecuta como UID 1000 dentro del contenedor
chown -R 1000:1000 ./configLos permisos de archivo importan
Si te saltas la creación de estos archivos primero, Docker los creará como directorios y tu configuración de PHP no se cargará. Después del primer docker compose up, revisa también el propietario de los directorios de datos: chown -R 1000:1000 ./wp-app ./db_data ./backups para evitar errores de permiso denegado dentro de WordPress.
Verifica que los archivos se crearon correctamente:
ls -la ./config/Deberías ver archivos normales (-rw-r--r--), no directorios (drwxr-xr-x).
3. Archivo Docker Compose: WordPress, MySQL 8.4 LTS y phpMyAdmin
Aquí está el compose.yaml completo con los cinco servicios. Explico las decisiones clave después del código.
Stack base (4 servicios)
services:
wp:
image: wordpress:php8.3
restart: unless-stopped
ports:
- 5010:80
volumes:
- ./config/wp_php.ini:/usr/local/etc/php/conf.d/conf.ini
- ./wp-app:/var/www/html
environment:
WORDPRESS_DB_HOST: wp-db:3306
WORDPRESS_DB_NAME: "${DB_NAME}"
WORDPRESS_DB_USER: "${DB_USER}"
WORDPRESS_DB_PASSWORD: "${DB_PASSWORD}"
WORDPRESS_TABLE_PREFIX: "wp_"
WORDPRESS_CONFIG_EXTRA: |
define('FS_METHOD', 'direct');
depends_on:
wp-db:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/wp-admin/install.php"]
interval: 30s
timeout: 10s
retries: 3
deploy:
resources:
limits:
memory: 512M
reservations:
memory: 256M
wp-db:
image: mysql:8.4
volumes:
- ./db_data:/var/lib/mysql
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: "${DB_ROOT_PASSWORD}"
MYSQL_DATABASE: "${DB_NAME}"
MYSQL_USER: "${DB_USER}"
MYSQL_PASSWORD: "${DB_PASSWORD}"
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
deploy:
resources:
limits:
memory: 1G
reservations:
memory: 512M
pma:
image: phpmyadmin:latest
ports:
- 5011:80
volumes:
- ./config/pma_php.ini:/usr/local/etc/php/conf.d/conf.ini
- ./config/pma_config.php:/etc/phpmyadmin/config.user.inc.php
restart: unless-stopped
environment:
PMA_HOST: wp-db
PMA_PORT: 3306
MYSQL_ROOT_PASSWORD: "${DB_ROOT_PASSWORD}"
UPLOAD_LIMIT: 100M
depends_on:
- wp-db
wp-db-backup:
image: tiredofit/db-backup:4.1
volumes:
- ./backups:/backup
restart: unless-stopped
environment:
DB_TYPE: mysql
DB_HOST: wp-db
DB_NAME: "${DB_NAME}"
DB_USER: "${DB_USER}"
DB_PASS: "${DB_PASSWORD}"
DB_BACKUP_INTERVAL: 720
DB_CLEANUP_TIME: 72000
CHECKSUM: SHA1
COMPRESSION: ZSTD
CONTAINER_ENABLE_MONITORING: "false"
depends_on:
- wp-dbStack completo con Redis (5 servicios)
services:
wp:
build:
context: .
dockerfile: Dockerfile
restart: unless-stopped
ports:
- 5010:80
volumes:
- ./config/wp_php.ini:/usr/local/etc/php/conf.d/conf.ini
- ./wp-app:/var/www/html
environment:
WORDPRESS_DB_HOST: wp-db:3306
WORDPRESS_DB_NAME: "${DB_NAME}"
WORDPRESS_DB_USER: "${DB_USER}"
WORDPRESS_DB_PASSWORD: "${DB_PASSWORD}"
WORDPRESS_TABLE_PREFIX: "wp_"
WORDPRESS_CONFIG_EXTRA: |
define('FS_METHOD', 'direct');
define('WP_REDIS_HOST', 'redis-wp');
define('WP_REDIS_PORT', 6379);
define('WP_REDIS_DATABASE', 0);
depends_on:
wp-db:
condition: service_healthy
redis-wp:
condition: service_started
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/wp-admin/install.php"]
interval: 30s
timeout: 10s
retries: 3
deploy:
resources:
limits:
memory: 512M
reservations:
memory: 256M
wp-db:
image: mysql:8.4
volumes:
- ./db_data:/var/lib/mysql
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: "${DB_ROOT_PASSWORD}"
MYSQL_DATABASE: "${DB_NAME}"
MYSQL_USER: "${DB_USER}"
MYSQL_PASSWORD: "${DB_PASSWORD}"
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
deploy:
resources:
limits:
memory: 1G
reservations:
memory: 512M
pma:
image: phpmyadmin:latest
ports:
- 5011:80
volumes:
- ./config/pma_php.ini:/usr/local/etc/php/conf.d/conf.ini
- ./config/pma_config.php:/etc/phpmyadmin/config.user.inc.php
restart: unless-stopped
environment:
PMA_HOST: wp-db
PMA_PORT: 3306
MYSQL_ROOT_PASSWORD: "${DB_ROOT_PASSWORD}"
UPLOAD_LIMIT: 100M
depends_on:
- wp-db
wp-db-backup:
image: tiredofit/db-backup:4.1
volumes:
- ./backups:/backup
restart: unless-stopped
environment:
DB_TYPE: mysql
DB_HOST: wp-db
DB_NAME: "${DB_NAME}"
DB_USER: "${DB_USER}"
DB_PASS: "${DB_PASSWORD}"
DB_BACKUP_INTERVAL: 720
DB_CLEANUP_TIME: 72000
CHECKSUM: SHA1
COMPRESSION: ZSTD
CONTAINER_ENABLE_MONITORING: "false"
depends_on:
- wp-db
redis-wp:
image: redis:7-alpine
restart: unless-stopped
volumes:
- ./redis_data:/data
deploy:
resources:
limits:
memory: 256M
reservations:
memory: 128MNO uses mysql:latest
mysql:latest ahora sigue las versiones Innovation de MySQL 9.x, versiones de vida corta con solo ~3 meses de soporte por versión menor. Para WordPress en producción, usa mysql:8.4 (LTS, con soporte hasta abril de 2032). La etiqueta mysql:lts también funciona como puntero móvil a la LTS.
MySQL Innovation vs LTS
Desde julio de 2024, MySQL usa una doble vía de publicación. Las versiones Innovation (9.x) traen novedades cada trimestre, pero tienen una ventana de soporte corta. Bien para pruebas, mal para producción. Las versiones LTS (8.4, la futura 9.7) tienen 5 años de soporte premier más 3 años de soporte extendido. Usa siempre LTS para cualquier cosa que almacene datos que te importen.
Decisiones clave en este archivo compose:
wordpress:php8.3en lugar dewordpress:latest: fija PHP en 8.3, que es la versión recomendada por WordPress. La etiquetalatesttambién incluye PHP 8.3 a mediados de 2025, pero fijar la etiqueta evita sorpresas cuando cambie el valor por defecto.mysql:8.4: la versión LTS actual, con soporte hasta 2032.redis:7-alpine: versión fijada, basada en Alpine para una imagen más pequeña (~30MB frente a ~130MB).tiredofit/db-backup:4.1: versión mayor fijada. Esta imagen está migrando anfrastack/container-db-backup. La antigua sigue funcionando bien, pero estate atento a la nueva versión.COMPRESSION: ZSTD: el nuevo valor por defecto en db-backup, con compresión y descompresión más rápidas que GZ.FS_METHOD: direct: indica a WordPress que escriba archivos directamente en lugar de usar FTP, que no funciona en Docker.- Health checks: MySQL tiene una comprobación
mysqladmin pingy WordPress una comprobación concurl. Eldepends_on: condition: service_healthyhace que WordPress espere a que MySQL esté realmente listo, no solo iniciado. - Límites de recursos: evitan que cada contenedor se coma toda tu RAM. Ajústalos según el tamaño de tu VPS.
Para producción, considera los secretos de Docker Compose en lugar de archivos .env. Son más seguros y no dejan credenciales en el historial del shell ni en los listados de procesos.
4. Configura el archivo .env y las claves de seguridad
Crea un archivo .env en el mismo directorio que tu compose.yaml:
DB_NAME='wordpress'
DB_USER='wp'
DB_PASSWORD='use-a-strong-random-password-here'
DB_ROOT_PASSWORD=another-strong-random-passwordAhora añade las sales de seguridad de WordPress. Son 8 claves criptográficas que WordPress usa para cifrar cookies y tokens de autenticación. La imagen oficial de Docker genera hashes SHA1 aleatorios únicos a partir de los valores que proporciones. Es una mejora de seguridad gratuita.
Mejora de seguridad gratuita
Añadir las sales de seguridad de WordPress no cuesta nada y hace que el secuestro de sesiones sea significativamente más difícil. La imagen de Docker lee estas variables de entorno y escribe las constantes define() correspondientes en wp-config.php en el primer arranque.
Añade esto a tu archivo .env:
WORDPRESS_AUTH_KEY='put-unique-phrase-here'
WORDPRESS_SECURE_AUTH_KEY='put-unique-phrase-here'
WORDPRESS_LOGGED_IN_KEY='put-unique-phrase-here'
WORDPRESS_NONCE_KEY='put-unique-phrase-here'
WORDPRESS_AUTH_SALT='put-unique-phrase-here'
WORDPRESS_SECURE_AUTH_SALT='put-unique-phrase-here'
WORDPRESS_LOGGED_IN_SALT='put-unique-phrase-here'
WORDPRESS_NONCE_SALT='put-unique-phrase-here'Genera valores aleatorios reales con el generador oficial de sales de WordPress:
Generar sales de WP →Sustituye cada put-unique-phrase-here por los valores generados. No los reutilices entre instalaciones.
Si usas Dockge, puedes añadirlas como variables de entorno en la configuración del stack en lugar de un archivo .env. Para configuraciones de producción, mira los secretos de Docker Compose o las variantes de variables de entorno _FILE que admite la imagen de WordPress (p. ej., WORDPRESS_DB_PASSWORD_FILE=/run/secrets/wp-db-password).
5. Arranca WordPress en Docker
Si usas Dockge, guarda el archivo compose y haz clic en Start. Si no:
docker compose up -dVigila los logs para detectar errores de arranque:
docker compose logs -fEspera unos 30 segundos y comprueba que todos los contenedores están sanos:
docker compose psDeberías ver todos los servicios con estado Up. Si incluiste health checks, el servicio wp-db debería mostrar (healthy) tras unos segundos, y el servicio wp alrededor de los 30 segundos.
El orden de arranque importa
El depends_on: condition: service_healthy del servicio wp hace que WordPress no arranque hasta que MySQL supere su health check. Esto evita la típica condición de carrera del “Error establishing a database connection” que se produce con un depends_on simple.
Si algo falla:
- Puerto 5010 o 5011 ya en uso:
lsof -i :5010para ver qué lo ocupa, luego cambia el mapeo de puertos - El health check de MySQL sigue fallando:
docker compose logs wp-db(busca errores de autenticación o de configuración) - WordPress no puede conectar a la base de datos: verifica que los valores de
.envcoinciden entre los servicioswpywp-db
6. Configura Cloudflare Tunnels para SSL
Cloudflare Tunnels te da SSL y protección DDoS sin abrir puertos en tu firewall ni gestionar certificados.
Ruta actualizada del panel
La ruta del panel de Cloudflare ha cambiado. Ve a Zero Trust → Networks → Tunnels, no a la antigua ruta “Access → Tunnels” que referencian las guías más viejas.
En el panel de Cloudflare Zero Trust:
- Ve a Zero Trust → Networks → Tunnels
- Selecciona tu túnel (o crea uno con
cloudflared) - Añade un hostname que mapee tu dominio a
http://localhost:5010 - Guarda — Cloudflare gestiona el SSL automáticamente

Puedes añadir un segundo hostname para phpMyAdmin en un subdominio (p. ej., pma.yourdomain.com) apuntando a http://localhost:5011. Recomiendo esto antes que exponer el puerto 5011 directamente.
Verifica que el túnel funciona:
curl -I https://yourdomain.comDeberías obtener una respuesta 200 o 301 con las cabeceras cf-ray y server: cloudflare.
¿502 Bad Gateway? El contenedor de WordPress no está corriendo o el mapeo de puertos es incorrecto. Ejecuta docker compose ps y comprueba que el servicio wp está Up.
También puedes usar Traefik v3 como proxy inverso si prefieres gestionar el SSL tú mismo, o CloudPanel con Dockge para un enfoque de proxy inverso diferente.
7. Completa la instalación de WordPress
Abre tu dominio en el navegador (o http://your-server-ip:5010 si aún no has configurado Cloudflare). Verás el asistente de instalación de WordPress:

Elige tu idioma, crea una cuenta de administrador y ya estás dentro. Después, configura los enlaces permanentes en Ajustes → Enlaces permanentes (yo uso «Nombre de la entrada» en la mayoría de los sitios) y empieza a añadir temas y plugins.
8. Personaliza la configuración de PHP para WordPress en Docker
Edita ./config/wp_php.ini para ajustar PHP para WordPress:
file_uploads = On
memory_limit = 256M
upload_max_filesize = 64M
post_max_size = 64M
max_execution_time = 300
max_input_time = 1000Sube memory_limit a 512M y upload_max_filesize a 128M si usas WooCommerce o subes archivos multimedia grandes.
Tras editarlo, reinicia el contenedor de WordPress:
docker compose restart wpVersión de PHP en la imagen de WordPress
La imagen wordpress:latest incluye PHP 8.2 a mediados de 2025. Si has seguido esta guía, estás usando wordpress:php8.3, que es el mínimo recomendado por WordPress. La etiqueta wordpress:php8.4 también está disponible y es totalmente compatible con WordPress 6.7+. Comprueba tu versión con: docker exec wp php -v
Gestión y copias de seguridad de la base de datos
9. Accede a phpMyAdmin para la gestión de la base de datos
Accede a phpMyAdmin en http://your-server-ip:5011 (o mediante un subdominio del túnel de Cloudflare). Inicia sesión con las credenciales de la base de datos de tu archivo .env — los valores DB_USER y DB_PASSWORD.
El UPLOAD_LIMIT: 100M del archivo compose te permite importar volcados de base de datos más grandes desde la interfaz de phpMyAdmin.
No expongas phpMyAdmin públicamente
phpMyAdmin da acceso total a tu base de datos. En producción, cierra el puerto 5011 con el firewall y accede solo a través de un Cloudflare Tunnel con una política de Access, o usa un túnel SSH: ssh -L 5011:localhost:5011 your-server-ip.
10. Verifica las copias de seguridad automáticas de la base de datos
El contenedor wp-db-backup se ejecuta según la programación definida por DB_BACKUP_INTERVAL: 720 (cada 12 horas) y limpia las copias de seguridad con más de DB_CLEANUP_TIME: 72000 minutos de antigüedad (~50 días).
Revisa el directorio de copias de seguridad:
ls -ltr ./backups/Deberías ver archivos como:
-rw------- 1 10000 10000 495 Jul 17 09:26 mysql_wordpress_wp-db_20250717-092619.sql.zst
-rw------- 1 10000 10000 87 Jul 17 09:26 mysql_wordpress_wp-db_20250717-092619.sql.zst.sha1
lrwxrwxrwx 1 10000 10000 44 Jul 17 09:26 latest-mysql_wordpress_wp-db -> mysql_wordpress_wp-db_20250717-092619.sql.zstMigración de la imagen de backups
La imagen tiredofit/db-backup está migrando a nfrastack/container-db-backup. La imagen actual (fijada en 4.1) sigue funcionando bien. Estate atento a la nueva versión si configuras esto después de mediados de 2026. La extensión de compresión cambió de .sql.gz a .sql.zst (ZSTD es más rápido que GZ).
Para copias de seguridad completas del sitio (archivos + base de datos), combina esto con un plugin de backups de WordPress — consulta los mejores plugins gratuitos de copias de seguridad para WordPress para ver opciones que también cubren temas, plugins y archivos subidos.
11. Cómo restaurar una copia de seguridad de la base de datos
Prueba tus restauraciones
Una copia de seguridad que no puedes restaurar no es una copia de seguridad. Ejecuta este procedimiento al menos una vez después de la configuración inicial para asegurarte de que funciona.
Para restaurar desde una copia comprimida:
# Para backups comprimidos con ZSTD (nuevo valor por defecto)
zstd -d ./backups/latest-mysql_wordpress_wp-db -c | docker exec -i wp-db mysql -u "${DB_USER}" -p"${DB_PASSWORD}" "${DB_NAME}"
# Para backups comprimidos con GZ (si no has actualizado la compresión)
zcat ./backups/latest-mysql_wordpress_wp-db | docker exec -i wp-db mysql -u "${DB_USER}" -p"${DB_PASSWORD}" "${DB_NAME}"Para verificar la integridad de la copia antes de restaurar, comprueba el archivo SHA1 adjunto:
cd ./backups
sha1sum -c mysql_wordpress_wp-db_20250717-092619.sql.zst.sha1Tras restaurar, abre la administración de WordPress y confirma que tus entradas y páginas están presentes.
Errores comunes:
- “Access denied” — credenciales incorrectas o faltan comillas alrededor de la contraseña
- “Unknown database” — el
DB_NAMEdel comando de restauración no coincide con la copia - “ERROR 2006 (HY000)” — MySQL server has gone away, el volcado es demasiado grande; aumenta
max_allowed_packeten la configuración de MySQL
Mejoras de rendimiento opcionales
12. Añade Redis Object Cache a WordPress en Docker
Redis requiere una extensión de PHP
La imagen oficial de WordPress para Docker NO incluye la extensión Redis de PHP. Añadir el servicio redis-wp a tu archivo compose no es suficiente — también debes instalar la extensión phpredis o usar la librería Predis de PHP puro. Sin esto, el plugin Redis Object Cache mostrará “Not connected”.
Hay tres formas de añadir soporte para Redis. Recomiendo el enfoque del Dockerfile personalizado — es el más limpio.
Dockerfile personalizado (recomendado)
Crea un Dockerfile en el mismo directorio que tu compose.yaml:
FROM wordpress:php8.3
RUN pecl install redis && docker-php-ext-enable redisLuego cambia el servicio wp de tu archivo compose de image: wordpress:php8.3 a:
wp:
build:
context: .
dockerfile: DockerfileEl WORDPRESS_CONFIG_EXTRA del compose del stack completo (pestaña 2 del paso 3) ya incluye los defines de host y puerto de Redis. Reconstruye con:
docker compose up -d --buildImagen preconstruida
Usa fazalfarhan01/wordpress-redis — una imagen de la comunidad que incluye la extensión de Redis:
wp:
image: fazalfarhan01/wordpress-redis:php8.3No hace falta Dockerfile, pero estás confiando en una imagen de terceros. Revisa la página de Docker Hub para las últimas etiquetas.
Predis (sin build)
El plugin Redis Object Cache admite la librería Predis de PHP puro como alternativa a la extensión phpredis. Instálala vía Composer dentro del contenedor:
docker exec wp bash -c "curl -sS https://getcomposer.org/installer | php && php composer.phar require predis/predis"Luego, en los ajustes del plugin, cambia el cliente a “Predis”. Es más lento que la extensión nativa, pero no requiere personalizar la imagen.
Tras activar el plugin Redis Object Cache (de Till Krüss), ve a Ajustes → Redis en la administración de WordPress y haz clic en Enable Object Cache. El estado debería mostrar “Connected”.
¿No conecta? Comprueba que:
- El contenedor de Redis está corriendo:
docker compose ps redis-wp - El host/puerto en
WP_REDIS_HOST/WP_REDIS_PORTcoinciden con el nombre del servicio y el puerto - La extensión PHP de Redis está realmente instalada:
docker exec wp php -m | grep redis
Para máximo rendimiento, combina el caché de Redis con Varnish y Cloudflare — consulta Cómo acelerar WordPress con Cloudflare, Varnish y Redis.
Endurecimiento para producción
13. Health checks de Docker para producción
Los health checks ya están configurados en el archivo compose del paso 3. Esto es lo que hacen:
- MySQL (
wp-db): ejecutamysqladmin pingcada 10 segundos. Tras 5 comprobaciones fallidas, el contenedor se marca como unhealthy. Esto evita que WordPress conecte antes de que MySQL esté listo. - WordPress (
wp): hace curl a la página de instalación cada 30 segundos. Confirma que el servidor web y PHP responden.
Monitoriza el estado de salud:
docker compose psTodos los servicios deberían mostrar (healthy) en la columna STATUS. Si un servicio está (unhealthy), revisa sus logs: docker compose logs <nombre-del-servicio>.
La política restart: unless-stopped hace que los contenedores se reinicien automáticamente ante un fallo o un reinicio del servidor, pero permanezcan detenidos si los paras manualmente.
14. Contenedor WP-CLI para el mantenimiento de WordPress
La imagen oficial wordpress:cli te da acceso por línea de comandos a WordPress sin instalar nada extra. Ejecuta comandos contra tu contenedor de WordPress existente:
docker run -it --rm \
--volumes-from wp \
--network container:wp \
wordpress:cli \
wp plugin listComandos comunes de WP-CLI
Gestión de plugins:
# Lista los plugins instalados
docker run -it --rm --volumes-from wp --network container:wp wordpress:cli wp plugin list
# Actualiza todos los plugins
docker run -it --rm --volumes-from wp --network container:wp wordpress:cli wp plugin update --all
# Desactiva un plugin
docker run -it --rm --volumes-from wp --network container:wp wordpress:cli wp plugin deactivate plugin-nameGestión de usuarios:
# Lista los usuarios
docker run -it --rm --volumes-from wp --network container:wp wordpress:cli wp user list
# Restablece la contraseña de un usuario
docker run -it --rm --volumes-from wp --network container:wp wordpress:cli wp user update admin --user_pass=newpasswordOperaciones con la base de datos:
# Exporta la base de datos
docker run -it --rm --volumes-from wp --network container:wp wordpress:cli wp db export /var/www/html/backup.sql
# Buscar y reemplazar URLs (útil tras cambios de dominio)
docker run -it --rm --volumes-from wp --network container:wp wordpress:cli wp search-replace 'http://old-domain.com' 'https://new-domain.com' --skip-columns=guidActualizaciones del núcleo:
# Comprueba la versión actual
docker run -it --rm --volumes-from wp --network container:wp wordpress:cli wp core version
# Actualiza el núcleo de WordPress
docker run -it --rm --volumes-from wp --network container:wp wordpress:cli wp core updateEl define FS_METHOD: direct en WORDPRESS_CONFIG_EXTRA es necesario para que WP-CLI (y el propio WordPress) pueda escribir archivos en Docker sin FTP.
¿“Error: This does not seem to be a WordPress install”? Te falta --volumes-from wp o el nombre del contenedor es incorrecto. Compruébalo con docker compose ps.
15. Lista de verificación de endurecimiento de seguridad
- MySQL 8.4 LTS fijado (no
mysql:latest) - Sales de seguridad de WordPress definidas en
.env FS_METHOD: directenWORDPRESS_CONFIG_EXTRA- Puerto 5011 (phpMyAdmin) cerrado con firewall — acceso solo vía Cloudflare Tunnel o SSH
- Política de Cloudflare Access delante del subdominio de phpMyAdmin
- Las contraseñas de la base de datos son cadenas aleatorias fuertes, no palabras de diccionario
- Prefijo de tablas de WordPress cambiado de
wp_si eres paranoico (se define enWORDPRESS_TABLE_PREFIX) - XML-RPC desactivado si no usas Jetpack (añádelo a
.htaccesso usa un plugin) - Límites de recursos definidos en el archivo compose para evitar contenedores desbocados
- Restauraciones de copias de seguridad probadas regularmente (no solo su creación)
Ya cubierto
La mayoría de estos puntos ya quedan cubiertos siguiendo los pasos 3-6 de esta guía. Esta lista está aquí como referencia y para cuando audites tu configuración más adelante.
Monitoriza tu servidor para detectar anomalías pronto — consulta Cómo monitorizar recursos del servidor y Docker para configurar la monitorización de recursos con herramientas como Beszel o Netdata.
16. MariaDB como alternativa a MySQL
MariaDB es totalmente compatible con WordPress, tiene una huella de memoria más ligera y está recomendado junto a MySQL en el WordPress Hosting Handbook. Muchos autoalojados lo prefieren.
Para cambiar, modifica una línea de tu archivo compose:
# Sustituye esto:
wp-db:
image: mysql:8.4
# Por esto:
wp-db:
image: mariadb:11.4Mismas variables de entorno, mismo todo
MariaDB usa las mismas variables de entorno que MySQL (MYSQL_ROOT_PASSWORD, MYSQL_DATABASE, etc.). No hace falta ningún otro cambio — solo cambia la etiqueta de la imagen. MariaDB 11.4 LTS tiene soporte hasta mayo de 2029.
17. Actualizar WordPress en Docker
Dos estrategias según el control que quieras:
Estrategia 1: Autogestionada (por defecto)
WordPress se auto-actualiza dentro del volumen. Es el comportamiento por defecto — WordPress busca actualizaciones y las aplica sin que toques Docker. Simple, pero tu infraestructura no es inmutable.
Estrategia 2: Versión fijada (recomendada para producción)
Fija la versión de la imagen de WordPress en tu archivo compose, desactiva las auto-actualizaciones y controla cuándo actualizas:
wp:
image: wordpress:php8.3:6.8Añade a WORDPRESS_CONFIG_EXTRA:
define('WP_AUTO_UPDATE_CORE', false);Cuando estés listo para actualizar, cambia la etiqueta de versión y redespliega:
docker compose pull
docker compose up -dTu volumen wp-app conserva todos los archivos de WordPress, temas, plugins y subidas. El contenedor es solo el runtime — tus datos viven en el host.
¿“Another update is in progress”? Es un transient atascado. Límpialo con WP-CLI:
docker run -it --rm --volumes-from wp --network container:wp wordpress:cli wp option delete core_updater.lock18. Próximos pasos
Tu stack de WordPress en Docker está en marcha. Algunas cosas para hacer desde aquí:
- Cierra los puertos del firewall — si usas Cloudflare Tunnels, bloquea los puertos 5010 y 5011 a nivel de firewall para que solo el túnel pueda alcanzarlos. El acceso es solo a través de tu dominio.
- Configura monitorización — monitoriza los recursos de tu servidor y de Docker para detectar picos de CPU, discos llenos y reinicios de contenedores antes de que se conviertan en problemas.
- Explora paneles autoalojados — si quieres una interfaz de gestión más amplia, mira los mejores paneles de servidor autoalojados para opciones más allá de Dockge.
- Domina los comandos de Docker — guarda estos comandos esenciales de Docker para diagnosticar contenedores, liberar espacio en disco y gestionar imágenes.
- Instala temas y plugins — WordPress está listo para tu contenido. Empieza con un tema ligero y añade solo los plugins que necesites.
Conclusión
Ahora tienes un stack de WordPress listo para producción corriendo en Docker con MySQL 8.4 LTS (con soporte hasta 2032), phpMyAdmin para la gestión de la base de datos, copias de seguridad automáticas con compresión ZSTD, caché de objetos Redis opcional y SSL a través de Cloudflare Tunnels — todo desde un único archivo compose.yaml.
El siguiente paso más importante es probar tus copias de seguridad. Una copia que nunca has restaurado es una apuesta, no una estrategia. Ejecuta el procedimiento de restauración del paso 11 al menos una vez, y luego pon un recordatorio en el calendario para probarlo cada trimestre.
Si algo se rompe, docker compose logs es tu mejor amigo. La mayoría de los problemas se reducen a conflictos de puertos, errores de permisos o que MySQL no esté listo cuando WordPress intenta conectar — todo lo cual los health checks de esta configuración están diseñados para detectar.


