Docker para desarrollo WordPress + Next.js: Entorno local perfecto

Configurar un ambiente de desarrollo local para proyectos WordPress headless puede ser una pesadilla: versiones de PHP conflictivas, bases de datos que no coinciden, Node.js incompatible entre proyectos. Docker resuelve todos estos problemas creando entornos aislados, reproducibles y que funcionan idénticamente en cualquier máquina. Esta guía te muestra cómo crear el setup perfecto.
Por qué Docker es esencial para WordPress headless
Desarrollar WordPress headless sin Docker significa lidiar con múltiples desafíos simultáneamente:
Configuración inconsistente entre desarrolladores
Un desarrollador tiene PHP 8.1, otro 8.3. Uno usa MySQL 5.7, otro MySQL 8.0. El frontend requiere Node 18, pero tienes Node 20 instalado globalmente. Cada diferencia puede causar bugs que aparecen solo en ciertas máquinas, llevando a las frustrantes conversaciones de "en mi máquina funciona".
Onboarding lento de nuevos desarrolladores
Sin Docker, incorporar un nuevo desarrollador significa días instalando XAMPP/MAMP, configurando virtual hosts, importando bases de datos, ajustando permisos de archivos, instalando dependencias. Con Docker, clonan el repo, ejecutan docker-compose up, y en 5 minutos tienen todo funcionando.
Conflictos entre proyectos
Trabajas en proyecto A que necesita PHP 7.4, luego cambias a proyecto B que requiere PHP 8.2. Sin contenedores, cambiar entre proyectos significa reconfigurar tu ambiente cada vez, o peor, mantener múltiples instalaciones locales de Apache/Nginx y PHP.
Diferencias entre desarrollo y producción
Tu servidor de producción usa Nginx y PHP-FPM, pero localmente usas Apache con mod_php. Estas diferencias causan comportamientos inesperados al deployar, especialmente con rewrites, headers y caching.
Docker elimina todos estos problemas encapsulando cada componente en contenedores aislados que son idénticos para todos los desarrolladores y muy similares a producción.
Arquitectura del entorno Docker
Un setup completo de WordPress headless con Next.js requiere múltiples servicios trabajando juntos:
WordPress backend
- Contenedor de PHP-FPM con WordPress instalado
- Servidor web Nginx sirviendo WordPress
- Base de datos MySQL o MariaDB
- PHPMyAdmin para gestión de BD (opcional pero útil)
- WP-CLI para operaciones de línea de comandos
- MailHog para capturar emails en desarrollo
Next.js frontend
- Contenedor Node.js con Next.js
- Hot reload configurado para desarrollo rápido
- Volúmenes montados para edición en tiempo real
- Variables de entorno para conectar con WordPress
Servicios compartidos
- Redis para caching (opcional pero recomendado)
- Network compartida para comunicación entre contenedores
- Volúmenes para persistencia de datos
Todos estos servicios se orquestan mediante Docker Compose, que define cómo se conectan y configuran.
Estructura de archivos del proyecto
Una estructura bien organizada facilita el mantenimiento y escalabilidad del proyecto. La configuración recomendada es:
proyecto-headless/
├── docker/
│ ├── wordpress/
│ │ ├── Dockerfile
│ │ └── php.ini
│ ├── nginx/
│ │ └── wordpress.conf
│ └── nextjs/
│ └── Dockerfile
├── wordpress/
│ ├── wp-content/
│ │ ├── plugins/
│ │ └── themes/
│ └── wp-config.php
├── frontend/
│ ├── pages/
│ ├── components/
│ ├── package.json
│ └── next.config.js
├── docker-compose.yml
├── .env.example
└── README.md
Esta estructura separa claramente la configuración Docker, el código de WordPress, y el código del frontend, facilitando que diferentes desarrolladores trabajen en áreas específicas sin conflictos.
Configuración de WordPress con Docker
El contenedor de WordPress necesita configuración específica para desarrollo óptimo.
Dockerfile personalizado para WordPress
En lugar de usar la imagen oficial de WordPress directamente, creamos un Dockerfile personalizado que instala herramientas adicionales necesarias para desarrollo: WP-CLI para operaciones de consola, Composer para gestión de dependencias PHP, extensiones PHP específicas como GD para manipulación de imágenes y opcache desactivado para desarrollo.
La clave es usar multi-stage builds y capas de cache inteligentes para que rebuilds sean rápidos. Instalas dependencias del sistema primero (cambian raramente), luego PHP y extensiones, y finalmente WordPress y tu código (cambian frecuentemente).
Configuración de Nginx
Nginx sirve como proxy reverso delante de PHP-FPM. La configuración debe manejar rewrites de WordPress correctamente, servir archivos estáticos eficientemente, y proxy requests PHP a PHP-FPM.
Para desarrollo, configuras logs verbose y desactivas caching agresivo. En producción, ajustas buffers, timeout y caching para máxima performance. Mantener estas configuraciones en archivos separados te permite optimizar para cada contexto.
Base de datos persistente
Un error común es no persistir la base de datos, resultando en pérdida de datos cada vez que recreas los contenedores. Usas volumes de Docker para mantener los datos de MySQL independientes del ciclo de vida del contenedor.
Además, configuras un script de inicialización que corre solo la primera vez que se crea la base de datos, importando datos de seed para tener contenido de prueba inmediatamente disponible.
Variables de entorno
WordPress requiere múltiples configuraciones: credenciales de base de datos, sales keys, debug mode, etc. En lugar de hardcodear estos valores, usas variables de entorno definidas en un archivo .env.
Esto permite que cada desarrollador tenga su propia configuración local sin conflictos en git, y facilita el cambio entre entornos simplemente cambiando el archivo de entorno.
Configuración de Next.js con Docker
El frontend Next.js tiene requisitos diferentes a WordPress, especialmente para desarrollo con hot reload.
Dockerfile optimizado para desarrollo
Para desarrollo, quieres que los cambios en código se reflejen inmediatamente. Esto requiere montar tu código local como volumen dentro del contenedor, en lugar de copiarlo durante el build.
El truco está en instalar node_modules dentro del contenedor, no en tu máquina local, para evitar conflictos entre diferentes sistemas operativos (especialmente Windows vs Linux). Usas un volumen anónimo específicamente para node_modules que sobrescribe el volumen del código.
Hot Module Replacement configurado
Next.js soporta HMR nativamente, pero dentro de Docker requiere configuración adicional. Necesitas asegurar que el watcher de archivos funcione correctamente, lo cual puede ser problemático en algunos sistemas de archivos y configuraciones de Docker.
La solución involucra configurar polling en lugar de eventos de filesystem, ajustar el webpack config de Next.js, y exponer los puertos correctos tanto para la aplicación como para el WebSocket de HMR.
Conexión con la API de WordPress
Next.js necesita comunicarse con WordPress para obtener contenido. En Docker, los contenedores se comunican mediante nombres de servicio en lugar de localhost.
Configuras variables de entorno diferentes para el servidor (donde Next.js hace requests internos a WordPress) y el cliente (donde el navegador hace requests). Esto permite que SSR y CSR funcionen correctamente con URLs apropiadas para cada contexto.
Docker Compose: Orquestando todo el stack
Docker Compose es donde defines todos los servicios y cómo interactúan. Un archivo docker-compose.yml bien estructurado es la clave para un ambiente reproducible.
Servicios básicos
El archivo define cada servicio con su imagen, variables de entorno, volúmenes y networks. Los servicios dependen unos de otros: WordPress depende de MySQL, Next.js depende de WordPress estar disponible.
Usas health checks para asegurar que servicios críticos estén completamente inicializados antes de que servicios dependientes intenten conectarse. Esto evita errores de "connection refused" durante el startup inicial.
Networks personalizadas
Por defecto, Docker Compose crea una network donde todos los servicios pueden comunicarse. Para mejor organización y seguridad, defines networks específicas:
backend-networkconecta WordPress con MySQLfrontend-networkconecta Next.js con WordPresspublic-networkexpone servicios al host
Esto permite controlar precisamente qué servicios pueden comunicarse, mejorando la seguridad incluso en desarrollo.
Volúmenes estratégicos
Los volúmenes determinan qué datos persisten y cómo se comparten entre el host y contenedores. Configuras:
- Volúmenes nombrados para datos críticos (MySQL data)
- Bind mounts para código que editas activamente
- Volúmenes anónimos para caches y temporales
La estrategia correcta de volúmenes hace la diferencia entre un ambiente ágil donde cambios se reflejan instantáneamente, y uno frustrante donde necesitas rebuild constantes.
Configuración específica por servicio
Cada servicio tiene configuración específica: límites de memoria, restart policies, logging drivers, etc. Para desarrollo local, configuras restart: "no" para evitar que contenedores se reinicien automáticamente cuando fallan, facilitando el debugging.
También ajustas logging para evitar que logs llenen tu disco, especialmente con servicios verbose como Nginx o MySQL en modo debug.
Workflows de desarrollo diario
Una vez configurado, tu flujo de trabajo diario es extremadamente eficiente.
Iniciar el ambiente
Un simple comando levanta todo el stack: docker-compose up -d. El flag -d (detached) corre los contenedores en background, liberando tu terminal.
En el primer inicio, Docker descarga imágenes, construye contenedores custom, crea volúmenes y networks, y ejecuta scripts de inicialización. Esto puede tomar 5-10 minutos. Inicios subsecuentes son instantáneos porque todo está cacheado.
Hacer cambios en el código
Editas archivos en tu editor favorito como siempre. Los cambios en PHP se reflejan inmediatamente porque WordPress corre en modo development sin opcode cache. Los cambios en JavaScript triggean hot reload de Next.js, actualizando el navegador automáticamente en 1-2 segundos.
No necesitas reiniciar contenedores ni reconstruir nada para cambios normales de código. Solo para cambios en la configuración de Docker (Dockerfiles, docker-compose.yml) necesitas rebuild.
Ejecutar comandos dentro de contenedores
Frecuentemente necesitas ejecutar comandos específicos: instalar un plugin de WordPress, correr migrations, añadir paquetes npm. Docker facilita esto con docker-compose exec:
docker-compose exec wordpress wp plugin install <plugin>docker-compose exec wordpress composer require <package>docker-compose exec frontend npm install <package>docker-compose exec db mysql -u root -p <database>
Estos comandos ejecutan dentro del contexto correcto con las dependencias y configuración apropiadas, sin contaminar tu máquina local.
Debugging y logs
Ver logs de todos los servicios: docker-compose logs -f. Ver logs de un servicio específico: docker-compose logs -f wordpress. El flag -f (follow) mantiene el stream abierto mostrando nuevos logs en tiempo real.
Para debugging más profundo, accedes a un shell dentro del contenedor: docker-compose exec wordpress bash. Desde ahí puedes inspeccionar archivos, verificar procesos, revisar configuración, exactamente como en un servidor remoto.
Optimizaciones de performance

Docker puede ser lento en ciertos sistemas, especialmente macOS y Windows. Estas optimizaciones marcan una gran diferencia.
Bind mounts optimizados
Los bind mounts (montar carpetas del host en contenedores) son convenientes pero pueden ser lentos, especialmente con muchos archivos pequeños como node_modules.
En macOS, usa el flag :cached en volúmenes para mejor performance. Considera estrategias como delegated consistency para escribir en el contenedor y sincronizar al host de forma asíncrona.
Para node_modules específicamente, la mejor práctica es NO montarlos desde el host. En su lugar, instálalos dentro del contenedor usando un volumen anónimo que sobrescribe el mount del código.
Layer caching inteligente
Dockerfiles se buildean en capas, y cada capa se cachea. La clave para builds rápidos es ordenar comandos de menos a más cambiantes:
- Instalar dependencias del sistema (casi nunca cambia)
- Copiar archivos de dependencias (package.json, composer.json)
- Instalar dependencias de la aplicación (cambia ocasionalmente)
- Copiar código de la aplicación (cambia frecuentemente)
Con este orden, cambios en tu código no invalidan el cache de instalación de dependencias, acelerando rebuilds dramáticamente.
Multi-stage builds
Para imágenes de producción, usas multi-stage builds que compilan y buildan en una etapa con todas las herramientas necesarias, y luego copian solo los artefactos finales a una imagen mínima.
Esto resulta en imágenes de producción mucho más pequeñas (200MB vs 800MB), más rápidas de deployar, y más seguras al no incluir herramientas de build que podrían ser vectores de ataque.
BuildKit y cache de BuildKit
Habilitar BuildKit (la nueva engine de build de Docker) da mejoras significativas de performance. Se activa con DOCKER_BUILDKIT=1 antes de comandos build.
BuildKit trae paralelización automática de stages independientes, mejor cache management, y la capacidad de montar caches persistentes que sobreviven rebuilds completos.
Datos de desarrollo y seeders
Un ambiente vacío no es útil para desarrollo. Necesitas datos de prueba consistentes.
Database seeders automatizados
Crea scripts SQL que se ejecutan automáticamente la primera vez que inicializas la base de datos. Estos scripts crean usuarios de prueba, posts de ejemplo, configuración de plugins, etc.
Coloca estos scripts en docker/mysql/init/ y Docker los ejecutará en orden alfabético durante la primera inicialización. Esto garantiza que todos los desarrolladores empiecen con exactamente los mismos datos.
Importar dumps de producción
Para proyectos existentes, querrás trabajar con datos reales de producción (sanitizados apropiadamente). Crea un script que descargue el dump de producción, reemplace URLs de producción con localhost, y lo importe a tu base de datos local.
Importante: nunca commits dumps de producción al repositorio, especialmente si contienen información sensible. Úsalos solo localmente y asegúrate de sanitizar datos personales.
Assets y uploads
WordPress almacena uploads en wp-content/uploads/. Para desarrollo, puedes usar imágenes dummy o sincronizar periódicamente desde producción.
Una estrategia eficiente es usar un plugin que sirva imágenes faltantes directamente desde producción, evitando tener que descargar gigabytes de assets. Solo descargas imágenes que realmente editas o necesitas.
Debugging avanzado con Xdebug
Para debugging serio de PHP, necesitas Xdebug configurado correctamente dentro de Docker.
Configuración de Xdebug 3
Xdebug 3 simplificó mucho la configuración comparado con versiones anteriores. En tu Dockerfile de WordPress, instalas la extensión y configuras los settings necesarios para que se conecte de vuelta a tu IDE.
La complicación en Docker es que Xdebug necesita conectarse desde el contenedor (donde corre PHP) a tu IDE (en el host). Esto requiere configuración específica de host y puerto, y potencialmente ajustes de firewall.
Integración con VS Code y PhpStorm
Los IDEs modernos soportan Xdebug nativamente. Configuras un launch configuration que especifica el path mapping entre archivos en el contenedor y archivos en tu máquina.
Una vez configurado, pones breakpoints en tu IDE, inicias debugging, y cuando WordPress procesa un request, el debugger pausa exactamente donde pusiste breakpoints, permitiéndote inspeccionar variables, step through código, y evaluar expresiones.
Performance considerations
Xdebug tiene overhead significativo de performance. Solo actívalo cuando necesitas debuggear activamente, no lo dejes corriendo siempre. Puedes configurar triggers para activar Xdebug solo para requests específicos usando cookies o query parameters.
Gestión de múltiples proyectos
Si trabajas en múltiples proyectos WordPress headless, necesitas estrategias para evitar conflictos.
Ports únicos por proyecto
Cada proyecto debe usar puertos diferentes para evitar conflictos. Proyecto A usa 8001 para WordPress y 3001 para Next.js, proyecto B usa 8002 y 3002, etc.
Define estos puertos en el archivo .env de cada proyecto, haciéndolos fáciles de cambiar sin tocar docker-compose.yml.
Project naming consistente
Docker Compose crea recursos (containers, networks, volumes) prefijados con el nombre del proyecto. Por defecto usa el nombre del directorio, lo cual puede causar conflictos.
Define explícitamente el nombre del proyecto en .env usando COMPOSE_PROJECT_NAME=proyecto-a. Esto garantiza nombres únicos y predecibles.
Resource limits
Con múltiples proyectos corriendo simultáneamente, puedes consumir mucha memoria. Configura límites de recursos en docker-compose.yml para cada servicio, previniendo que un proyecto monopolice recursos.
También considera parar proyectos que no estés usando activamente con docker-compose stop, liberando memoria mientras mantienes volúmenes y configuración intactos.
CI/CD integration
Tu ambiente Docker local debe ser consistente con CI/CD para evitar sorpresas en deployments.
Dockerfile de producción
Mantén Dockerfiles separados para desarrollo y producción. Development incluye herramientas de debugging y configuración laxa. Production es minimalista, optimizado y hardened.
Usa el mismo Dockerfile base para ambos para garantizar consistencia en dependencias y versiones, diferenciándote solo en build arguments y configuración runtime.
Testing en contenedores
Tus tests automatizados deben correr dentro de contenedores Docker, no directamente en el host. Esto garantiza que pruebas pasen consistentemente en CI/CD y localmente.
Crea servicios específicos para testing en docker-compose que levanten el stack con configuración de test, ejecuten la suite completa, y reporten resultados.
Image registry
Para proyectos de equipo, considera pushear imágenes buildeadas a un registry (Docker Hub, GitHub Container Registry, GitLab Registry). Esto permite que desarrolladores descarguen imágenes pre-buildeadas en lugar de construir localmente, acelerando onboarding.
Troubleshooting común
Incluso con buena configuración, surgen problemas. Aquí están los más comunes y sus soluciones.
Contenedores no inician
Revisa logs con docker-compose logs. Típicamente el problema es:
- Puerto ya en uso (cambia el puerto en .env)
- Variable de entorno faltante (revisa .env vs .env.example)
- Dependencia no disponible (health check fallando)
- Permisos de archivos incorrectos (especialmente en Linux)
Performance lenta en macOS/Windows
Docker Desktop en Mac/Windows usa virtualización, lo cual tiene overhead. Mitigaciones:
- Usa
:cachedo:delegateden bind mounts - Excluye directorios pesados como node_modules del bind mount
- Aumenta recursos asignados a Docker Desktop en preferencias
- Considera usar Docker con WSL2 en Windows
Cambios no se reflejan
Si editas código pero no ves cambios:
- Verifica que el volumen esté montado correctamente
- Para PHP, asegura que opcache esté desactivado en desarrollo
- Para Next.js, confirma que el watcher de HMR esté funcionando
- Revisa que no haya caches intermedios (Redis, CDN local)
Database connection refused
Si WordPress no puede conectar a MySQL:
- Espera a que MySQL termine de inicializar (puede tomar 30-60s en primer inicio)
- Verifica que el hostname sea el nombre del servicio, no 'localhost'
- Confirma credenciales en .env coincidan con docker-compose.yml
- Revisa que ambos servicios estén en la misma network
💡 ¿Necesitas ayuda con tu proyecto?
Si quieres que te ayude con tu proyecto web, no tienes más que ponerte en contacto. Estaré encantado de ayudarte.
Conclusión: El setup que escala con tu proyecto
Invertir tiempo en configurar Docker correctamente al inicio del proyecto paga dividendos enormes a lo largo de todo el ciclo de vida del desarrollo. Lo que comienza como un proyecto simple de WordPress + Next.js inevitablemente crece en complejidad: añades Redis para caching, Elasticsearch para búsqueda avanzada, servicios adicionales de microservicios.
Con Docker, cada nueva adición es simplemente otro servicio en docker-compose.yml. Sin Docker, cada adición requiere reconfiguraciones manuales en cada máquina de desarrollo, documentación detallada de setup, y debugging de configuraciones inconsistentes.
El ambiente Docker que has construido no es solo para desarrollo local. Los mismos Dockerfiles, con mínimas modificaciones, se usan para deployar a staging y producción. Los mismos contenedores que corren en tu laptop corren en servidores de producción, eliminando la categoría completa de bugs "funciona en mi máquina".
Para equipos grandes, Docker es indispensable. Para desarrolladores solo, sigue siendo extremadamente valioso al permitirte trabajar en múltiples proyectos sin conflictos, experimentar sin miedo de romper tu ambiente, y mantener tu máquina local limpia de dependencias específicas de cada proyecto.
Empieza con la configuración básica presentada aquí, y evoluciona incrementalmente según necesites. Añade servicios, optimiza performance, integra con tus herramientas preferidas. La flexibilidad de Docker significa que tu ambiente de desarrollo crece con tu proyecto y tu equipo, manteniéndose siempre reproducible, eficiente y confiable.
¿Listo para despegar?
Si buscas una web rápida, segura y diseñada para convertir, solicita tu presupuesto sin compromiso.
Solicitar PresupuestoArtículos Relacionados
Conecta WordPress con Stripe usando Next.js API Routes: Implementa pagos seguros en arquitectura headless
Implementar pagos en una arquitectura WordPress headless presenta desafíos únicos: no puedes usar plugins tradicionale...
El Stack Técnico Perfecto para WordPress Headless en 2026
Introducción La evolución del desarrollo web ha transformado la forma en que construimos sitios y aplicaciones. Wor...
La Importancia de los Temas Hijos en WordPress
La Importancia de los Temas Hijos en WordPress Si trabajas con WordPress, probablemente has escuchado hablar de los t...