Introducción
Tener un sitio web propio no necesariamente requiere contratar un servicio de hosting tradicional. Si disponemos de un servidor en casa, una conexión a Internet y algunos conocimientos de Linux, podemos alojar WordPress utilizando Docker y publicar el sitio mediante Cloudflare Tunnel.
En este artículo documentamos una implementación real de WordPress sobre Ubuntu Server, utilizando contenedores Docker para ejecutar WordPress y MariaDB, y Cloudflare Tunnel para permitir el acceso público mediante HTTPS sin tener que exponer directamente los puertos del servidor a Internet.
El objetivo no es solamente mostrar cómo poner WordPress en funcionamiento, sino explicar la arquitectura, las decisiones técnicas, las medidas de seguridad y las comprobaciones necesarias para mantener el servicio.
La implementación descrita corresponde a un entorno de homelab. No debe interpretarse como una arquitectura de alta disponibilidad ni como sustituto de una estrategia completa de recuperación ante desastres.
1. Arquitectura de la solución
La implementación utiliza tres servicios principales:
- WordPress: aplicación web ejecutada sobre Apache y PHP.
- MariaDB: motor de base de datos que almacena el contenido y la configuración de WordPress.
- Cloudflare Tunnel: establece la conexión entre el servidor y la infraestructura de Cloudflare para publicar el sitio.
Los servicios se ejecutan en contenedores Docker y comparten una red interna.
Flujo de conexión
INTERNET
|
|
Inicio
|
|
CLOUDFLARE
|
|
CLOUDFLARE TUNNEL
|
|
Docker: wordpress_net
|
+-------+-------+
| |
| |
wordpress-web wordpress-db
| |
Apache MariaDB
| |
+-------+-------+
|
Volúmenes Docker
Una característica importante de esta arquitectura es que WordPress no necesita publicar su puerto directamente en Internet. El contenedor de Cloudflare Tunnel se comunica con WordPress a través de la red interna de Docker.
Componentes utilizados
| Componente | Implementación |
|---|---|
| Sistema operativo | Ubuntu Server 26.04 LTS |
| Contenedores | Docker Engine |
| Orquestación | Docker Compose |
| CMS | WordPress |
| Servidor web | Apache |
| PHP | 8.3 |
| Base de datos | MariaDB 11.4 |
| Publicación externa | Cloudflare Tunnel |
| Red Docker | wordpress_net |
2. Preparación del entorno
Antes de desplegar WordPress es necesario contar con un servidor Ubuntu actualizado y Docker Engine con Docker Compose.
Podemos verificar las versiones instaladas con:
docker --version
docker compose version
También conviene comprobar que Docker esté funcionando:
sudo systemctl status docker --no-pager
Para esta implementación se utiliza el usuario habitual del servidor, con permisos para administrar Docker.
Es recomendable trabajar con un directorio dedicado para mantener organizados los archivos de configuración:
mkdir -p ~/docker/wordpress
cd ~/docker/wordpress
La estructura de trabajo utilizada es:
/home/dotbugs/docker/wordpress/
├── .env
├── docker-compose.yml
└── php-security.ini
El archivo .env contiene variables de entorno y credenciales. No debe publicarse, compartirse ni incluirse en repositorios públicos.
3. Organización de los servicios Docker
La implementación utiliza tres contenedores:
| Contenedor | Imagen |
|---|---|
| wordpress-web | wordpress.3-apache |
| wordpress-db | mariadb:11.4 |
| wordpress-cloudflared | cloudflare/cloudflared |
La base de datos no publica ningún puerto en el servidor. WordPress accede a MariaDB mediante el nombre del servicio dentro de la red Docker.
WordPress, por su parte, publica su puerto HTTP únicamente en la interfaz local:
ports:
- "127.0.0.1:8088:80"
Esto significa que el servicio puede consultarse desde el propio servidor mediante 127.0.0.1:8088, pero no queda directamente expuesto en la interfaz de red pública o LAN del servidor mediante ese mapeo.
El contenedor de Cloudflare Tunnel utiliza la red Docker para alcanzar WordPress mediante:
http://wordpress-web:80
Variables y credenciales
Las credenciales de MariaDB y WordPress se administran mediante variables de entorno almacenadas en .env.
Un ejemplo de las variables que deben gestionarse es:
MARIADB_DATABASE=wordpress
MARIADB_USER=wordpress_user
MARIADB_PASSWORD=CAMBIAR_POR_UNA_CLAVE_SEGURA
MARIADB_ROOT_PASSWORD=CAMBIAR_POR_OTRA_CLAVE_SEGURA
Los valores son ilustrativos. Deben utilizarse contraseñas largas, únicas y aleatorias.
El token de Cloudflare Tunnel también debe mantenerse fuera del archivo público de configuración y nunca compartirse en capturas, registros o repositorios.
4. Persistencia de datos: un aspecto fundamental
Los contenedores son reemplazables. Los datos importantes deben mantenerse fuera de la capa temporal del contenedor.
En esta implementación se utilizan dos volúmenes Docker:
| Volumen | Destino dentro del contenedor |
|---|---|
| wordpress_wordpress_files | /var/www/html |
| wordpress_wordpress_db | /var/lib/mysql |
El primero conserva los archivos de WordPress, incluyendo temas, plugins, archivos multimedia y configuración.
El segundo conserva los datos de MariaDB.
Podemos revisar los volúmenes existentes con:
docker volume ls
Y consultar dónde están montados:
docker inspect wordpress-web \
--format '{{range .Mounts}}{{println .Type .Name .Destination}}{{end}}'
docker inspect wordpress-db \
--format '{{range .Mounts}}{{println .Type .Name .Destination}}{{end}}'
En esta arquitectura también se utiliza un archivo de configuración PHP montado como solo lectura.
Es importante recordar que los volúmenes permiten conservar los datos al recrear contenedores, pero no sustituyen una copia de seguridad.
Precaución: no utilizar docker compose down -v sin comprender sus consecuencias. La opción -v puede eliminar los volúmenes asociados y, con ellos, los datos persistentes.
5. Publicación mediante Cloudflare Tunnel
Uno de los objetivos de esta implementación fue evitar la apertura directa de puertos entrantes en el router.
Cloudflare Tunnel permite que el servidor establezca conexiones salientes hacia la infraestructura de Cloudflare. De esta manera, el servicio puede publicarse sin configurar un reenvío tradicional de los puertos 80 y 443 hacia el servidor doméstico.
En la configuración utilizada, el hostname público es:
dotbugs.com
El destino interno del túnel es:
http://wordpress-web:80
El tráfico sigue este recorrido:
- El visitante accede a
https://dotbugs.com. - Cloudflare recibe la solicitud.
- Cloudflare Tunnel transporta la solicitud hasta el servidor.
- El contenedor de Cloudflare Tunnel se comunica con WordPress por la red Docker.
- WordPress procesa la solicitud y devuelve la respuesta.
¿Por qué se utiliza HTTP entre los contenedores?
En esta implementación, la comunicación entre Cloudflare Tunnel y WordPress utiliza HTTP dentro de la red privada de Docker.
La conexión pública del visitante utiliza HTTPS. Esto no significa que debamos configurar HTTP público sin protección: la terminación HTTPS y la comunicación interna son tramos diferentes de la arquitectura.
Si se utiliza este modelo, es importante proteger el acceso al servidor y controlar quién puede acceder a la red Docker.
6. Configuración adicional de seguridad PHP
Durante la revisión de la instalación se identificó una medida preventiva relacionada con el mecanismo de seguimiento del progreso de cargas de archivos de PHP.
Para aplicarla se utiliza un archivo personalizado:
; Mitigación preventiva temporal del aviso de seguridad de PHP
session.upload_progress.enabled = 0
El archivo se encuentra en:
/home/dotbugs/docker/wordpress/php-security.ini
Y se monta en el contenedor como:
/usr/local/etc/php/conf.d/zz-security.ini
El montaje se realiza en modo de solo lectura.
Comprobar que PHP aplica la configuración
No basta con escribir una directiva en un archivo. Es importante verificar el valor efectivo dentro del contenedor:
docker exec wordpress-web php -r '
echo "session.upload_progress.enabled=";
var_export(ini_get("session.upload_progress.enabled"));
echo PHP_EOL;
'
En nuestra instalación, el resultado fue:
session.upload_progress.enabled='0'
También podemos comprobar la versión de PHP:
docker exec wordpress-web php -v
Esta medida es una mitigación específica. No sustituye las actualizaciones de PHP, WordPress, los plugins ni las demás medidas de seguridad.
7. Verificación del funcionamiento
Una vez desplegados los servicios, es necesario comprobar que todos estén funcionando.
Estado de los contenedores
cd ~/docker/wordpress
docker compose ps
En nuestra implementación, los tres contenedores se encontraban en ejecución.
MariaDB cuenta además con una comprobación de salud propia.
Podemos consultar su estado mediante:
docker inspect wordpress-db \
--format '{{.State.Status}} | Salud: {{.State.Health.Status}}'
Resultado esperado:
running | Salud: healthy
Es importante señalar que WordPress y Cloudflare Tunnel no tienen un healthcheck de Docker configurado en esta implementación. Por lo tanto, que aparezcan como Up no garantiza por sí solo que la aplicación web sea accesible.
Comprobar WordPress localmente
Como el puerto está publicado únicamente en localhost, podemos realizar la prueba desde el propio servidor:
curl -sS -o /dev/null \
--max-time 15 \
-w "HTTP %{http_code} | Tiempo %{time_total}s\n" \
http://127.0.0.1:8088/
Comprobar el acceso público
curl -sS -o /dev/null \
--max-time 20 \
-w "HTTP %{http_code} | Tiempo %{time_total}s\n" \
https://dotbugs.com/
En las comprobaciones realizadas el 3 de noviembre de 2026 obtuvimos:
| Prueba | Resultado |
|---|---|
| WordPress local | HTTP 200 |
| Acceso público HTTPS | HTTP 200 |
| MariaDB | Healthy |
| Tiempo de respuesta local | Aproximadamente 0.07 segundos |
| Tiempo de respuesta público | Aproximadamente 0.31 segundos |
Estos resultados corresponden a las pruebas realizadas en ese momento; no representan una garantía de disponibilidad permanente.
8. Diagnóstico y monitorización semanal
Además de comprobar manualmente los servicios, implementamos un script de diagnóstico semanal.
El script se encuentra en:
/home/dotbugs/scripts/wordpress-weekly-check.sh
Su función es revisar distintos aspectos de la instalación:
- Disponibilidad del sitio público.
- Respuesta local de WordPress.
- Estado de los contenedores.
- Salud de MariaDB.
- Actualizaciones disponibles de WordPress, plugins y temas.
- Diferencias entre los digest locales y remotos de las imágenes Docker.
- Existencia y antigüedad de los respaldos.
- Integridad del archivo comprimido.
- Presencia de contenido SQL esperado.
Un segundo script ejecuta el diagnóstico y genera un informe con fecha y hora:
/home/dotbugs/scripts/run-wordpress-weekly-check.sh
Los informes se almacenan en:
/home/dotbugs/reports/wordpress/
El directorio y los informes individuales utilizan permisos restrictivos para proteger su contenido.
Programación mediante cron
La tarea se ejecuta los lunes a las 09:00, hora de Ciudad de México.
La entrada configurada es:
0 15 * * 1 /home/feram/scripts/run-wordpress-weekly-check.sh >> /home/feram/reports/wordpress/cron.log 2>&1
El horario corresponde a las 15:00 UTC.
Podemos consultar la programación con:
crontab -l
Y revisar los informes generados con:
ls -lah /home/dotbugs/reports/wordpress/
Importante: este mecanismo es un diagnóstico programado, no un servicio de monitorización continua. Tampoco actualiza automáticamente WordPress ni las imágenes Docker.
9. Copias de seguridad: qué tenemos y qué falta
Una instalación con volúmenes persistentes sigue necesitando copias de seguridad independientes.
En nuestro servidor se identificaron respaldos completos que contienen:
- Una exportación SQL de la base de datos.
- Un archivo comprimido con los archivos de WordPress.
El directorio utilizado es:
/home/dotbugs/backups/wordpress/
Los nombres de los archivos son:
wordpress-database.sql
wordpress-files.tar.gz
El diagnóstico semanal verifica que ambos archivos existan, que no estén vacíos, que el archivo comprimido pueda validarse y que la antigüedad de la copia no supere el límite configurado de ocho días.
Sin embargo, hay una distinción importante: la implementación documentada cuenta con copias de seguridad existentes y comprobaciones automáticas, pero no tiene confirmada una tarea periódica que genere nuevas copias automáticamente.
Tampoco se ha documentado una restauración completa probada.
Por ello, antes de considerar el entorno protegido ante cualquier incidente, todavía es necesario implementar y probar:
- Generación periódica de copias SQL y de archivos.
- Una política de retención y rotación.
- Una segunda ubicación de almacenamiento independiente del servidor.
- Un procedimiento de restauración probado.
Un respaldo que nunca se ha restaurado sigue siendo una copia cuya capacidad de recuperación no hemos demostrado.
10. Consideraciones de seguridad y mantenimiento
Al alojar un sitio web desde un servidor doméstico, debemos tener presentes algunos aspectos:
- Mantener Ubuntu, Docker, WordPress, PHP y MariaDB actualizados.
- Revisar los plugins y temas instalados y retirar los que no se utilicen.
- Utilizar contraseñas únicas y robustas.
- Proteger el archivo
.envy el token de Cloudflare Tunnel. - Evitar publicar directamente el puerto de MariaDB.
- Limitar la exposición de los servicios que no necesitan acceso externo.
- Realizar copias de seguridad independientes.
- Probar periódicamente los procedimientos de recuperación.
- Revisar los informes de diagnóstico y las advertencias de actualización.
- Evitar actualizar imágenes de producción sin revisar los cambios y contar con un respaldo reciente.
También conviene recordar que utilizar etiquetas como latest facilita el despliegue, pero no fija una versión inmutable. Antes de actualizar es recomendable revisar la imagen disponible, realizar una copia de seguridad y validar el funcionamiento posterior.
11. Conclusiones
Alojar WordPress en un homeserver utilizando Docker y Cloudflare Tunnel es una alternativa viable para quienes desean administrar su propia infraestructura y aprender sobre contenedores, redes y servicios web.
En esta implementación conseguimos:
- Ejecutar WordPress y MariaDB en contenedores independientes.
- Mantener los datos mediante volúmenes Docker.
- Evitar publicar directamente el puerto de WordPress en la interfaz de red del servidor.
- Publicar el sitio mediante Cloudflare Tunnel.
- Aplicar una configuración PHP personalizada.
- Comprobar la disponibilidad local y pública.
- Automatizar diagnósticos semanales y generar informes.
La experiencia también demuestra que desplegar un servicio es solamente una parte del trabajo. Mantenerlo actualizado, verificar su disponibilidad y contar con una estrategia de respaldos y recuperación son responsabilidades igual de importantes.
La ventaja de un homelab es que permite construir, experimentar y documentar una infraestructura real. La clave está en conocer sus límites y no confundir que un servicio esté funcionando con que esté completamente protegido.
Nota técnica: este artículo describe una implementación real en Ubuntu Server 26.04 LTS, Docker y Cloudflare Tunnel, revisada el 3 de noviembre de 2026. Las rutas, nombres de contenedores y resultados corresponden a ese entorno y pueden necesitar ajustes en otras instalaciones.
