Logotipo de Bitdoze

Cómo instalar Umami Analytics en Docker (guía 2026)

Aprende a instalar Umami Analytics con Docker Compose en tu VPS. Guía paso a paso para autoalojar esta alternativa a Google Analytics centrada en la privacidad.

Dragos

Actualizado Publicado 31 min de lectura

Cómo instalar Umami Analytics en Docker (guía 2026)

Umami es una herramienta de analítica web sencilla, rápida y centrada en la privacidad, y una sólida alternativa a Google Analytics. Tiene licencia MIT, más de 37.000 estrellas en GitHub y solo recopila las métricas que necesitas sin rastrear a tus visitantes. Si quieres comparar opciones, Plausible y Matomo son herramientas de analítica autoalojadas similares. También puedes instalar Plausible Analytics con una configuración de Docker parecida.

Con Google Analytics 4 y la aplicación del RGPD en la UE, la analítica web autoalojada se ha convertido en la opción por defecto para quienes quieren la propiedad total de sus datos. Los scripts de Google Analytics son pesados y ralentizan tu sitio. Umami añade menos de 2 KB de sobrecarga.

Esta guía cubre cómo instalar Umami Analytics en Docker con Docker Compose, incluida la configuración del tracker, cómo evitar los bloqueadores de anuncios, las actualizaciones y las copias de seguridad. Umami v3 se lanzó en noviembre de 2025 con una nueva interfaz, mapas de calor, reproducción de sesiones, y ahora es solo compatible con PostgreSQL (se eliminó el soporte de MySQL).

Umami v3 (noviembre de 2025)

Umami v3 es una versión mayor con una interfaz rediseñada, Segments, Cohorts, Session Replay, Heatmaps y seguimiento de Web Vitals. MySQL ya no es compatible. PostgreSQL es la única opción de base de datos. Los archivos compose de esta guía están actualizados para v3. Consulta el artículo del blog sobre Umami v3 para todos los detalles.

Puedes encontrar más aplicaciones autoalojadas de código abierto y gratuitas en la sección self-hosted de toolhunt.net.

Pasos para instalar Umami Analytics con Docker Compose

Vamos a desplegar Umami en un VPS con Docker Compose y después configuraremos Cloudflare Tunnels para el acceso público con SSL. El vídeo a continuación cubre todo el proceso.

1. Requisitos previos

  • Un VPS con Docker y Docker Compose v2 instalados. Un Hetzner CX22 a ~€4/mes es suficiente (Umami + Postgres usan ~300 MB de RAM en total). Hostinger es otra opción económica.
  • Dockge o cualquier herramienta de gestión de Docker, o simplemente docker compose. Consulta la guía de instalación de Dockge para un recorrido completo. También puedes ver otros paneles de servidor autoalojados.
  • Un túnel de Cloudflare configurado para tu VPS (o cualquier proxy inverso con terminación SSL)
  • Un dominio o subdominio listo para Umami
  • PostgreSQL v12.14+ (usamos v15-alpine, que cumple este requisito)

Los precios de los VPS subieron en todos los proveedores en 2026: si estás reconsiderando un servidor alquilado, mira qué cambió y cuándo gana un mini PC.

No uses 'analytics' como subdominio

Los bloqueadores de anuncios suelen bloquear las peticiones a analytics.yourdomain.com. Usa algo como stats, metrics o track en su lugar. Esto aplica tanto al subdominio como a la ruta del script.

2. Archivo Docker Compose para Umami

A continuación tienes dos archivos compose. El primero es una configuración limpia sin copias de seguridad; úsalo si piensas hacer backups con un pg_dump programado por cron (recomendado). El segundo añade un contenedor sidecar tiredofit/db-backup para volcados automáticos programados.

Simple (sin backup)

yaml
services:
  umami:
    image: docker.umami.is/umami-software/umami:postgresql-latest
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@umami-db:5432/${POSTGRES_DB}
      APP_SECRET: ${APP_SECRET}
      DISABLE_TELEMETRY: ${DISABLE_TELEMETRY:-1}
    depends_on:
      umami-db:
        condition: service_healthy
    init: true
    restart: always
    healthcheck:
      test: ["CMD-SHELL", "curl http://localhost:3000/api/heartbeat"]
      interval: 5s
      timeout: 5s
      retries: 5

  umami-db:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      TZ: UTC
    volumes:
      - ./umami-db-data:/var/lib/postgresql/data
    restart: always
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
      interval: 5s
      timeout: 5s
      retries: 5

El servicio de Umami se ejecuta en el puerto 3000, se conecta a PostgreSQL a través de la red interna de Docker e incluye un health check que consulta /api/heartbeat. Los datos de Postgres viven en un volumen local en ./umami-db-data. Puedes cambiar el mapeo de puertos a lo que prefieras.

Con backup

yaml
services:
  umami:
    image: docker.umami.is/umami-software/umami:postgresql-latest
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@umami-db:5432/${POSTGRES_DB}
      APP_SECRET: ${APP_SECRET}
      DISABLE_TELEMETRY: ${DISABLE_TELEMETRY:-1}
    depends_on:
      umami-db:
        condition: service_healthy
    init: true
    restart: always
    healthcheck:
      test: ["CMD-SHELL", "curl http://localhost:3000/api/heartbeat"]
      interval: 5s
      timeout: 5s
      retries: 5

  umami-db:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      TZ: UTC
    volumes:
      - ./umami-db-data:/var/lib/postgresql/data
    restart: always
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
      interval: 5s
      timeout: 5s
      retries: 5

  umami-db-backup:
    container_name: umami-db-backup
    image: tiredofit/db-backup
    volumes:
      - ./backups:/backup
    environment:
      DB_TYPE: postgres
      DB_HOST: umami-db
      DB_NAME: ${POSTGRES_DB}
      DB_USER: ${POSTGRES_USER}
      DB_PASS: ${POSTGRES_PASSWORD}
      DB_BACKUP_INTERVAL: 720
      DB_CLEANUP_TIME: 72000
      CHECKSUM: SHA1
      COMPRESSION: GZ
      CONTAINER_ENABLE_MONITORING: false
    depends_on:
      umami-db:
        condition: service_healthy
    restart: always

El sidecar de backup vuelca la base de datos cada 12 horas y elimina las copias con más de 50 días. Los volcados se guardan en ./backups. La imagen tiredofit/db-backup es más pesada que un simple cron. Si quieres algo más ligero, consulta la sección de copia de seguridad y restauración para una alternativa de pg_dump en una sola línea.

Fija la versión de la imagen

postgresql-latest apunta ahora a Umami v3 (actualmente v3.2.0 a junio de 2026). Para despliegues reproducibles, fija una etiqueta concreta como 3.2.0. Ten en cuenta que las etiquetas de v3 eliminaron el prefijo v: es 3.2.0, no v3.2.0. Consulta las etiquetas de Docker Hub para todas las versiones disponibles. La imagen también está disponible en GHCR (ghcr.io/umami-software/umami:latest) y Docker Hub (umamisoftware/umami:postgresql-latest).

Si necesitas ejecutar varias bases de datos PostgreSQL en el mismo host, puedes consolidarlas en un único contenedor de Postgres para ahorrar memoria.

3. Configura el archivo .env

Crea un archivo .env en el mismo directorio que tu archivo compose (o añade las variables en la sección de entorno de Dockge).

bash
POSTGRES_USER=umami
POSTGRES_PASSWORD=your-secure-password
POSTGRES_DB=umami
APP_SECRET=run-openssl-rand-hex-32-to-generate
DISABLE_TELEMETRY=1

Genera un APP_SECRET único

Nunca uses el valor de ejemplo. Genera un secreto seguro con:
openssl rand -hex 32
Pega la salida como tu APP_SECRET. Se usa para el cifrado de sesiones.

4. Despliega Umami

Si usas Dockge, ponle un nombre a tu stack y pulsa deploy. Con Docker Compose directamente:

bash
docker compose up -d

Verifica que ambos contenedores estén saludables:

bash
docker compose ps

Deberías ver tanto umami como umami-db con estado healthy. Revisa los logs de Umami para confirmar que las migraciones se ejecutaron:

bash
docker compose logs -f umami

Una salida correcta muestra All migrations have been successfully applied seguido del servidor escuchando en el puerto 3000.

Accede a Umami en http://tu-ip-del-vps:3000. Inicia sesión con las credenciales por defecto:

  • Usuario: admin
  • Contraseña: umami

Cambia la contraseña por defecto ahora

Las credenciales por defecto admin / umami son de dominio público. Ve a Settings → Profile → Change password de inmediato, antes de exponer Umami a internet.

Verificación rápida con curl:

bash
curl http://localhost:3000/api/heartbeat
# Debería devolver: OK

5. Configura Cloudflare Tunnels para Umami

Ve a Access → Tunnels en el panel de Cloudflare, elige tu túnel y añade un hostname que mapee un dominio o subdominio al servicio de Umami en el puerto 3000.

Configuración de Cloudflare Tunnel para Umami Analytics

¿Ves IPs de visitantes como 'Unknown'?

Si Umami muestra “Unknown” en las ubicaciones de los visitantes detrás de Cloudflare Tunnels, añade CLIENT_IP_HEADER=cf-connecting-ip al bloque de entorno del servicio umami en tu archivo compose. Esto indica a Umami que lea la IP real del cliente desde la cabecera de Cloudflare. Reinicia el contenedor tras el cambio.

También puedes consultar Configurar CloudPanel como proxy inverso con Docker y Dockge para usar CloudPanel como proxy inverso de tus contenedores Docker, o autoalojar con Docker y Cloudflare Tunnels para otro ejemplo de despliegue.

6. Añade tu primer sitio web a Umami

Una vez configurado el túnel, accede a Umami en tu dominio. Inicia sesión y ve a Settings en la cabecera. Verás un botón para añadir un sitio web:

Añadiendo un sitio web en Umami Analytics

Después de añadir el sitio web, haz clic en Edit Website (o ve a Settings) para encontrar tu código de seguimiento. Puedes pegarlo en el <head> de tu sitio.

Vista general del panel de Umami Analytics

Código de seguimiento de Umami y configuración del tracker

Una vez añadido tu sitio web, Umami te proporciona un script de seguimiento. Así se usa y estas son las opciones disponibles.

Script de seguimiento básico

El embed estándar tiene este aspecto:

html
<script
  defer
  src="https://your-umami-domain.com/script.js"
  data-website-id="your-website-id"
></script>
  • data-website-id (el UUID del sitio web que añadiste en Umami, visible en el fragmento del código de seguimiento)
  • data-host-url (configúralo si el dominio del tracker difiere del dominio del panel, por ejemplo si sirves el script desde una CDN)

Eventos personalizados y opciones avanzadas

Umami admite el seguimiento de eventos tanto programático como declarativo.

Programático (llamada desde JavaScript):

js
// Registra un evento personalizado con propiedades
umami.track("signup", { plan: "pro", source: "header" });

Declarativo (añade atributos HTML a cualquier elemento):

html
<button data-umami-event="click-download" data-umami-file="whitepaper.pdf">
  Download
</button>

Otros atributos data útiles:

Atributo Propósito
data-domains="example.com,api.example.com" Restringe el seguimiento a dominios específicos
data-do-not-track="true" Respeta la cabecera DNT del navegador
data-auto-track="false" Desactiva el seguimiento automático de páginas vistas (útil en SPAs que gestionan su propio enrutado)
data-performance="true" Registra las Core Web Vitals (nuevo en v3.1.0)
data-auto-pageview="false" Suprime el pageview automático al cargar el script (nuevo en v3.2.0)

Si usas un sitio Astro, consulta Plausible Analytics para Astro con Cloudflare Workers para ver un enfoque de integración de analítica relacionado.

Evitar los bloqueadores de anuncios

Los bloqueadores de anuncios atacan dos rutas por defecto: /script.js y /api/send. Puedes renombrar ambas con variables de entorno:

text
TRACKER_SCRIPT_NAME=custom-tracker
COLLECT_API_ENDPOINT=/api/collect

Añádelas al entorno del servicio umami en tu archivo compose y reinicia. Actualiza el src de tu script de seguimiento para que coincida:

html
<script
  defer
  src="https://your-domain.com/custom-tracker.js"
  data-website-id="your-website-id"
></script>

Evitar los bloqueadores requiere cambios en el compose

Tras añadir TRACKER_SCRIPT_NAME y COLLECT_API_ENDPOINT a tu archivo compose, reinicia el contenedor de Umami: docker compose up -d –force-recreate umami. Asegúrate de que las reglas del proxy inverso dejen pasar la nueva ruta del endpoint.

Ejemplo completo para evitar bloqueadores de anuncios

Aquí tienes un fragmento completo para añadir al entorno del servicio umami en tu archivo compose:

yaml
environment:
  DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@umami-db:5432/${POSTGRES_DB}
  APP_SECRET: ${APP_SECRET}
  DISABLE_TELEMETRY: ${DISABLE_TELEMETRY:-1}
  TRACKER_SCRIPT_NAME: my-stats
  COLLECT_API_ENDPOINT: /api/c
  CLIENT_IP_HEADER: cf-connecting-ip

Después, en el HTML de tu sitio:

html
<script
  defer
  src="https://stats.yourdomain.com/my-stats.js"
  data-website-id="your-website-id"
></script>

Si usas un proxy inverso (Nginx, Caddy, Traefik), asegúrate de que las rutas personalizadas se reenvíen al contenedor de Umami en el puerto 3000. No hace falta configuración de enrutado adicional si el proxy envía todo el tráfico a Umami.

Cómo actualizar Umami Analytics en Docker

Actualizaciones estándar (misma versión mayor)

Para actualizaciones menores dentro de v3.x:

bash
# 1. Haz primero una copia de seguridad de la base de datos
docker compose exec -T umami-db pg_dump -U umami umami | gzip > backups/umami-$(date +%F-%H%M).sql.gz

# 2. Descarga la última imagen
docker compose pull

# 3. Recrea los contenedores
docker compose up -d --force-recreate

# 4. Verifica: busca "All migrations have been successfully applied"
docker compose logs -f umami

Actualizar de Umami v2 a v3

Cambios importantes en Umami v3

v3 elimina el soporte de MySQL, tiene una interfaz completamente nueva y ejecuta migraciones de esquema que pueden tardar en bases de datos grandes. Haz siempre una copia de seguridad antes de actualizar. Si estabas en MySQL, sigue primero la guía de migración de MySQL a PostgreSQL.

Pasos:

bash
# 1. Copia de seguridad
docker compose exec -T umami-db pg_dump -U umami umami > umami-backup-$(date +%F-%H%M).sql

# 2. Descarga y recrea
docker compose pull
docker compose up -d --force-recreate umami

# 3. Revisa los logs para ver la salida de las migraciones
docker compose logs -f umami

# 4. Ejecuta ANALYZE en PostgreSQL (crítico tras actualizaciones mayores)
docker compose exec -T umami-db psql -U umami umami -c "ANALYZE;"

El comando ANALYZE actualiza las estadísticas del planificador de consultas de PostgreSQL tras las migraciones de esquema. La documentación oficial de Umami lo recomienda. Sin él, las consultas del panel pueden volverse lentas después de una actualización mayor.

Tras actualizar, las imágenes Docker antiguas se acumulan en el disco. Puedes limpiar el espacio en disco de Docker para recuperar almacenamiento.

Copia de seguridad y restauración de la base de datos

Haz siempre copia de seguridad antes de actualizar

Una migración fallida puede corromper los datos. Ejecuta siempre pg_dump antes de descargar una nueva versión mayor.

pg_dump manual

Copia de seguridad (una sola línea, se puede programar con cron):

bash
docker compose exec -T umami-db pg_dump -U umami umami | gzip > backups/umami-$(date +%F).sql.gz

Añádelo al crontab para copias diarias a las 3 AM:

bash
0 3 * * * cd /path/to/umami && docker compose exec -T umami-db pg_dump -U umami umami | gzip > backups/umami-$(date +\%F).sql.gz

Restauración:

bash
gunzip < backups/umami-YYYY-MM-DD.sql.gz | docker compose exec -T umami-db psql -U umami umami

Para copias externas, sube el volcado comprimido a cualquier almacenamiento compatible con S3 (MinIO, Cloudflare R2, Backblaze B2).

tiredofit/db-backup

Si usaste el archivo compose “Con backup”, los volcados están en el directorio ./backups y se crean cada 12 horas. Para restaurar desde un backup de tiredofit:

bash
# Encuentra el último backup
ls -lt backups/

# Restáuralo
zcat backups/umami-db-*.sql.gz | docker compose exec -T umami-db psql -U umami umami

El sidecar tiredofit/db-backup es más pesado que un enfoque basado en cron (ejecuta su propio programador, compresión y limpieza). Para una huella mínima, el cron manual de pg_dump es más ligero.

Verifica y soluciona problemas de tu instalación de Umami

  • docker compose ps: tanto umami como umami-db muestran el estado healthy
  • docker compose logs umami: muestra “All migrations have been successfully applied”
  • curl http://localhost:3000/api/heartbeat: devuelve OK
  • El inicio de sesión funciona con admin / umami (cambia la contraseña de inmediato)
  • Incrusta el script de seguimiento en una página de prueba, abre las DevTools del navegador → pestaña Network, y confirma que script.js carga (200 OK)
  • Visita la página de prueba y comprueba el panel de Umami para ver una visita en tiempo real

Si quieres monitorizar los recursos de tu servidor junto a Umami, monta un stack de monitorización ligero.

El contenedor se reinicia constantemente

Revisa docker compose logs umami. La causa más común es una DATABASE_URL incorrecta o que el contenedor de la base de datos aún no esté saludable. Verifica que las variables de tu .env coincidan con lo que referencia el archivo compose. La condición depends_on debería esperar al health check de la base de datos, pero si tarda en arrancar en un VPS con poca memoria, dale 30 segundos.

Error 'service umami-db not found'

Un error tipográfico en depends_on. Asegúrate de que ponga umami-db (con “m”), no unami-db. Era un fallo en versiones anteriores del archivo compose de esta guía.

Panel en blanco o lento tras actualizar

Tras una actualización de versión mayor (sobre todo de v2 a v3), el planificador de consultas de PostgreSQL puede tener estadísticas desactualizadas. Ejecuta:

bash
docker compose exec -T umami-db psql -U umami umami -c "ANALYZE;"
Las IPs de los visitantes aparecen como 'Unknown'

Cuando Umami se ejecuta detrás de Cloudflare Tunnels o de un proxy inverso, la IP del cliente no le llega directamente. Añade al entorno del servicio umami:

text
CLIENT_IP_HEADER=cf-connecting-ip

Usa x-forwarded-for si estás detrás de Nginx o Traefik en lugar de Cloudflare. Reinicia el contenedor tras el cambio.

El script de seguimiento está bloqueado por bloqueadores de anuncios

Las rutas por defecto /script.js y /api/send están en las listas de bloqueo. Usa las variables de entorno TRACKER_SCRIPT_NAME y COLLECT_API_ENDPOINT para renombrarlas. Consulta la sección sobre cómo evitar los bloqueadores de anuncios más arriba.

Sin memoria / OOM kills

Umami usa ~200 MB de RAM y Postgres ~50-100 MB. Un VPS de 1 GB es el mínimo práctico. Comprueba el uso de recursos con docker stats. Si los contenedores sufren OOM kills, considera añadir límites de memoria en el compose:

yaml
deploy:
  resources:
    limits:
      memory: 256M

Umami v3: novedades

Versión Fecha Novedades
v3.0.0 Nov 2025 Nueva interfaz, Segments, Cohorts, Links, Pixels, página de administración. Solo PostgreSQL (MySQL eliminado).
v3.1.0 Abr 2026 Boards, Session Replay, Web Vitals, página de compartir rediseñada, filtros OR, regex, embudos. Requiere Node 22.
v3.2.0 Jun 2026 Heatmaps, Session Replay mejorado, informes de propiedades de eventos/sesiones, atributo data-auto-pageview.

Autoalojamiento vs Umami Cloud

Umami autoalojado = eventos ilimitados, propiedad total de los datos, gratis (licencia MIT). Umami Cloud empieza gratis (100K eventos/mes, 1 sitio web) pero el plan Pro cuesta $20/mes por 1M de eventos y 20 sitios web. Para un operador individual con unos pocos sitios, autoalojar en un VPS de ~€4/mes es el claro ganador.

PostgreSQL v12.14+ es obligatorio para v3. La imagen postgres:15-alpine usada en esta guía lo cumple.

Conclusión

Ya tienes Umami Analytics instalado en Docker, con una configuración de tracker funcional, opciones para evitar los bloqueadores de anuncios y un flujo sólido de actualizaciones y copias de seguridad. Umami es gratuito, usa unos 300 MB de RAM en total y te da la propiedad completa de tus datos de analítica. Sin acceso de terceros, sin dolores de cabeza con el RGPD.

Puntos clave a recordar: haz copia de seguridad antes de cada actualización mayor, fija la versión de la imagen para reproducibilidad y cambia la contraseña por defecto inmediatamente después del primer inicio de sesión. Si quieres explorar otras opciones, también puedes instalar Plausible Analytics con un enfoque de Docker similar, o monitorizar los recursos de tu servidor para vigilar tu VPS.

Explorar más herramientas autoalojadas