Oleksii Siniaiev
Navegación de la página del artículo
Artículos 8 min de lectura March 21, 2026

Docker y WordPress para desarrollo local

En resumen La verdadera ventaja de Docker para WordPress es la paridad y el onboarding: la misma versión de PHP, extensiones y MySQL para todos, arrancados con un comando en lugar de un día…

En esta página

En resumen

  • La verdadera ventaja de Docker para WordPress es la paridad y el onboarding: la misma versión de PHP, extensiones y MySQL para todos, arrancados con un comando en lugar de un día de configuración.
  • Las trampas son específicas de WordPress, no de Docker: DB_HOST es el nombre del servicio, no localhost; WordPress guarda URLs absolutas en la base de datos; y el UID del usuario del contenedor pelea con tus permisos de archivos.
  • Monta tu código con bind-mount; nunca montes vendor/, wp/ ni node_modules; mantén la base de datos en un volumen con nombre para que sobreviva a docker compose down.
  • Esto cubre el flujo de desarrollo local. Para el despliegue en producción con git pull en hosting compartido, mira la guía de Bedrock enlazada al final.

El bug de «en mi máquina funciona» que finalmente me empujó a usar Docker con WordPress fue una extensión de PHP. Un plugin necesitaba intl, estaba en mi Mac y faltaba en la de un compañero, y perdimos una tarde con una pantalla en blanco que solo uno de los dos podía reproducir. Esa es la clase de problema que eliminan los contenedores: no por magia, sino convirtiendo el runtime en un archivo del repositorio en lugar de una propiedad del portátil de alguien.

Este artículo es la mitad de desarrollo local de ejecutar WordPress en Docker: la paridad, el flujo de trabajo y las trampas específicas de WordPress que los tutoriales genéricos de Docker se saltan. El despliegue en producción en hosting compartido es otro problema con sus propias aristas, y tiene su propia guía al final.

Qué arregla realmente Docker en el desarrollo de WordPress

WordPress está inusualmente expuesto a la deriva del entorno. Corre sobre una versión de PHP, un conjunto de extensiones, una versión de MySQL y un servidor web, y un plugin puede depender en silencio de cualquiera de ellos. Los contenedores fijan los cuatro en configuración versionada, así que el stack es idéntico para todos y reproducible en CI.

Los beneficios concretos, en orden de cuánto importan en el día a día:

Onboarding

Un desarrollador nuevo ejecuta un comando y tiene el stack completo, en vez de un README lleno de brew install.

Paridad de versiones

Fija PHP y MySQL para que coincidan con producción, para que «funciona en local» signifique algo.

Estado desechable

Rompes la base de datos, borras el volumen, reimportas. Sin miedo a corromper una instalación local.

Probar plugins

Levanta un stack desechable para probar un plugin o una actualización de PHP sin tocar tu instalación real.

Un stack de desarrollo que de verdad se puede usar

La demo de un solo contenedor que todos te muestran sirve para un vistazo de cinco minutos e es inútil para trabajo real:

Bash
docker run --name wp -p 80:80 -d wordpress:latest

No tiene base de datos persistente, ni acceso al código de tu tema y plugins, ni una forma cómoda de ejecutar WP-CLI. El desarrollo real necesita un stack multiservicio con tu código montado y la base de datos persistida:

Code
services:
  app:
    image: wordpress:php8.3-apache
    ports:
      - "8880:80"
    volumes:
      - ./wp-content:/var/www/html/wp-content
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_NAME: wordpress
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: wordpress
    depends_on:
      - db

  db:
    image: mysql:8.0
    environment:
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: wordpress
      MYSQL_ROOT_PASSWORD: root
    volumes:
      - db_data:/var/lib/mysql

volumes:
  db_data:

Dos decisiones ahí importan más de lo que parece. La versión de PHP está fijada en la etiqueta de la imagen para coincidir con producción —la deriva entre un PHP 8.3 local y un 8.1 de producción es justo el bug que Docker debe prevenir, así que no lo dejes flotando en latest. Y la base de datos vive en el volumen con nombre db_data, que sobrevive a docker compose down; el código es un bind-mount para que tus cambios en el editor estén en vivo.

Las trampas específicas de WordPress

Estos son los problemas que no están en un tutorial genérico de Docker porque son problemas de WordPress que resultan aflorar dentro de Docker.

DB_HOST es el nombre del servicio, no localhost

Dentro de la red de Compose, los contenedores se encuentran por nombre de servicio. El host de la base de datos es db, no localhost ni 127.0.0.1. Ponerlo en localhost hace que WordPress intente un socket que no existe en el contenedor de la aplicación, y obtienes «Error establishing a database connection» sin más detalle.

WordPress guarda URLs absolutas en la base de datos. Las opciones siteurl y home, y las URLs incrustadas en el contenido de las entradas, son cadenas completas http://localhost:8880. Importa un volcado de la base de producción en local y cada enlace, imagen y redirección sigue apuntando a producción. Arréglalo con un search-replace seguro para datos serializados, nunca con un UPDATE de SQL crudo que corrompe los datos serializados:

Bash
docker compose exec app wp search-replace 'https://example.com' 'http://localhost:8880' --all-tables --skip-columns=guid

La propiedad de los archivos pelea contigo. La imagen de Apache corre como www-data (UID 33). Los archivos que escribe —subidas, CSS generado, instalaciones de plugins— acaban propiedad del UID 33 en tu host, y los archivos que creas como tu usuario del host pueden no ser escribibles por el contenedor. En Linux sobre todo esto aparece como fallos de subida. La solución limpia es alinear el usuario del contenedor con el UID de tu host, o mantener los directorios escribibles (uploads) en un volumen que posee el contenedor mientras tu código sigue siendo un bind-mount simple.

Ejecuta WP-CLI a través del contenedor. WP-CLI necesita el mismo PHP y la misma base de datos que usa el sitio, así que ejecútalo dentro del contenedor de la aplicación, no en tu host:

Bash
docker compose exec app wp plugin list
docker compose exec app wp db export backup.sql

Qué no montar

Los bind-mounts son cómo tu código llega al contenedor, pero montar los directorios equivocados es la forma más común de ralentizar el stack o romperlo del todo.

Montar vendor/ o wp/

Los directorios gestionados por Composer deben vivir en la imagen, no ser sobrescritos por archivos del host. Montarlos invita a desajustes de versión.

Montar node_modules

Las arquitecturas del host y del contenedor difieren. Compila los assets dentro del contenedor o mantén node_modules fuera del montaje.

Versión de PHP flotante

Usar wordpress:latest en vez de una etiqueta de PHP fijada reintroduce justo la deriva que Docker debe eliminar.

Base de datos en un bind-mount

Los datos de MySQL en un bind-mount del host son lentos y propensos a problemas de permisos. Usa un volumen con nombre.

Commitear las subidas

wp-content/uploads son datos de usuario, no código. Mantenlos fuera de git y fuera de la imagen.

localhost como host de la base de datos

Los contenedores alcanzan la base de datos por nombre de servicio. localhost apunta el contenedor de la aplicación a sí mismo.

Servicios opcionales que vale la pena añadir

Una vez que el stack base funciona, unos pocos servicios extra mejoran notablemente el desarrollo local. Un capturador de correo como Mailpit intercepta el correo saliente para que los restablecimientos de contraseña y las notificaciones caigan en una interfaz web en lugar del vacío. Redis te da una caché de objetos real que coincide con el comportamiento de producción. Un contenedor dedicado de worker de cola te deja ejercitar los jobs como se ejecutan de verdad. Cada uno son unas pocas líneas en el mismo archivo de Compose, versionadas junto al resto.

FAQ

¿Por qué WordPress muestra «Error establishing a database connection» en Docker?
Casi siempre es el host de la base de datos. Dentro de Docker Compose, los servicios se alcanzan por nombre de servicio, así que DB_HOST debe ser el nombre del servicio de base de datos (db), no localhost. Una causa secundaria es que el contenedor de la aplicación arranca antes de que MySQL esté listo; depends_on más un reintento de conexión lo resuelve.
¿Por qué las URLs de mis imágenes están mal tras importar una base de producción?
WordPress guarda URLs absolutas en la base de datos. Ejecuta un wp search-replace seguro para datos serializados desde el dominio de producción a tu URL local en todas las tablas, omitiendo la columna guid. Nunca lo hagas con un UPDATE de SQL crudo, que corrompe los datos serializados de las opciones.
¿Debo montar vendor/ y wp/ como volúmenes?
No. Los directorios gestionados por Composer pertenecen a la imagen para que las versiones sean consistentes. Monta con bind-mount solo tu propio código (temas, plugins, mu-plugins). Montar directorios gestionados es una fuente común de desajustes de «funciona en CI pero no en local».
¿Cómo ejecuto WP-CLI en un sitio WordPress dockerizado?
Ejecútalo dentro del contenedor de la aplicación con docker compose exec app wp <command> para que use la misma versión de PHP y conexión a la base de datos que el sitio. Un envoltorio de Makefile alrededor de ese comando ahorra mucho tecleo.

Artículos relacionados

Actualizado: June 17, 2026

Compartir este artículo

LinkedIn X Email

Contacto

¿Trabajas en algo parecido? Hablemos.

Estoy abierto a hablar de arquitectura, Laravel, WordPress, rendimiento y problemas prácticos de implementación.

Enviar un mensaje Ver proyectos

Explorar más

Artículos

June 14, 2026

Parte 3. Un mes con un diario de IA: cómo encontrar patrones entre sueño, estrés y entrenamientos

Cómo analizar un diario de IA tras el primer mes: correcciones de transcripción, reflexión…
Artículos

June 14, 2026

Parte 2. Hermes Agent + DeepSeek en Ubuntu: guía completa de un diario con IA en Telegram

Guía paso a paso: Hermes Agent y DeepSeek en Ubuntu, bot privado de Telegram,…
Artículos

June 14, 2026

Parte 1. Cómo convertí un viejo portátil gaming en un diario de bienestar con IA

Cómo un viejo Xiaomi Mi Gaming Laptop se convirtió en servidor doméstico con IA:…