Etiqueta: MariaDB

  • Cómo instalar WordPress con Docker y Cloudflare Tunnel en un homeserver Ubuntu

    Cómo instalar WordPress con Docker y Cloudflare Tunnel en un homeserver Ubuntu

    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

    ComponenteImplementación
    Sistema operativoUbuntu Server 26.04 LTS
    ContenedoresDocker Engine
    OrquestaciónDocker Compose
    CMSWordPress
    Servidor webApache
    PHP8.3
    Base de datosMariaDB 11.4
    Publicación externaCloudflare Tunnel
    Red Dockerwordpress_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:

    ContenedorImagen
    wordpress-webwordpress.3-apache
    wordpress-dbmariadb:11.4
    wordpress-cloudflaredcloudflare/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:

    VolumenDestino 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:

    1. El visitante accede a https://dotbugs.com.
    2. Cloudflare recibe la solicitud.
    3. Cloudflare Tunnel transporta la solicitud hasta el servidor.
    4. El contenedor de Cloudflare Tunnel se comunica con WordPress por la red Docker.
    5. 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:

    PruebaResultado
    WordPress localHTTP 200
    Acceso público HTTPSHTTP 200
    MariaDBHealthy
    Tiempo de respuesta localAproximadamente 0.07 segundos
    Tiempo de respuesta públicoAproximadamente 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:

    1. Generación periódica de copias SQL y de archivos.
    2. Una política de retención y rotación.
    3. Una segunda ubicación de almacenamiento independiente del servidor.
    4. 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 .env y 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.