Mantener el control absoluto sobre los datos demográficos y de comportamiento de tus visitantes es fundamental en el mundo de la tecnología Enterprise. Umami Analytics se ha convertido en la alternativa por excelencia para quienes buscan una solución ligera, respetuosa de la privacidad y, sobre todo, fácil de desplegar mediante Docker.
¿Por qué Self-Hosted sobre la versión Cloud?
Aunque Umami ofrece una versión en la nube, el despliegue por cuenta propia (Self-Hosted) ofrece ventajas indiscutibles para un perfil técnico o corporativo:
- Sin límites de retención: La versión gratuita del cloud suele tener límites en cuanto a cuántos eventos puedes procesar o por cuánto tiempo se guardan. En tu propio servidor, el único límite es tu almacenamiento.
- Soberanía de Datos: Los datos no salen de tu infraestructura. Esto es vital para cumplir con normativas de privacidad o simplemente por filosofía de control total.
- Privacidad por Diseño: Al no depender de servidores de terceros, eliminas un punto de rastreo externo.
- Cero Costo Operativo: Si ya tienes un servidor con Docker, añadir un contenedor de Umami no incrementa tus costos mensuales.
1. Preparación del Entorno
Umami requiere una base de datos para almacenar las métricas. La forma más limpia y autocontenida de desplegarlo es utilizando Docker Compose para levantar tanto la aplicación de Umami como una base de datos PostgreSQL en el mismo stack.
Crea un directorio dedicado en tu servidor para este servicio:
# Create the directory for Umami
mkdir -p ./docker/umami
cd ./docker/umami

2. Archivo Docker Compose
Crea el archivo docker-compose.yml dentro del directorio. Asegúrate de cambiar el valor de APP_SECRET por una cadena de texto larga y aleatoria (puedes generarla con openssl rand -hex 32), y modifica las contraseñas de la base de datos por seguridad.
config para exponer puertos de umami
# Docker Compose stack for Umami Analytics with PostgreSQL
networks:
umami-net:
driver: bridge
services:
umami:
image: ghcr.io/umami-software/umami:postgresql-latest
container_name: VPC_CT_umami_app
environment:
# Database connection string format: postgresql://user:password@hostname:port/database_name
DATABASE_URL: postgresql://umami_user:SuperSecretDBPassword!@umami_db:5432/umami_data
DATABASE_TYPE: postgresql
# Replace with your own random hash for session encryption
APP_SECRET: replace_this_with_a_random_string_of_characters
ports:
# Expose port 3000 to the host
- "3000:3000"
depends_on:
- umami_db
networks:
- umami-net
restart: unless-stopped
umami_db:
image: postgres:15-alpine
container_name: umami_db
environment:
# These must match the credentials in the DATABASE_URL above
POSTGRES_DB: umami_data
POSTGRES_USER: umami_user
POSTGRES_PASSWORD: SuperSecretDBPassword!
volumes:
# Persist the database files on the host
- umami-db-data:/var/lib/postgresql/data
networks:
- umami-net
restart: unless-stopped
volumes:
# Define the named volume for persistence
umami-db-data:
Config con Cloudflare tunnels sin exponer puertos
Utilizar Cloudflare Tunnels es la opción más segura para exponer servicios en un entorno Enterprise. A diferencia de la apertura de puertos (Port Forwarding), un túnel establece una conexión saliente hacia Cloudflare, lo que significa que no tienes que abrir puertos en tu firewall ni exponer tu IP pública directamente a Internet.
Esta arquitectura no solo mitiga vectores de ataque como escaneos de puertos o ataques DDoS directos, sino que te permite apalancar el WAF (Web Application Firewall) de Cloudflare para filtrar tráfico malicioso antes de que llegue a tu red. Además, es la solución definitiva para self-hosters que se encuentran behind cgNAT, ya que elimina la necesidad de contar con una IP pública direccionable.
Profundizaremos en la arquitectura y despliegue de Cloudflare Tunnels en futuros posts, donde veremos cómo configurarlos paso a paso.
# Docker Compose stack for Umami Analytics with PostgreSQL and Cloudflare Tunnels
networks:
# External network managed outside this compose file (Cloudflare Tunnel network)
cloudflare_internal_net:
external: true
# Isolated internal network for Umami to communicate with its database
umami_internal_net:
driver: bridge
services:
umami:
image: ghcr.io/umami-software/umami:postgresql-latest
container_name: VPC_CT_umami_app
environment:
# Database connection string format: postgresql://user:password@hostname:port/database_name
DATABASE_URL: postgresql://umami_user:SuperSecretDBPassword!@umami_db:5432/umami_data
DATABASE_TYPE: postgresql
# Replace with your own secure random hash for session encryption
APP_SECRET: replace_this_with_a_random_string_of_characters
depends_on:
- umami_db
networks:
# Connect to Cloudflare tunnel network to expose the service
- cloudflare_internal_net
# Connect to internal network to reach the database
- umami_internal_net
restart: unless-stopped
# The 'ports' directive is intentionally omitted. The service is only accessible within the Docker networks.
umami_db:
image: postgres:15-alpine
container_name: umami_db
environment:
# These must match the credentials in the DATABASE_URL above
POSTGRES_DB: umami_data
POSTGRES_USER: umami_user
POSTGRES_PASSWORD: SuperSecretDBPassword!
volumes:
# Persist the database files on the host
- umami-db-data:/var/lib/postgresql/data
networks:
# The database is only connected to the internal network for security
- umami_internal_net
restart: unless-stopped
volumes:
# Define the named volume for database persistence
umami-db-data:
3. Despliegue y Ejecución
Una vez guardado el archivo, levanta los contenedores en segundo plano:
# Start the containers in detached mode
sudo docker compose up -d


Puedes verificar que ambos contenedores estén corriendo correctamente y revisar los logs si algo falla:
# Check container status
docker ps
# View application logs
docker logs -f umami
4. Configuración Inicial en la Interfaz Web
Abre tu navegador y accede a
http://<IP-DE-TU-SERVIDOR>:3000, (Si expusiste tu servicio por Cloudflare, abre el subdominio que hayas seleccionado)Inicia sesión con las credenciales por defecto:
- Usuario:
admin - Contraseña:
umami
- Usuario:

- Paso Crítico: Ve a la sección de Profile (Perfil) en la esquina superior derecha y cambia la contraseña inmediatamente.


Crea un nuevo usuario, asígnalo como administrador, cierra sesión e ingresa con la nueva sesión.
Ahora, regresa a la administración y elimina el usuario admin.
Es una excelente práctica de seguridad eliminar los usuarios por defecto (como admin) una vez que has creado tu propia cuenta administrativa. Los atacantes suelen automatizar intentos de inicio de sesión utilizando estos nombres de usuario comunes.

- Navega a Websites > Add website.

- Ingresa el nombre de tu sitio y el dominio.

Te saldrá el icono de tu página, haz clic sobre el nombre.

- Una vez creado, haz clic en el botón de Edit (Editar).

- Copia el script de JavaScript proporcionado; este es el que inyectarás en la cabecera (
<head>) de tu sitio estático en Hugo.

Una vez que hayas agregado el código al archivo head.html, podrás ver la información en Umami.

Conclusión
Implementar Umami mediante Docker no solo te otorga métricas precisas sin comprometer la velocidad de carga de tu sitio, sino que refuerza la robustez de tu infraestructura al integrar una herramienta de grado profesional bajo tu total mando. Una vez que el script está inyectado, el análisis de tráfico se vuelve una tarea trivial dentro de tu ecosistema tecnológico.