💾 ¿Como he montado este blog?
Desde hace tiempo tenía pendiente montar un entorno estable y fácil de mantener para alojar mi blog. Aunque la opción más tradicional habría sido configurar un servidor completo con un panel de control tipo ISPConfig o Plesk, esta vez quería probar un enfoque diferente: algo más ligero, modular y que me diera mayor control sobre cada servicio.
Ahí es donde entra Docker. Gracias a su filosofía de contenedores, permite separar cada componente —base de datos, aplicación, proxy, certificados, etc.— y gestionarlos de forma independiente. Esto no solo simplifica la configuración y el despliegue, sino que además facilita la replicación del entorno o su migración a otro servidor en cuestión de minutos.
En esta entrada explico cómo estructuré el despliegue del blog utilizando Docker, agrupando todos los servicios necesarios y configurando un entorno limpio, funcional y fácilmente escalable.
La configuración inicial es muy sencilla: un contenedor para la base de datos MySQL, otro para la ejecución de WordPress y, por último, un proxy en Apache para redirigir las peticiones externas al contenedor de WordPress. Como complemento opcional, añadí un contenedor adicional para generar certificados con Let’s Encrypt.
Para el despliegue agrupé la definición de los contenedores en un fichero docker-compose.yml. Lo bueno de esto es que pude realizar prácticamente toda la construcción de la infraestructura en mi equipo local, usando la versión de Docker para Windows.
Lo primero que definí fue un fichero .env para guardar valores como las contraseñas de cada servicio u opciones de arranque. Como las pruebas las hacía en local, no quería que Docker iniciara el blog automáticamente cada vez que encendía el ordenador.
RESTART_POLICY=always DB_NAME=wordpress DB_USER=wp DB_PASSWORD=********* DB_ROOT_PASSWORD=********* DOMINIO=maceh.es EMAIL=a.david.le.mola@la.informatica.com
Una vez definidas las variables de entorno, creé el fichero docker-compose.yml, donde añadí el contenedor para la base de datos MySQL utilizando la imagen oficial. Para asegurar la persistencia de los datos, empleé volúmenes, de modo que la información no se perdiera al reiniciar los contenedores. En este caso, asocié un volumen a la ruta interna donde MySQL almacena sus ficheros.
services: blogdb: image: mysql:8.0 restart: ${RESTART_POLICY:-no} expose: - 3306 container_name: blogdb environment: MYSQL_DATABASE: ${DB_NAME} MYSQL_USER: ${DB_USER} MYSQL_PASSWORD: ${DB_PASSWORD} MYSQL_RANDOM_ROOT_PASSWORD: ${DB_PASSWORD} volumes: - blogdb:/var/lib/mysql volumes: blogdb:
También opté por no mapear directamente el puerto 3306 a localhost, sino simplemente exponerlo para que fuera accesible desde otros contenedores definidos en el docker-compose. Solo durante las pruebas lo mapeé temporalmente para acceder con un cliente MySQL.
Las variables de entorno son las clásicas en un servicio como este, simplemente seguí las instrucciones que se detallan en la pagina oficial de la imagen https://hub.docker.com/_/mysql. Tras este primer paso se comprobé que si arrancaba sin problemas con el comando:
docker compose up --build
Tras comprobar que la base de datos arrancaba correctamente, añadí la definición del servicio de WordPress, que se comunica con MySQL y hace que el blog funcione. Siguiendo la misma lógica, usé la imagen oficial, expuse el puerto 80 para la comunicación entre contenedores y creé un volumen para los ficheros del sitio.
services: blogwordpress: image: wordpress container_name: blogwordpress restart: ${RESTART_POLICY:-no} expose: - 80 environment: WORDPRESS_DB_HOST: blogdb WORDPRESS_DB_USER: ${DB_USER} WORDPRESS_DB_PASSWORD: ${DB_PASSWORD} WORDPRESS_DB_NAME: ${DB_NAME} volumes: - wordpress:/var/www/html volumes: wordpress:
Con WordPress ya operativo, quedaba la parte de hacerlo accesible desde la web. Para ello, creé un nuevo contenedor basado en Apache que actuara como proxy inverso, redirigiendo las peticiones del dominio hacia el contenedor de WordPress y gestionando las conexiones seguras mediante HTTPS.
En primer lugar tocaba toquetear la configuración de Apache, para ello descargue el sample de los ficheros httpd.conf para la configuración global y httpd-vhosts.conf para agregar la configuración del dominio.
Sobre httpd.conf lo que hice fue descomentar (o añadir) las siguientes líneas:
# Los puertos que quiero que escuche # Puerto HTTP Listen 80 # Puerto HTTPS Listen 443 # Los módulos extra que necesito para activar HTTPS y el proxy interno LoadModule proxy_module modules/mod_proxy.so LoadModule proxy_http_module modules/mod_proxy_http.so LoadModule ssl_module modules/mod_ssl.so # Activar la el fichero de configuración de sitios para crearle una entrada a http://maceh.es y https://maceh.es Include conf/extra/httpd-vhosts.conf # Activar el modulo proxy para redirigir las peticiones sobre maceh.es al contenedor de WordPress <IfModule proxy_html_module> Include conf/extra/proxy-html.conf </IfModule>
En httpd-vhosts.conf simplemente añadí la configuración del sitio:
# Lo que llegue por HTTP (puerto 80) <VirtualHost *:80> ServerName maceh.es # Lo rediriges a HTTPS Redirect permanent / https://maceh.es/ </VirtualHost> # Lo que llegue por HTTPS (puerto 443) <VirtualHost *:443> ServerName maceh.es # Opción para que WordPress funcione bien a través de un proxy RequestHeader set X-Forwarded-Proto "https" RequestHeader set X-Forwarded-Ssl "on" # Se activa el proxy para que Apache redirija las peticiones de https://maceh.es al contenedor de WordPress ProxyPreserveHost On ProxyPass "/" "http://blogwordpress/" ProxyPassReverse "/" "http://blogwordpress/" # Ruta del certificado para maceh.es SSLEngine on SSLCertificateFile /var/certs/maceh.es/fullchain.pem SSLCertificateKeyFile /var/certs/maceh.es/privkey.pem </VirtualHost>
Casi por último, generé una imagen personalizada donde añadí las herramientas necesarias (como OpenSSL y un editor de texto) y monté el directorio de certificados de Let’s Encrypt. Con esto, el entorno quedó completamente funcional y listo para desplegar el blog de forma sencilla, modular y mantenible.
FROM httpd:2.4 # Se instala un editor de texto y openssl RUN apt-get update && apt-get install nano openssl -y # Se copia un nuevo httpd.conf y httpd-vhosts.conf COPY ./conf/httpd.conf /usr/local/apache2/conf COPY ./conf/httpd-vhosts.conf /usr/local/apache2/conf/extra # Se crea una carpeta para montar el directorio de certificados de Lets Encrypt RUN mkdir -p /var/certs
En la entrada del docker-compose referencié el Dockerfile en lugar de la imagen original de httpd, mapeé los puertos 80 y 443 para permitir el acceso desde fuera y añadí el volumen donde se almacenan los certificados.
services: httpd: build: ./httpd restart: ${RESTART_POLICY:-no} container_name: httpd volumes: - certs:/var/certs:ro ports: - 80:80 - 443:443 volumes: certs:
Como último paso, quedaba la obtención de un certificado válido para el sitio, para ello utilicé el servicio de Let’s Encrypt y lo encapsule dentro de un contenedor, pero partiendo de una imagen de Debian pero antes generé un script el cual se ejecutaría en el contenedor y crearía un certificado en el volumen de certificados.
#!/bin/bash DOMAIN=$1 EMAIL=$2 certbot certonly --standalone --preferred-challenges http -d "$DOMAIN" --agree-tos --email "$EMAIL" --non-interactive mkdir -p /var/certs/"$DOMAIN" cp /etc/letsencrypt/live/"$DOMAIN"/fullchain.pem /var/certs/"$DOMAIN"/ cp /etc/letsencrypt/live/"$DOMAIN"/privkey.pem /var/certs/"$DOMAIN"/
Con el script generado tocaba crear el Dockerfile:
FROM debian:bullseye-slim RUN apt update && apt install -y certbot curl RUN mkdir -p /var/certs /var/log/letsencrypt /etc/letsencrypt COPY ./certbot-run.sh /usr/local/bin/certbot-run.sh RUN chmod +x /usr/local/bin/certbot-run.sh CMD ["/bin/bash"]
Y añadir el nuevo servicio al docker-compose pero en este caso indicaremos que no se arrancará por defecto ya que este contenedor no se plantea como un servicio en segundo plano sino como un comando que se lanzará desde un crontab cada 2 meses.
services: certbot: build: ./certbot restart: no container_name: certbot network_mode: host volumes: - certs:/var/certs command: /usr/local/bin/certbot-run.sh ${DOMINIO} ${EMAIL} volumes: certs:
En este caso para lanzarlo es necesario apagar momentáneamente el servicio httpd puesto que es necesario liberar el puerto 80:
docker compose stop httpd docker compose run --rm certbot docker compose start httpd
Con todo esto, el blog quedó desplegado de forma completamente funcional, manteniendo cada servicio aislado, fácilmente actualizable y con una configuración sencilla de replicar o migrar. Gracias a Docker, el proceso de montar una infraestructura que antes requería varias herramientas y configuraciones manuales se redujo a unos pocos ficheros y comandos.
Más allá de la parte técnica, lo interesante de este enfoque es la flexibilidad que ofrece: puedo levantar el entorno en local para hacer pruebas, desplegarlo en un servidor remoto en cuestión de minutos o añadir nuevos servicios sin afectar al resto.
En definitiva, montar el blog con Docker me ha permitido tener un entorno limpio, modular y totalmente bajo control, sin depender de paneles de gestión ni configuraciones pesadas. Una vez automatizado todo, mantener el blog se ha convertido en una tarea mucho más ligera y predecible… pero ahora queda la parte mas complicada… llenarlo.
Filed under: HowTos - @ 4 de octubre de 2025 22:34
Etiquetas: apache, blog, docker, letsencrypt, mysql, wordpress