¿De verdad necesita este blog WordPress y Kubernetes? Un experimento con Hugo, cifras reales y lo que costaría migrar
He reconstruido parte de este blog con Hugo y lo he comparado con WordPress sobre K3s: velocidad, hardware, parches, seguridad, Grafana, roles de Ansible y esfuerzo de migración. La decisión sigue abierta.

🌐 También en: English · Deutsch · Français
Actualización del 6 de noviembre de 2026: ya está decidido y hecho: este blog funciona ahora con Hugo. Lo que ocurrió está aquí.
En resumen: este blog es sobre todo texto e imágenes, pero funciona sobre Kubernetes con una base de datos, un pool de PHP, una caché y diez plugins. Así que reconstruí una parte como sitio estático con Hugo y comparé ambos. El resultado me sorprendió en los dos sentidos: para los lectores, la ganancia de velocidad es menor de lo esperado; para la operación, la ganancia es mucho mayor. No hay nada decidido. Este blog sigue funcionando con WordPress.
En el pasado, Hugo me parecía demasiado engorroso, demasiado laborioso y poco fácil de usar, tanto en el día a día como para migrar desde WordPress. Con una asistencia de IA rápida, eso ha cambiado por completo: la conversión, el tema y los scripts de comprobación de esta prueba surgieron en poco tiempo. Por eso vuelvo a plantearme la pregunta.
Este artículo reúne todo lo que quiero saber antes de decidir: mediciones, estimaciones claramente señaladas, el efecto sobre cada rol de Ansible, Grafana, SELinux, la cuestión de un repositorio nuevo, el esfuerzo real y una lista de comprobación por si me decido.
Índice
🧒 Para quienes no son de TI: ¿cuál es la diferencia?
WordPress construye una página cuando alguien la pide: un programa lee el texto de una base de datos y monta la página, y una caché guarda el resultado durante un tiempo. Hugo construye todas las páginas una sola vez, por adelantado, en mi propio equipo. Después, el servidor solo entrega archivos ya terminados. No se ejecuta ningún programa para el visitante, no hay base de datos ni página de inicio de sesión que atacar.
El precio: ya no hay un editor cómodo en el navegador. Un artículo es un archivo de texto, y publicar significa construir y subir.
Lo que queda: WordPress es un programa en marcha que sirve páginas. Hugo es una herramienta que produce archivos. Casi todas las diferencias de este artículo se derivan de eso.
🧪 El experimento: tres artículos, todas las páginas, cuatro idiomas
Convertí a Markdown los tres últimos artículos y todas las páginas estáticas en inglés, alemán, francés y español, y escribí un pequeño tema con los mismos colores, las mismas fuentes y la misma estructura. Un artículo tiene ahora este aspecto en el disco:
---
title: 'HTTP/3 para mi blog y Nextcloud'
slug: http-3-para-mi-blog-y-nextcloud
date: '2026-11-03T19:59:27+01:00'
translationKey: wp3277
categories: [DevSecOps]
image: img/2026_11_no_http3.webp
---
**Actualización del 4 de noviembre de 2026:** HTTP/3 vuelve a estar desactivado en todos mis servidores.
Para un lector, se comporta igual: mismo menú, banderas de idioma, cinta de titulares, carrusel, lista de artículos, barra lateral, índice y búsqueda. Las pequeñas diferencias solo se ven comparando lado a lado. El tema es una reconstrucción mía y no una copia del tema de WordPress, así que algunos píxeles varían.
Un aspecto parecido demuestra poco. Por eso, un script comparó cada página con el original:
| Comprobado | Resultado |
|---|---|
| Páginas comparadas con el original de WordPress | 28 de 28, texto idéntico |
| Bloques de código, carácter por carácter | 24, ninguna diferencia |
| Tablas, títulos, elementos de lista | 20, 251 y 300, cantidades idénticas |
| Enlaces en el texto de los artículos | 84, todos correctos |
| Direcciones, canonical, hreflang, noindex | Idénticos en las 28 páginas |
| Referencias internas de todo el sitio generado | 7.740 en 225 archivos HTML, 0 rotas |
| Tiempo de construcción | 1,9 s la primera vez, luego 0,14 s |
| Tamaño del tema y las herramientas | 561 líneas de tema, 217 líneas de Python |
La prueba también encontró los errores que la justifican. Hugo sustituía en silencio los enlaces tel: de mi aviso legal por #ZgotmplZ. Los archivos mensuales de otros idiomas acababan en la ruta inglesa. Mi primera versión mostraba la meta descripción en lugar del extracto real. Y el logotipo estaba invertido. Solo el script de comparación encontró el primer error; yo nunca habría pulsado un enlace de teléfono.
Lo que queda: una migración vale lo que vale su comparación automática. Un sitio que parece correcto puede tener contenido roto.
⚡ Velocidad: lo que he medido y lo que solo estimo
Primero las mediciones. Se hicieron desde mi servidor doméstico contra el blog real, con 14 peticiones por línea, midiendo el tiempo hasta el primer byte. Un archivo estático en el servidor real representa lo que entregaría un sitio con Hugo.
| Petición | Mediana | Rango |
|---|---|---|
| Archivo estático | 57 ms | 50–59 ms |
| Portada desde la caché de páginas de WordPress | 58 ms | 55–62 ms |
| Artículo desde la caché de páginas de WordPress | 60 ms | 56–63 ms |
| Mismo artículo, generado por PHP (sin caché) | 146 ms | 134–163 ms |
Esa es la sorpresa: una página de WordPress en caché es tan rápida como un archivo estático. La caché ya hace su trabajo. La diferencia solo aparece cuando la caché no actúa.
La ganancia es más clara en lo que el navegador debe descargar, sin contar imágenes y fuentes:
| Portada, comprimida | WordPress | Hugo | Cambio |
|---|---|---|---|
| HTML | 19,4 KB | 4,9 KB | −75 % |
| CSS | 8 archivos, 67,7 KB | 1 archivo, 3,6 KB | −95 % |
| JavaScript | 16 archivos, 111,1 KB | 1 archivo, 1,0 KB | −99 % |
| Total | 25 archivos, 198 KB | 3 archivos, 9,5 KB | −95 % |
Ahora, las estimaciones. Son mis expectativas, no mediciones. Todavía no he hecho una prueba real en un navegador y en condiciones realistas.
| Situación | Ganancia esperada | Base |
|---|---|---|
| Página ya en la caché de WordPress | Aproximadamente 0 % | Medido: 57 frente a 58 ms |
| Página fuera de la caché | Alrededor de un 60 % menos de espera, unas 2,5 veces más rápido | Medido: 146 frente a 57 ms |
| Primera visita desde el móvil con conexión regular | Alrededor de un 20–40 % más rápido hasta que la página se puede usar | Estimación a partir de un 95 % menos de CSS y JavaScript |
| Siguientes páginas de la misma visita | Pequeña | Los archivos ya están en la caché del navegador |
| Pico de tráfico | Mucho más robusto | Sin límite de workers ni base de datos de por medio |
Lo que queda: la velocidad por sí sola no justificaría una migración. Un WordPress con buena caché ya es rápido. El lector gana sobre todo porque descarga menos código, en especial en el móvil.
🖥️ Hardware: lo que el servidor necesitaría de verdad
Estos son los valores actuales del servidor del blog y mi estimación para un sitio estático. Grafana y toda la monitorización se mantienen en ambos casos, porque quiero conservarlas.
| Valor | WordPress sobre K3s (medido) | Hugo (estimación) |
|---|---|---|
| CPU | 4 núcleos, carga de unos 0,2 | 1–2 núcleos |
| Memoria en uso | 2,7 GB de 7,5 GB | Unos 1,0–1,2 GB |
| Tamaño de servidor razonable | 4 GB o más | 2 GB |
| Datos de Kubernetes en el disco | 7,1 GB | 0 |
| Sitio en el disco | 375 MB de aplicación y medios | Menos de 500 MB para todos los artículos |
| Pods en marcha / imágenes de contenedor | 17 / 14 | 0 / 0 |
| Tarea de fondo cada pocos minutos | wp-cron | Ninguna |
| Reglas de cortafuegos (salida de nftables) | 7.760 líneas, sobre todo de K3s | Unos cientos de líneas |
Así se reparte hoy la memoria, por proceso. Estos valores se solapan, de modo que su suma supera el consumo real:
| Parte | Memoria | Con Hugo |
|---|---|---|
| k3s-server | 847 MB | Desaparece |
| containerd y sus shims | 383 MB | Desaparece |
| php-fpm | 349 MB | Desaparece |
| MariaDB | 249 MB | Desaparece |
| Ingress (nginx y controlador) | 316 MB | Sustituido por un nginx pequeño |
| Grafana | 357 MB | Se queda |
| Alloy | 273 MB | Se queda |
| Loki | 179 MB | Se queda |
| Prometheus | 175 MB | Se queda |
Lo que queda: el sitio en sí apenas necesitaría nada. Con alrededor de 1 GB, la monitorización sería lo más grande del servidor y marcaría su tamaño mínimo.
📅 Trabajo diario y parches
Desde el 1 de septiembre, este repositorio ha recibido 256 commits, y 76 de ellos tocaban los tres grandes roles del blog. No todo es mantenimiento, porque buena parte fue construcción. Aun así, muestra adónde va la atención. El día de actualizaciones y las dos vulnerabilidades de PHP en un día son buenos ejemplos de lo que esto significa en la práctica.
| Tarea | WordPress sobre K3s | Hugo |
|---|---|---|
| Actualizaciones del sistema | Sí | Sí |
| K3s, Helm, ingress, cert-manager | Sí | Desaparece |
| WordPress, 10 plugins, tema | Sí, automático con comprobaciones | Desaparece |
| PHP, incluidos parches de urgencia | Sí | Desaparece del servidor |
| MariaDB y Redis | Sí | Desaparece |
| Versiones fijadas para el blog | 9 de 30 entradas, más la pila común de K3s | nginx del sistema, Hugo en mi equipo |
| Análisis nocturno de imágenes y sus hallazgos | Sí, véase Trivy | Desaparece; quedan los paquetes del sistema |
| Alertas de workers, pods, base de datos | Sí | Desaparece |
| Vaciar cachés tras un cambio | Caché de páginas, de objetos y de optimización | Desaparece |
| Leer correos de monitorización y seguridad | Sí | Sí, con muchos menos motivos |
| Escribir un artículo | Editor en el navegador | Archivo de texto, Git, construir, subir |
| Si una actualización rompe algo | El sitio real puede caer | Falla la construcción, el sitio sigue igual |
| Marcha atrás | Volcado de base de datos y restaurar archivos | Subir la construcción anterior |
Los Nextcloud siguen en K3s, así que mi día de actualizaciones no desaparece. Solo se acorta.
Lo que queda: esta es la verdadera ganancia. Desaparecen clases enteras de actualizaciones, urgencias y alertas, porque ese software ya no existe en el servidor.
🔐 Seguridad: una valoración honesta
Esta valoración es mi opinión, no una medición. «Alto» significa aquí riesgo alto.
| Amenaza | WordPress sobre K3s | Hugo | Por qué |
|---|---|---|---|
| Vulnerabilidad en plugin o núcleo | Medio a alto | Ninguno | No se ejecuta código en el servidor |
| Ejecución remota de código en PHP | Medio | Ninguno | Sin PHP |
| Inyección SQL | Bajo a medio | Ninguno | Sin base de datos |
| Robo de acceso de administrador | Bajo, gracias a 2FA y filtro geográfico | Ninguno | No hay acceso web |
| Pérdida de datos tras una intrusión | Medio | Bajo | Sin datos de usuarios; basta una reconstrucción limpia |
| Vulnerabilidad de nginx | Medio | Medio | Se mantiene |
| Núcleo o sistema operativo | Medio | Medio | Se mantiene |
| SSH o clave de despliegue | Bajo | Bajo a medio | La clave de despliegue pasa a ser el secreto valioso |
| Cadena de construcción (Hugo, tema, mi equipo) | No aplica | Bajo a medio | Nuevo: se publicaría una construcción envenenada |
| Acceso a Grafana | Bajo a medio | Bajo a medio | Se mantiene y pasa a ser el mayor objetivo web |
| Kubernetes como superficie de ataque | Bajo a medio | Ninguno | Sin servidor de API |
Dos puntos incómodos. Primero, «estático» no significa «seguro»: el servidor, SSH, nginx y el DNS requieren el mismo cuidado, y mis nueve capas de defensa no dejan de tener sentido. Segundo, con Grafana el servidor no es puramente estático. Sigue habiendo un acceso, y es el que merece más atención.
Lo que queda: la superficie de ataque se reduce mucho, porque varias clases de ataque ya no tienen nada a lo que apuntar. Los riesgos restantes se trasladan a la clave de despliegue y a Grafana.
🧱 ¿Sigo necesitando K3s en el servidor del blog?
Técnicamente, no. El artículo a fondo sobre K3s y el artículo sobre WordPress en Kubernetes explican por qué lo elegí. Un solo nginx con una carpeta de archivos no necesita orquestador. Pero K3s tiene ventajas que perdería de verdad:
| Aspecto | Con K3s | Sin K3s |
|---|---|---|
| Memoria y disco | ➖ Unos 1,2 GB de RAM y 7,1 GB de disco | ➕ Nada |
| Piezas móviles | ➖ Servidor de API, kubelet, containerd, CNI, ingress, cert-manager | ➕ Un servicio |
| Cortafuegos | ➖ K3s añade miles de líneas; hay que tener cuidado en cada cambio | ➕ Solo mis reglas |
| Actualizaciones | ➖ K3s, charts e imágenes | ➕ Paquetes del sistema |
| Alta disponibilidad | ➖ Ninguna con un solo nodo | ➖ Tampoco |
| Aislamiento entre partes | ➕ Pods, NetworkPolicies | ➖ Apenas necesario con un servicio |
| Actualizaciones sin corte | ➕ Actualizaciones progresivas incluidas | ➕ Sustituidas por un cambio atómico de carpeta |
| Mismas herramientas que en los Nextcloud | ➕ Una forma de trabajar | ➖ Dos formas de trabajar |
| Banco de pruebas para los Nextcloud | ➕ Servidor pequeño que recibe K3s primero | ➖ Se pierde; lo tendría que sustituir una VM de pruebas |
| Valor de aprendizaje y material para el blog | ➕ Alto | ➖ Menor en este servidor |
Los Nextcloud son otro caso: PHP, Collabora, push y tareas cron son realmente varios servicios que trabajan juntos.
Lo que queda: en el blog, K3s ya no se justifica por necesidad técnica, sino por uniformidad, pruebas y aprendizaje. Son razones legítimas, pero conviene llamarlas por su nombre.
📝 Lo que sigue justificando WordPress
Sería fácil descartar WordPress aquí. Sería un error, porque resuelve problemas reales que Hugo no resuelve:
| Punto fuerte de WordPress | Con Hugo |
|---|---|
| Editor en el navegador, utilizable por cualquiera | Editor de texto y Git, o una herramienta CMS adicional |
| Escribir y publicar desde el móvil | Incómodo sin herramientas adicionales |
| Biblioteca de medios con subida y búsqueda | Archivos en una carpeta |
| Varios autores con roles | Solo mediante Git o una herramienta adicional |
| Comentarios y formularios | Hace falta un servicio externo |
| Publicación programada | Solo con una construcción programada |
| Plugins para casi todo | Hay que hacerlo uno mismo |
| Análisis SEO mientras se escribe (Yoast) | Metaetiquetas sí, análisis en directo no |
| Traspaso a una persona no técnica | Casi imposible |
| Tienda, miembros, contenido dinámico | No es la herramienta adecuada |
Esto es en lo que se convertirían los plugins actuales:
| Plugin | Con Hugo |
|---|---|
| Polylang | Incluido en Hugo |
| Yoast SEO | Plantillas propias: títulos, meta, Open Graph, schema, sitemap. Schema más simple, sin análisis en el editor |
| Easy Table of Contents | Incluido en Hugo |
| Autoptimize | Incluido en Hugo (minify) |
| CompressX | Procesado de imágenes de Hugo al construir |
| WP Super Cache, Redis Cache | No hacen falta |
| Two Factor, Limit Login Attempts | No hacen falta sin acceso |
| Classic Editor | No aplica |
También hay un aspecto personal. La mayoría de mis últimos artículos se crean con ayuda de la IA, por lo que el editor cómodo me importa menos que a la mayoría de los blogueros. Con un archivo Markdown y un commit de Git, trabajar con un asistente de IA sería incluso más sencillo que hoy. Tampoco quedaría atado a él: Markdown se edita con cualquier editor de texto.
Lo que queda: WordPress sigue siendo la opción correcta cuando escriben otras personas, cuando se publica sobre la marcha o cuando el sitio hace algo más que mostrar contenido. Solo es la herramienta equivocada si nada de eso se aplica.
📈 ¿Cuántos sitios con WordPress, cuántos con Hugo?
En el mercado, Hugo es un nicho pequeño. Según W3Techs, a 5 de noviembre de 2026:
| Indicador | WordPress | Hugo |
|---|---|---|
| Cuota de todos los sitios web | 39,5 % | 0,1 % |
| Cuota de sitios con CMS conocido | 58,2 % | 0,2 % |
| Proporción sobre todos los sitios | Alrededor de 400 : 1 | 1 |
| Estrellas en GitHub | 21.519 (un espejo; el desarrollo se hace en otro lugar) | 90.379 |
| Licencia | GPL | Apache 2.0 |
Estas cifras deben leerse con cautela. W3Techs da cuotas y no números absolutos, y no siempre reconoce los sitios estáticos, así que Hugo probablemente esté infravalorado. Las estrellas miden el interés entre desarrolladores, no los sitios en funcionamiento, y el repositorio de WordPress es solo un espejo. No conviene comparar directamente ambos valores.
Aun así, las cifras muestran algo: millones de personas usan WordPress sin haber visto nunca un repositorio de Git, mientras que Hugo tiene una comunidad inusualmente grande para su cuota de mercado. Mi impresión, sin datos sólidos: quienes toman este camino son sobre todo los más frikis, y por motivos que encajan con este artículo: sin PHP, sin base de datos, sin acceso, poca superficie de ataque, Markdown y Git en lugar de una interfaz web. Un sitio rápido es un buen extra. Para muchos, supongo que también es el placer de tener una instalación que se entiende por completo.
Lo que queda: WordPress es la opción convencional y segura con el mayor ecosistema; Hugo, un nicho elegido a propósito. Estar en minoría no es un argumento en ningún sentido.
🛡️ ¿Sería SELinux más sencillo?
Sí, claramente en la parte web, aunque no desaparece. El artículo a fondo sobre SELinux muestra cuánto trabajo hay ahí.
| Tema | WordPress sobre K3s | Hugo sobre AlmaLinux |
|---|---|---|
| Paquete de reglas para contenedores | k3s-selinux, aún no estable para AlmaLinux 10 | No hace falta |
| Dominios de la parte web | Contenedores con sus categorías | nginx en el probado httpd_t |
| Archivos | Volúmenes y etiquetas de contenedor | Una carpeta como httpd_sys_content_t |
| Reetiquetar tras descargar K3s | Obligatorio | No aplica |
| Proxy hacia Grafana | A través del ingress en un pod | Un booleano o un puerto etiquetado |
Mis dominios de monitorización (homelab_*) | Se quedan | Se quedan |
| Denegaciones ocultas por dontaudit | Hay que comprobarlas | Hay que comprobarlas |
| Esfuerzo de migración | — | Nueva fase de aprendizaje para nginx en el servidor |
En FreeBSD la pregunta cambia por completo, porque allí no existe SELinux. Su lugar lo ocupan las jails y el framework MAC.
Lo que queda: desaparece la parte más difícil de SELinux, los contenedores y su paquete. Se mantiene el modo enforcing, y también la obligación de comprobar el dominio real de cada servicio.
📊 Grafana se queda. ¿Qué implica eso?
Hoy Grafana funciona como un servicio normal del sistema, pero se accede a él a través del ingress de K3s. Esta es la dependencia más importante:
| Parte | Hoy | Con Hugo | Esfuerzo |
|---|---|---|---|
| Servicio Grafana | Servicio del sistema | Sin cambios | Ninguno |
| Acceso por /grafana/ | Ingress de K3s | Proxy inverso en el nginx del servidor, con WebSocket | Pequeño |
| Filtro geográfico del acceso | En el ingress | Pasarlo al nginx del servidor | Pequeño |
| fail2ban para el acceso a Grafana | Lee registros de pods | Lee el registro de nginx, nueva ruta y filtro | Pequeño |
| Prometheus | Node, kubelet, kube-state, MariaDB, PHP-FPM | Node y un exportador de nginx | Pequeño |
| Alloy y Loki | Leen /var/log/pods | Leen /var/log/nginx y el journal | Medio |
| Panel de contenedores | Disponible | Ya no en este servidor | Retirar |
| Paneles de PHP-FPM, MariaDB, Redis | Disponibles | Ya no en este servidor | Retirar |
| Alertas de workers, pods, base de datos | Activas | Retirarlas o notificarán NoData | Medio |
| Nuevo: peticiones y errores de nginx | A través del ingress | Con el exportador de nginx y los registros | Medio |
| Nuevo: último despliegue correcto | — | Métrica de marca de tiempo con alerta | Pequeño |
| Caducidad de certificados TLS | Comprobación de Monit en el ingress | Comprobación de Monit en el servidor | Pequeño |
| Memoria | Alrededor de 1 GB | Alrededor de 1 GB | Determina el tamaño del servidor |
Aquí se aplica directamente una lección anterior: si retiro métricas y olvido una regla, puede dejar de avisar y quedarse en verde para siempre. Por eso, tras la migración comprobaría el estado de cada regla y no la ausencia de correos.
Una alternativa sería un Grafana central en casa que recoja datos de todos los servidores. Quitaría el último acceso del servidor del blog, pero es una decisión aparte, con sus propios riesgos.
Lo que queda: conservar Grafana no es un obstáculo. Sí significa que el proxy inverso, el filtro geográfico y un acceso se quedan en el servidor, y que la monitorización debe mirar a nginx en vez de a los pods.
🎭 Los roles de Ansible, uno por uno
Mi primera suposición fue que apenas quedarían roles. Era falsa. La mayoría de los roles protegen el servidor y no WordPress, así que la mayoría se queda. Estos son los 29 roles de blog.yml, con el número de líneas referidas a Kubernetes:
| Rol | Líneas | Líneas K8s | Con Hugo |
|---|---|---|---|
| blog_k3s_deploy | 2.962 | 69 | ❌ Eliminado |
| blog_wordpress | 1.473 | 45 | ❌ Eliminado |
| common_k3s | 1.139 | 186 | ❌ No en este servidor (Nextcloud lo conserva) |
| common_kube_metrics | 214 | 5 | ❌ No en este servidor |
| common_trivy | 406 | 14 | ❌ No en este servidor (sin imágenes) |
| blog_packages | 222 | 0 | 🔄 Reescribir: nginx, cliente ACME, rsync |
| blog_verify | 478 | 13 | 🔄 Reescribir: sitio, certificados, enlaces, despliegue |
| blog_visitor_stats | 237 | 3 | 🔄 Nueva ruta de registro, misma idea |
| common_version_check | 340 | 49 | 🔄 Nueva rama para el blog |
| common_version_active | 223 | 17 | 🔄 Nueva rama para el blog |
| common_fail2ban | 487 | 42 | 🔧 Rutas nuevas, sin jail de WordPress |
| common_firewall | 1.179 | 18 | 🔧 Más simple: sin tablas de K3s ni NodePorts |
| common_monit | 702 | 12 | 🔧 Sin control de pods; controles de nginx y despliegue |
| common_grafana | 1.552 | 0 | 🔧 Menos paneles y alertas |
| common_loki | 843 | 7 | 🔧 Archivos de registro en vez de registros de pods |
| common_prometheus | 451 | 1 | 🔧 Otros destinos |
| common_selinux | 220 | 6 | 🔧 Sin k3s-selinux; reglas de nginx |
| common_selinux_monitoring | 399 | 0 | 🔧 Menos dominios |
| common_clamav | 261 | 0 | 🔧 Probablemente apagado: sin subidas en el servidor |
| common_cis_hardening | 571 | 8 | 🔧 Quitar excepciones de K3s |
| common_auditd | 252 | 4 | 🔧 Quitar rutas de K3s |
| common_sysctl | 313 | 2 | 🔧 Quitar ajustes de K3s |
| common_aide_refresh | 101 | 1 | 🔧 Carpeta web excluida a propósito |
| common_dnf_automatic | 225 | 1 | 🔧 Pequeño ajuste |
| common_ssh | 650 | 0 | ✅ Sin cambios |
| common_msmtp | 138 | 0 | ✅ Sin cambios |
| common_rkhunter | 187 | 0 | ✅ Sin cambios |
| common_logrotate | 115 | 0 | ✅ Sin cambios, se añade nginx |
| common_node_exporter | 273 | 1 | ✅ Sin cambios |
| Resultado | Roles |
|---|---|
| ❌ Fuera de este servidor | 5 |
| 🔄 Reescribir | 5 |
| 🔧 Ajustar | 14 |
| ✅ Sin cambios | 5 |
En líneas, unas 6.200 de las aproximadamente 16.600 líneas que el blog recorre hoy ya no se usarían allí, es decir, un 37 %. Solo unas 4.400 saldrían realmente del repositorio, porque los Nextcloud necesitan el resto. El recorrido de Ansible sobre el blog se convertiría en un documento histórico.
Por supuesto, habría que crear algunas cosas nuevas:
| Nuevo | Qué hace | Tamaño |
|---|---|---|
| Rol para el sitio estático | nginx en el servidor, cabeceras de seguridad, redirecciones, proxy de Grafana | Medio |
| Certificados | Cliente ACME con renovación y recarga de nginx, en lugar de cert-manager | Pequeño |
| Despliegue | Usuario restringido, rsync, carpeta nueva por versión, cambio atómico, últimas versiones conservadas | Pequeño a medio |
| Exportador de nginx | Peticiones y errores para Prometheus | Pequeño |
| Cadena de construcción | Versión de Hugo fijada, construir, comparar, índice de búsqueda, subir | Medio |
| Verificación | Control de enlaces y comparación de contenido en cada construcción | Ya existe del experimento |
| Tabla de redirecciones | Direcciones antiguas como el feed y las rutas de medios | Pequeño |
| Copia de seguridad | Simplificada: contenido en Git, más configuración del servidor y registros | Pequeño |
Lo que queda: los roles caros desaparecen, la base de seguridad se queda. Es una buena noticia: el endurecimiento construido durante meses se conserva.
📦 ¿Un repositorio nuevo?
Mi repositorio contiene ahora las dos pilas. Los roles comunes deciden por la variable stack, y mi regla es que cada cambio debe funcionar para ambas. Pasar a Hugo plantea cómo dividirlo:
| Opción | Ventaja | Inconveniente |
|---|---|---|
| A: todo en el repositorio actual | Un lugar, un historial | Cada artículo e imagen entra en el historial de Ansible y pasa por su lint; el repositorio crece con los medios |
| B: repositorio nuevo para contenido, tema y construcción. Ansible se queda | Separación limpia; sin cambios en los roles comunes; CI propia y simple | Dos repositorios; el despliegue debe conectarlos |
| C: separación completa con roles comunes como colección | Limpio para varios usuarios | Versiones y publicaciones para solo dos usuarios; esfuerzo alto |
| D: repositorio de Ansible aparte para el blog con roles copiados | Totalmente independiente | Cada corrección de seguridad, por duplicado; las copias divergen |
Yo elegiría la opción B. Artículos, imágenes, tema y scripts de construcción irían a un repositorio nuevo. El servidor sigue en el repositorio de Ansible con un tercer valor, stack: static. Los roles siguen en un solo lugar y el endurecimiento no hay que mantenerlo dos veces. Para publicar basta una clave de despliegue restringida, sin acceso al código de Ansible ni a su vault.
El precio es real, eso sí: la regla «funciona para ambas pilas» pasaría a ser «funciona para las tres». A cambio, las ramas de WordPress desaparecen de muchos roles.
Lo que queda: contenido e infraestructura cambian a ritmos distintos y guardan secretos distintos, así que deben estar en repositorios separados. El endurecimiento común no debe copiarse.
🐡 ¿Y en FreeBSD?
Llevo años entusiasmado con FreeBSD, aunque junto a Linux lleve una existencia de nicho. Es un sistema completo y coherente, con muy buena documentación, jails, ZFS y pf. Aun así, para este blog nunca tuvo opción, porque K3s depende de funciones del núcleo Linux y allí no funciona de forma apta para producción.
Eso es exactamente lo que cambia Hugo. Sin K3s, contenedores, PHP ni base de datos, los requisitos se reducen a nginx, un cliente ACME, rsync y la monitorización, y todo eso es software corriente en FreeBSD. La complejidad que me ataba a Linux ha desaparecido. Esta es la comparación de las tres variantes:
| Tema | WordPress sobre K3s | Hugo sobre AlmaLinux | Hugo sobre FreeBSD |
|---|---|---|---|
| RAM con monitorización completa (estimación) | 2,7 GB | Unos 1,0–1,2 GB | Unos 1,0–1,2 GB |
| Parches | ➖ Muchas capas | ➕ Sistema y nginx | ➕ freebsd-update y pkg |
| Actualizaciones automáticas | ➕ Existen | ➕ Existen | ➖ Hay que construirlas |
| Aislamiento de servicios | ➕ SELinux y pods | ➕ SELinux y sandbox de systemd | ➖ Jails, más toscas pero simples |
| Cortafuegos y bloqueos | ➕ Existen | ➕ Existen, más simples | ➖ pf y fail2ban nuevos |
| Roles reutilizables | Todos | 24 de 29, en parte ajustados | Pocos; casi todo sería nuevo |
| Grafana, Prometheus, Loki | ➕ Existen | ➕ Existen, ajustados | ➖ Hay paquetes; rutas y reglas nuevas |
| Instantáneas antes de actualizar | ➖ Ninguna | ➖ Ninguna por defecto | ➕ ZFS |
| Operación junto a los Nextcloud | ➕ Igual | ➕ Mismo sistema | ➖ Segunda plataforma |
| Conocimiento existente | ➕ Grande | ➕ Grande | ➖ Hay que adquirirlo |
| Valor como tema del blog | Ya escrito | Medio | ➕ Alto |
La pregunta decisiva es si esta instalación podría protegerse tan bien como la actual. La he valorado capa por capa. Es una valoración sobre el papel: no la he construido ni probado en FreeBSD.
| Capa | Hoy en AlmaLinux | Hugo sobre FreeBSD | Valoración |
|---|---|---|---|
| Filtro de paquetes | nftables | pf con tablas | 🟢 Equivalente |
| Endurecimiento de SSH, filtro geográfico | sshd y conjuntos de nftables | sshd y tablas de pf | 🟢 Equivalente |
| Bloquear atacantes | fail2ban | fail2ban con pf | 🟢 Equivalente, con reglas nuevas |
| Aislar nginx | SELinux y sandbox de systemd | Jail, contenido montado solo lectura | 🟢 Bueno; simple y fácil de comprobar |
| Aislar la monitorización | Mis dominios de SELinux | Una jail por servicio | 🟡 Más tosco, pero con fronteras claras |
| Reglas finas por proceso | Type enforcement de SELinux | Sin equivalente directo; framework MAC, Capsicum solo si el software lo admite | 🔴 Más débil |
| Superficie de ataque del servidor web | Con WordPress: grande | Archivos estáticos | 🟢 Mucho menor que hoy |
| Información sobre vulnerabilidades | dnf, Trivy | pkg audit y freebsd-update | 🟢 Buena |
| Registro de auditoría | auditd | OpenBSM | 🟡 Equivalente en principio; hay que rehacer el análisis |
| Integridad de archivos, rootkits | AIDE, rkhunter | AIDE, rkhunter, freebsd-update IDS | 🟢 Equivalente |
| Marcha atrás tras una actualización | Paquetes, restauración | Instantáneas de ZFS y boot environments | 🟢 Mejor |
| Base de endurecimiento | Referencia CIS aplicada en mi rol | Hay que rehacerla a mano | 🟡 Esfuerzo; menos material preparado |
| Grafana | Tras filtro geográfico y fail2ban | Igual, en su propia jail | 🟡 Sigue siendo el mayor objetivo |
| Experiencia con incidentes reales | Meses de hallazgos reales | Empieza de cero | 🔴 Más débil al principio |
Mi valoración: sí, para un sitio estático, FreeBSD podría protegerse a un nivel global parecido, aunque con otros medios. Lo que perdería de verdad es el control fino de SELinux por proceso. Con un WordPress con PHP y plugins, eso dolería. Con un nginx que entrega archivos en solo lectura desde una jail, pesa mucho menos, porque un atacante puede hacer sencillamente mucho menos. A cambio, las jails son lo bastante simples para comprobarlas a simple vista. Los dos puntos débiles reales son Grafana, que sigue necesitando un acceso, y mi falta de práctica: mi instalación de AlmaLinux ha pasado por meses de errores reales, y una de FreeBSD no.
Un punto abierto: que yo sepa, los paquetes de Grafana, Prometheus, Loki y node_exporter están en los ports de FreeBSD, pero antes tendría que comprobar Alloy y las versiones actuales. De lo contrario, haría falta otro recolector de registros.
Lo que queda: la gran ganancia viene de Hugo, no del sistema operativo. Pero es Hugo lo que hace posible FreeBSD: sin K3s, la elección ya no es técnica. Sería una elección por convicción y, para un sitio estático, una elección defendible.
💶 Costes y beneficios
| Punto | Coste | Beneficio |
|---|---|---|
| Migración | Único, véase abajo | — |
| Servidor | — | Posible servidor más pequeño y barato |
| Mantenimiento | Nueva cadena de construcción que mantener | Muchos menos parches y alertas |
| Seguridad | Proteger la clave de despliegue y la construcción | Varias clases de ataque eliminadas |
| Velocidad | — | Menos que descargar; mejor en picos y fuera de caché |
| Escritura | Sin editor ni trabajo desde el móvil | Historial de Git, archivos simples, fácil con IA |
| Copias de seguridad | — | Mucho más simples |
| Portabilidad | — | Markdown funciona en todas partes; sin atadura a una base de datos |
| Conocimiento | Se pierde el banco de pruebas de K3s | Menos casos especiales en la cabeza |
| SEO | Riesgo durante el traslado; sin análisis de Yoast | Páginas rápidas y control total |
| Material para el blog | Menos historias de operación en este servidor | Una buena historia de migración |
No puedo dar aquí un precio de servidor útil, porque depende del proveedor. De todos modos, el ahorro es modesto. La verdadera moneda aquí es la atención.
Lo que queda: esta migración no se paga con dinero ni devuelve dinero. Es un intercambio: comodidad al escribir a cambio de mucho menos trabajo de operación.
⏱️ ¿Cuánto trabajo sería?
El blog tiene 143 artículos en cada uno de los cuatro idiomas, 572 en total, además de 16 páginas estáticas y alrededor de 1.400 archivos de medios. Los valores siguientes son estimaciones. El experimento llevó aproximadamente una tarde con ayuda de la IA, lo que sugiere que la conversión es la parte más pequeña.
| Fase | Con ayuda de la IA | Nota |
|---|---|---|
| Inventario: plugins, bloques especiales, descargas | 2–3 h | La herramienta de exportación existe |
| Convertir todos los artículos y medios | 3–6 h | El script existe; los artículos antiguos guardan sorpresas |
| Terminar el tema: paginación, etiquetas, archivos, móvil | 3–5 h | La base existe |
| SEO: direcciones, redirecciones, feed, schema, sitemap | 2–3 h | Comparación automática con el sitemap actual |
| Ansible: rol nuevo, 14 ajustados, 5 reescritos, VM de pruebas | 8–15 h | La parte mayor |
| Monitorización y Grafana | 3–5 h | Comprobar cada regla |
| Copia, prueba de restauración, documentación en tres idiomas, texto de privacidad | 3–5 h | Debe hacerse con cuidado |
| Cambio y vigilancia activa | 2–3 h | Después, dos semanas de observación |
| Total, Hugo sobre AlmaLinux | Unas 26–45 h | En la práctica, repartidas en 2–3 semanas |
| Adicional para FreeBSD | Unas 15–30 h | Pocos roles reutilizables |
| Sin ayuda de la IA (estimación aproximada) | 3–5 semanas de trabajo | Sobre todo tema, conversión y tres idiomas |
Las ejecuciones reales en producción, la revisión de cada diff y las decisiones siguen siendo mías. La IA acelera la escritura, pero no elimina la obligación de comprobar.
Lo que queda: la mayor parte del trabajo no está en la conversión, sino en Ansible, la monitorización y la verificación. Ahí es donde hay que reservar tiempo.
✅ Lo que tendría que hacer si elijo Hugo
- Aclarar el requisito: ¿puedo vivir sin editor en el navegador ni trabajo desde el móvil? Sin esta respuesta, todo lo demás da igual.
- Elegir la plataforma: AlmaLinux por defecto, FreeBSD solo como decisión consciente.
- Hacer una copia completa: base de datos, archivos y medios. Mantener WordPress disponible varias semanas.
- Crear el repositorio de contenido con artículos, medios, tema y versión fijada de Hugo. Buscar secretos antes del primer push.
- Convertir todo y comparar automáticamente cada página: texto, bloques de código con sus barras invertidas, tablas, enlaces, textos alternativos, títulos y descripciones.
- Conservar cada dirección: artículos, idiomas, categorías, etiquetas y también
/wp-content/uploads/, porque hay imágenes enlazadas desde fuera. Comparar con el sitemap actual. - Configurar redirecciones: sin configuración, el feed RSS pasa de
/feed/a/index.xml. Comprobar también los enlaces?p=y la antigua dirección de búsqueda. - Reescribir la política de privacidad y revisar el aviso legal. El texto actual describe WordPress y sus plugins. Revisar también el aviso de IA y las licencias de todas las descargas.
- Terminar el tema y el SEO, incluidas imágenes Open Graph por debajo del límite de WhatsApp y
noindexpara las páginas legales. - Construir la parte de Ansible en una VM de pruebas:
stack: static, el rol nuevo, los roles ajustados. Ejecutarlo dos veces; la segunda debe mostrarchanged=0. - Comprobar SELinux de verdad: dominios con
ps -eZ, denegaciones ocultas consemodule -DBy el registro propio de cada servicio. - Rehacer la monitorización: Grafana tras nginx con filtro geográfico y fail2ban, exportador nuevo, reglas antiguas retiradas y, después, comprobar el estado de cada regla.
- Proteger la vía de despliegue: clave restringida, cambio atómico, últimas versiones conservadas y una alerta si falla el despliegue.
- Probar una restauración: construir un servidor limpio solo desde los repositorios, antes de tocar producción.
- Adaptar todo lo demás: scripts de copia, vigilancia externa, control de actualizaciones y de deriva, guías de operación en tres idiomas y una nota en los artículos a fondo antiguos.
- Planificar el cambio: bajar el TTL del DNS, elegir un momento tranquilo, tener lista la marcha atrás y luego revisar buscadores y errores 404 en los registros.
- Observar durante dos semanas, y solo entonces retirar WordPress y K3s del servidor.
Lo que queda: la mayoría de estos puntos no tienen nada que ver con Hugo. Tratan de no perder direcciones, cumplir la ley, mantener la monitorización y conservar una vía de retorno.
🌐 ¿Sería HTTP/3 más sencillo?
En parte. Como conté en el artículo sobre HTTP/3, lo desactivé porque las conexiones se colgaban tras una recarga. Con nginx directamente en el servidor, la configuración sería más sencilla, ya que desaparece el rodeo por el ingress. La causa, sin embargo, sigue ahí: los workers antiguos retienen los sockets UDP. Es un asunto de nginx y no de K3s, y por eso abrí la issue #1937. Menos conexiones largas y recargas poco frecuentes suavizarían el problema. No lo he probado. En un sitio estático el beneficio sería pequeño de todos modos, así que por ahora lo dejaría desactivado.
Lo que queda: una configuración más sencilla no corrige un fallo de la capa inferior.
🎯 Las lecciones
- Medir antes de creer. Esperaba una gran ganancia de velocidad. Un WordPress en caché respondió en 58 ms, un archivo estático en 57 ms.
- La mayor parte del beneficio está en la operación. El software que ya no existe no necesita parches, alertas ni urgencias.
- El endurecimiento sobrevive. Mi suposición de que quedarían pocos roles era falsa: 24 de 29 se quedan al menos en parte, porque protegen el servidor.
- «Estático» no significa automáticamente «simple». Grafana, el despliegue y la cadena de construcción se quedan, y el riesgo pasa a la clave de despliegue.
- Una migración necesita una herramienta de comparación. Sin ella, no habría visto los enlaces de teléfono rotos en mi aviso legal.
🤔 Dónde estoy ahora
No he decidido. Para este blog, K3s y WordPress ya no son técnicamente necesarios, eso está claro. Igual de claro es que me cuestan atención, no experiencia de lectura. Al otro lado están el editor, el banco de pruebas de K3s para los Nextcloud y una instalación que funciona y que conozco muy bien.
Si me decido, será con Hugo y un repositorio de contenido separado, y la monitorización se queda. AlmaLinux es la opción sensata, porque casi todo se conserva. FreeBSD es la opción que me entusiasma y, por primera vez, sería realista en este blog. Y si lo hago, habrá una segunda parte con las cifras reales, incluido todo lo que estas estimaciones hayan fallado.
