Cachet es una página de estado de código abierto escrita en PHP con Laravel. Permite mostrar el estado de tus servicios agrupados en componentes, publicar incidentes y mantenimientos programados, representar métricas y avisar a los suscriptores por correo. En este tutorial desplegarás Cachet con Docker Compose y PostgreSQL en Ubuntu 24.04, lo publicarás en tu dominio con Nginx y HTTPS, y usarás su API para crear componentes, abrir y resolver incidentes y enviar métricas de forma automática.
Requisitos previos
Para seguir esta guía necesitas:
- Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath, con al menos 1 GB de RAM.
- Un usuario no root con privilegios
sudo. - Docker Engine y el plugin Docker Compose instalados desde el repositorio oficial.
- Un subdominio, por ejemplo
status.your_domain, con un registro DNS A apuntando a la IP del servidor. - Los puertos 80 y 443 abiertos.
- Opcional: los datos de un servidor SMTP para enviar notificaciones a los suscriptores.
Notaesta guía usa la imagen oficial
cachethq/docker, que empaqueta la rama 2.x de Cachet. Es la versión estable y la que documenta la API v1 que se usa aquí. La rama 3.x está en desarrollo y cambia la instalación.
Conviene alojar la página de estado en un servidor distinto al de los servicios que monitoriza. Si ese servidor cae, la página de estado debe seguir disponible para informar.
Paso 1: Preparar el directorio y los secretos
Crea un directorio para el proyecto:
sudo mkdir -p /opt/cachet
sudo chown "$USER":"$USER" /opt/cachet
cd /opt/cachet
Cachet necesita una clave de aplicación de Laravel (APP_KEY), que cifra sesiones y datos, y la base de datos necesita una contraseña. Genera ambas con openssl y guárdalas en un archivo .env que Docker Compose leerá automáticamente:
cat > .env <<EOF
APP_KEY=base64:$(openssl rand -base64 32)
DB_PASSWORD=$(openssl rand -hex 24)
EOF
chmod 600 .env
cat .env
APP_KEY=base64:kR3v1pQ...=
DB_PASSWORD=9f2c4e...
Guarda una copia de APP_KEY en un lugar seguro: si la pierdes, Cachet no podrá descifrar los datos existentes.
Paso 2: Crear el archivo de Docker Compose
Crea compose.yaml con dos servicios: PostgreSQL para los datos y Cachet, que escucha en el puerto 8000 del contenedor:
nano /opt/cachet/compose.yaml
services:
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: cachet
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: cachet
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U cachet"]
interval: 10s
timeout: 5s
retries: 5
cachet:
image: cachethq/docker:latest
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
ports:
- "127.0.0.1:8000:8000"
environment:
DB_DRIVER: pgsql
DB_HOST: postgres
DB_PORT: "5432"
DB_DATABASE: cachet
DB_USERNAME: cachet
DB_PASSWORD: ${DB_PASSWORD}
DB_PREFIX: chq_
APP_KEY: ${APP_KEY}
APP_ENV: production
APP_DEBUG: "false"
APP_LOG: errorlog
DEBUG: "false"
MAIL_DRIVER: smtp
MAIL_HOST: smtp.your_domain
MAIL_PORT: "587"
MAIL_USERNAME: status@your_domain
MAIL_PASSWORD: your_smtp_password
MAIL_ADDRESS: status@your_domain
MAIL_NAME: "Estado de servicios"
MAIL_ENCRYPTION: tls
volumes:
pgdata:
Detalles importantes:
- Cachet se publica solo en
127.0.0.1:8000. Nginx será el único punto de entrada público. Docker gestiona sus propias reglas de iptables, así que un puerto publicado en todas las interfaces quedaría abierto aunque UFW lo bloquee. - Las variables
MAIL_*configuran el envío de correo. Sustituye los valores de ejemplo por los de tu proveedor SMTP o elimina ese bloque si no vas a usar suscriptores. - Las contraseñas se leen del archivo
.env, no se escriben en elcompose.yaml.
Paso 3: Arrancar Cachet
Descarga las imágenes y arranca los contenedores en segundo plano:
docker compose up -d
En el primer arranque, Cachet crea las tablas en PostgreSQL. Comprueba el estado de los servicios:
docker compose ps
NAME IMAGE SERVICE STATUS PORTS
cachet-cachet-1 cachethq/docker:latest cachet Up 40 seconds 127.0.0.1:8000->8000/tcp
cachet-postgres-1 postgres:16-alpine postgres Up 51 seconds (healthy)
Verifica que la API responde con el endpoint ping, que no requiere autenticación:
curl -s http://127.0.0.1:8000/api/v1/ping
{"data":"Pong!"}
Si no obtienes respuesta, revisa los logs con docker compose logs --tail 50 cachet.
Paso 4: Publicar Cachet con Nginx y HTTPS
Instala Nginx y Certbot y permite el tráfico web en UFW:
sudo apt update
sudo apt install -y nginx certbot python3-certbot-nginx
sudo ufw allow 'Nginx Full'
Crea el bloque de servidor, sustituyendo status.your_domain por tu subdominio:
sudo nano /etc/nginx/sites-available/cachet
server {
listen 80;
listen [::]:80;
server_name status.your_domain;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Activa el sitio, valida la sintaxis y recarga Nginx:
sudo ln -s /etc/nginx/sites-available/cachet /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Solicita el certificado de Let's Encrypt. Certbot añadirá la configuración de TLS y la redirección de HTTP a HTTPS:
sudo certbot --nginx -d status.your_domain
Successfully deployed certificate for status.your_domain to /etc/nginx/sites-enabled/cachet
Congratulations! You have successfully enabled HTTPS on https://status.your_domain
Paso 5: Completar el asistente de instalación
Abre https://status.your_domain en el navegador. Cachet te redirige a /setup, un asistente de tres pantallas:
- Entorno: elige
Databasecomo controlador de caché y de sesiones, ySMTPcomo controlador de correo si has configurado las variablesMAIL_*. - Página de estado: nombre del sitio, dominio (
https://status.your_domain), zona horaria e idioma. Selecciona Español si tus usuarios son hispanohablantes. - Cuenta de administrador: nombre de usuario, correo y una contraseña robusta.
Al terminar, entra en https://status.your_domain/dashboard con esa cuenta. La página pública estará vacía hasta que crees componentes.
Paso 6: Obtener el token de la API
Todas las operaciones de escritura en la API v1 se autentican con la cabecera X-Cachet-Token. Cada usuario tiene su token en el panel: haz clic en tu nombre de usuario (perfil) y copia el valor del campo API Token.
Guárdalo en variables de tu sesión para los ejemplos siguientes:
export CACHET_URL="https://status.your_domain/api/v1"
export CACHET_TOKEN="your_api_token"
Comprueba que funciona listando los componentes:
curl -s "$CACHET_URL/components" -H "X-Cachet-Token: $CACHET_TOKEN"
{"meta":{"pagination":{"total":0,...}},"data":[]}
Paso 7: Crear grupos y componentes
Los componentes representan cada servicio que muestras (web, API, correo...) y los grupos los organizan en la página. Crea un grupo:
curl -s -X POST "$CACHET_URL/components/groups" \
-H "X-Cachet-Token: $CACHET_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Plataforma", "order": 1, "collapsed": 0}'
La respuesta incluye el id del grupo. Crea un componente dentro de él (sustituye 1 por el id devuelto):
curl -s -X POST "$CACHET_URL/components" \
-H "X-Cachet-Token: $CACHET_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "API", "description": "API REST pública", "status": 1, "group_id": 1, "enabled": true}'
{"data":{"id":1,"name":"API","description":"API REST pública","status":1,"status_name":"Operational",...}}
El campo status de un componente admite estos valores:
| Valor | Estado |
|---|---|
| 1 | Operativo |
| 2 | Problemas de rendimiento |
| 3 | Interrupción parcial |
| 4 | Interrupción grave |
Para cambiar el estado de un componente existente, envía un PUT con el nuevo valor:
curl -s -X PUT "$CACHET_URL/components/1" \
-H "X-Cachet-Token: $CACHET_TOKEN" \
-H "Content-Type: application/json" \
-d '{"status": 2}'
Recarga la página pública: el componente API aparece dentro del grupo Plataforma con el estado actualizado.
Paso 8: Abrir y resolver un incidente
Un incidente informa a los usuarios de un problema y, opcionalmente, cambia a la vez el estado del componente afectado. Crea uno en estado "Investigando" que marque la API con problemas de rendimiento y avise a los suscriptores:
curl -s -X POST "$CACHET_URL/incidents" \
-H "X-Cachet-Token: $CACHET_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Latencia elevada en la API",
"message": "Estamos investigando un aumento de la latencia en algunas peticiones.",
"status": 1,
"visible": 1,
"component_id": 1,
"component_status": 2,
"notify": true
}'
Los estados de un incidente son:
| Valor | Estado |
|---|---|
| 0 | Programado (mantenimiento) |
| 1 | Investigando |
| 2 | Identificado |
| 3 | En observación |
| 4 | Resuelto |
Cuando el problema esté solucionado, marca el incidente como resuelto y devuelve el componente a operativo (sustituye 1 por el id del incidente):
curl -s -X PUT "$CACHET_URL/incidents/1" \
-H "X-Cachet-Token: $CACHET_TOKEN" \
-H "Content-Type: application/json" \
-d '{"status": 4, "message": "El problema está resuelto. La latencia ha vuelto a valores normales.", "component_id": 1, "component_status": 1}'
Los incidentes también se pueden gestionar desde Dashboard > Incidentes, que es lo más cómodo para comunicaciones manuales. La API es útil para que tus herramientas de monitorización actualicen la página de forma automática.
Paso 9: Enviar métricas automáticamente
Cachet puede mostrar gráficas, por ejemplo el tiempo de respuesta de tu web. Crea la métrica en Dashboard > Métricas (nombre "Tiempo de respuesta", sufijo ms, cálculo por media) o con la API:
curl -s -X POST "$CACHET_URL/metrics" \
-H "X-Cachet-Token: $CACHET_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Tiempo de respuesta", "suffix": "ms", "description": "Tiempo de respuesta de la web", "default_value": 0, "calc_type": 1, "display_chart": 1}'
Anota el id de la métrica. Ahora crea un script que mida el tiempo de respuesta y lo envíe como punto de la métrica. Guarda primero el token en un archivo solo legible por root, para no dejarlo en el script ni en el crontab:
sudo install -m 600 /dev/null /etc/cachet-metrics.env
sudo nano /etc/cachet-metrics.env
CACHET_URL=https://status.your_domain/api/v1
CACHET_TOKEN=your_api_token
METRIC_ID=1
TARGET_URL=https://your_domain
Crea el script:
sudo nano /usr/local/bin/cachet-metric.sh
#!/usr/bin/env bash
set -euo pipefail
# shellcheck source=/dev/null
source /etc/cachet-metrics.env
# Tiempo total de la petición en segundos, convertido a milisegundos enteros
seconds=$(curl -fsS -o /dev/null -w '%{time_total}' --max-time 10 "$TARGET_URL")
ms=$(awk -v s="$seconds" 'BEGIN { printf "%d", s * 1000 }')
curl -fsS -o /dev/null -X POST "$CACHET_URL/metrics/$METRIC_ID/points" \
-H "X-Cachet-Token: $CACHET_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"value\": $ms, \"timestamp\": $(date +%s)}"
Hazlo ejecutable y pruébalo a mano:
sudo chmod 755 /usr/local/bin/cachet-metric.sh
sudo /usr/local/bin/cachet-metric.sh && echo OK
OK
Si la web no responde, curl -f devuelve un error, el script termina sin enviar un valor falso y el punto simplemente no se registra. Programa la ejecución cada minuto con cron:
echo '* * * * * root /usr/local/bin/cachet-metric.sh' | sudo tee /etc/cron.d/cachet-metric
Tras unos minutos, la gráfica aparecerá en la página pública bajo los componentes.
Paso 10: Hacer copias de seguridad
Todos los datos de Cachet están en PostgreSQL. Haz un volcado comprimido con pg_dump desde el contenedor:
mkdir -p ~/backups
docker compose -f /opt/cachet/compose.yaml exec -T postgres pg_dump -U cachet cachet | gzip > ~/backups/cachet-$(date +%F).sql.gz
ls -lh ~/backups
Guarda también /opt/cachet/.env, que contiene APP_KEY. Sin esa clave, una restauración de la base de datos no será utilizable.
Solución de problemas
El navegador muestra un error 500. Revisa los logs de la aplicación con docker compose logs --tail 100 cachet. La causa más común es una APP_KEY vacía o con formato incorrecto: debe empezar por base64:.
Cachet no conecta con la base de datos. Comprueba que PostgreSQL está healthy con docker compose ps y que DB_PASSWORD no ha cambiado desde el primer arranque. PostgreSQL solo aplica POSTGRES_PASSWORD al crear el volumen; si cambias la contraseña después, debes cambiarla también dentro de la base de datos.
Los suscriptores no reciben correos. Verifica las variables MAIL_* y que tu servidor puede salir al puerto SMTP con nc -vz smtp.your_domain 587. Algunos proveedores bloquean el puerto 25 saliente, así que usa el 587 con TLS.
La API devuelve 401 Unauthorized. El token es incorrecto o falta la cabecera X-Cachet-Token. Cópialo de nuevo desde tu perfil en el panel.
Conclusión
Tienes Cachet funcionando en Ubuntu 24.04 con Docker Compose y PostgreSQL, publicado en tu dominio con HTTPS, con componentes, incidentes y una métrica que se actualiza cada minuto. Como siguientes pasos, puedes integrar la API con tu sistema de alertas (Uptime Kuma, Prometheus Alertmanager o Zabbix) para abrir incidentes automáticamente, programar mantenimientos desde el panel y automatizar las copias de seguridad con un temporizador de systemd.
