Auto-alojar Uptime Kuma y Beszel en un VPS con Docker Compose

17 min de lectura·Matthieu·uptime-kumabeszelmonitoringself-hostingdocker-composedocker|

Despliega Uptime Kuma para monitorización de disponibilidad externa y Beszel para métricas del servidor en un solo VPS. Configuración con Docker Compose, notificaciones, alertas, páginas de estado y seguridad reforzada.

Tu aplicación está desplegada. Los usuarios se están registrando. Pero no tienes ni idea de si se cae a las 3 de la madrugada, o si el disco se llena mientras duermes.

Esta guía despliega dos herramientas de monitorización en un solo VPS usando Docker Compose:

  • Uptime Kuma (84k+ estrellas en GitHub) monitoriza la disponibilidad externa: endpoints HTTP, puertos TCP, registros DNS, certificados SSL.
  • Beszel (~20k estrellas) monitoriza la salud interna del servidor: CPU, RAM, disco, red y estadísticas Docker por contenedor.

Juntos consumen unos 150-180 MB de RAM. Un stack de Prometheus + Grafana + node_exporter haciendo lo mismo necesita 800+ MB.

Requisitos previos: Un VPS con Debian 12 o Ubuntu 24.04 con Docker y Docker Compose instalados. Un reverse proxy (Caddy o Nginx) gestionando TLS. Un nombre de dominio con DNS apuntando a tu servidor. Esta guía usa Caddy para los ejemplos de reverse proxy. Docker en producción en un VPS: qué falla y cómo solucionarlo

¿Cuál es la diferencia entre Uptime Kuma y Beszel?

Uptime Kuma monitoriza la disponibilidad externa. Te dice si tus sitios web, APIs y servicios son accesibles desde fuera de tu servidor. Beszel monitoriza la salud interna del servidor: uso de CPU, RAM, espacio en disco, ancho de banda de red y estadísticas Docker por contenedor. Un servidor web puede reportar CPU baja y mucha memoria libre mientras es completamente inaccesible debido a un firewall mal configurado o un certificado TLS caducado. Necesitas ambas herramientas.

Característica Uptime Kuma Beszel
Qué monitoriza HTTP, TCP, DNS, ping, expiración SSL, push/heartbeat CPU, RAM, disco, red, temperatura, contenedores Docker
Arquitectura Contenedor único, interfaz web Hub + agente (un agente por servidor monitorizado)
Base de datos SQLite (por defecto) o MariaDB PocketBase (SQLite integrado)
Canales de notificación 90+ (email, Telegram, Discord, Slack, webhooks, etc.) Email (SMTP vía PocketBase)
Páginas de estado Sí, públicas con dominio personalizado No
Uso de RAM ~80-120 MB Hub: ~10-50 MB, Agente: ~25 MB
Estrellas en GitHub 84k+ ~20k

Ninguna de las dos herramientas reemplaza a la otra. Uptime Kuma detecta fallos externos. Beszel detecta el agotamiento de recursos antes de que cause fallos externos.

¿Cuánta RAM usa el stack de monitorización?

Uptime Kuma v2.x usa aproximadamente 80-120 MB de RAM dependiendo del número de monitores. El hub de Beszel añade 10-50 MB y cada agente usa unos 25 MB. El stack combinado funciona cómodamente en un VPS de 1 GB, usando unos 150-180 MB en total. En comparación, Prometheus + Grafana + node_exporter juntos necesitan 800+ MB solo en reposo.

Stack RAM en reposo Tiempo de configuración Ideal para
Uptime Kuma + Beszel ~150-180 MB 30 minutos Instalaciones auto-alojadas pequeñas y medianas
Prometheus + Grafana + node_exporter ~800 MB+ 2-4 horas Infraestructura a gran escala con consultas personalizadas
Netdata ~300-400 MB 15 minutos Métricas en tiempo real, servidor único

¿Cómo instalo Uptime Kuma con Docker Compose?

Uptime Kuma funciona como un contenedor único sirviendo su interfaz web en el puerto 3001. El archivo Compose siguiente fija la versión mayor 2, enlaza solo a localhost, establece límites de recursos y añade un health check.

Crea el directorio del proyecto:

mkdir -p /opt/uptime-kuma && cd /opt/uptime-kuma

Crea el archivo Compose:

# /opt/uptime-kuma/compose.yaml
services:
  uptime-kuma:
    image: louislam/uptime-kuma:2
    container_name: uptime-kuma
    restart: unless-stopped
    ports:
      - "127.0.0.1:3001:3001"
    volumes:
      - ./data:/app/data
    environment:
      - TZ=Europe/Berlin
    deploy:
      resources:
        limits:
          memory: 256m
          cpus: "0.5"
    healthcheck:
      test: ["CMD", "node", "/app/extra/healthcheck.js"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 15s

El binding 127.0.0.1:3001:3001 asegura que el contenedor solo escuche en localhost. Sin el prefijo 127.0.0.1, Docker publica el puerto en todas las interfaces, saltándose tu firewall. Docker ignora UFW: 4 soluciones probadas para tu VPS

Inicia el contenedor:

docker compose up -d
[+] Running 1/1
 ✔ Container uptime-kuma  Started
docker compose ps
NAME           IMAGE                      COMMAND                  SERVICE        CREATED          STATUS                    PORTS
uptime-kuma    louislam/uptime-kuma:2     "/usr/bin/dumb-init …"   uptime-kuma    10 seconds ago   Up 9 seconds (healthy)    127.0.0.1:3001->3001/tcp

El estado (healthy) significa que el health check integrado ha pasado. Si ves (starting), espera 15 segundos a que termine el start_period.

Reverse proxy con Caddy

Añade una entrada a tu Caddyfile:

# /etc/caddy/Caddyfile (append)
status.example.com {
    reverse_proxy localhost:3001
}

Recarga Caddy:

systemctl reload caddy

Caddy obtiene automáticamente un certificado TLS de Let's Encrypt. Abre https://status.example.com en tu navegador. Uptime Kuma te pide crear una cuenta de administrador en el primer acceso.

Activa 2FA inmediatamente

Después de crear tu cuenta de administrador, ve a Settings > Security > Two-Factor Authentication y actívalo. El panel de Uptime Kuma da acceso de lectura a toda la topología de tu infraestructura. Cualquiera que comprometa el login ve cada endpoint monitorizado. Configura 2FA antes de añadir monitores.

¿Cómo configuro Beszel para monitorizar mi VPS?

Beszel usa una arquitectura hub-agente. El hub es el panel web que almacena datos y muestra métricas. El agente se ejecuta en cada servidor que quieres monitorizar y transmite métricas al hub. Cuando ambos corren en el mismo VPS, se comunican a través de un socket Unix en lugar de la red.

Crea el directorio del proyecto:

mkdir -p /opt/beszel && cd /opt/beszel

Crea el archivo Compose:

# /opt/beszel/compose.yaml
services:
  beszel-hub:
    image: henrygd/beszel:0.18
    container_name: beszel-hub
    restart: unless-stopped
    ports:
      - "127.0.0.1:8090:8090"
    environment:
      - APP_URL=https://beszel.example.com
    volumes:
      - ./beszel_data:/beszel_data
      - ./beszel_socket:/beszel_socket
    deploy:
      resources:
        limits:
          memory: 128m
          cpus: "0.25"

  beszel-agent:
    image: henrygd/beszel-agent:0.18
    container_name: beszel-agent
    restart: unless-stopped
    network_mode: host
    volumes:
      - ./beszel_agent_data:/var/lib/beszel-agent
      - ./beszel_socket:/beszel_socket
      - /var/run/docker.sock:/var/run/docker.sock:ro
    environment:
      - LISTEN=/beszel_socket/beszel.sock
      - KEY=${BESZEL_KEY}
    deploy:
      resources:
        limits:
          memory: 64m
          cpus: "0.15"

Sobre esta configuración:

  • La imagen del hub está fijada en 0.18, que incluye la corrección de CVE-2026-27734 (v0.18.4). Fijar a una versión menor previene cambios incompatibles inesperados mientras se siguen recibiendo actualizaciones de parches.
  • El agente usa network_mode: host para poder leer las estadísticas de las interfaces de red del host. Esto es necesario para una monitorización precisa del ancho de banda.
  • El agente y el hub comparten un volumen beszel_socket para la comunicación por socket Unix. Esto evita exponer el puerto 45876 en la red cuando ambos corren en el mismo servidor.
  • El socket de Docker se monta en modo solo lectura (:ro). Más detalles en la sección de seguridad más abajo.

Seguridad del socket Docker

Montar /var/run/docker.sock da al agente acceso a la API de Docker. Incluso con :ro, esto es efectivamente acceso equivalente a root porque la API de Docker puede crear contenedores privilegiados, leer variables de entorno de cualquier contenedor y acceder a volúmenes. Hardening de seguridad en Docker: modo rootless, seccomp y AppArmor en un VPS

Beszel necesita el socket para recopilar estadísticas de CPU, memoria y red por contenedor. Si no necesitas monitorización de contenedores, elimina completamente el montaje del socket Docker.

CVE-2026-27734 (corregido en v0.18.4) demostró este riesgo: un usuario autenticado de Beszel podía atravesar la API de Docker mediante IDs de contenedor no saneados, alcanzando endpoints arbitrarios como /version o /containers/json. La corrección sanea toda la entrada del usuario antes de construir las URLs de la API de Docker. Asegúrate de ejecutar v0.18.4 o posterior. La etiqueta 0.18 en el archivo Compose anterior resuelve a v0.18.4 (último parche a fecha de marzo de 2026).

Generar la clave del agente

Inicia primero el hub:

cd /opt/beszel
docker compose up -d beszel-hub

Abre el hub en https://beszel.example.com (después de configurar tu reverse proxy, siguiente sección). Crea una cuenta de administrador. Ve a Add System en el panel. El hub muestra una clave pública. Cópiala.

Crea un archivo .env para el agente:

# /opt/beszel/.env
BESZEL_KEY="ssh-ed25519 AAAA... (paste the key from the hub UI)"
chmod 600 /opt/beszel/.env

Ahora inicia el agente:

docker compose up -d beszel-agent

De vuelta en la interfaz del hub, añade el sistema usando la ruta del socket /beszel_socket/beszel.sock. En segundos aparecen las métricas de CPU, RAM, disco y contenedores Docker.

Reverse proxy para Beszel

Añade a tu Caddyfile:

# /etc/caddy/Caddyfile (append)
beszel.example.com {
    reverse_proxy localhost:8090
}
systemctl reload caddy

¿Cómo configuro las notificaciones en Uptime Kuma?

Uptime Kuma soporta más de 90 canales de notificación. Los tres más comunes para auto-alojamiento son email (SMTP), Telegram y Discord.

Notificaciones por email SMTP

Ve a Settings > Notifications > Setup Notification. Selecciona Email (SMTP) como tipo.

Campo Valor
Hostname Tu servidor SMTP (ej.: smtp.example.com)
Port 587 (STARTTLS) o 465 (TLS implícito)
Security STARTTLS o TLS
Username Tu nombre de usuario SMTP
Password Tu contraseña SMTP
From Email monitoring@example.com
To Email tu@example.com

Haz clic en Test para enviar una notificación de prueba antes de guardar. Uptime Kuma envía la prueba inmediatamente. Si falla, comprueba tus credenciales SMTP y las reglas del firewall (el puerto 587 saliente debe estar abierto).

Notificaciones de bot de Telegram

  1. Envía un mensaje a @BotFather en Telegram y crea un nuevo bot con /newbot.
  2. Copia el token del bot (formato: 123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11).
  3. Inicia un chat con tu bot y envía cualquier mensaje.
  4. Obtén tu chat ID: abre https://api.telegram.org/bot<YOUR_TOKEN>/getUpdates en tu navegador. El campo chat.id en la respuesta es tu chat ID.
  5. En Uptime Kuma, añade una notificación de Telegram. Pega el token del bot y el chat ID. Haz clic en Test.

Notificaciones de webhook de Discord

  1. En tu servidor de Discord, ve a Server Settings > Integrations > Webhooks > New Webhook.
  2. Ponle un nombre (ej.: "Uptime Kuma"), elige un canal y copia la URL del webhook.
  3. En Uptime Kuma, añade una notificación de Discord y pega la URL del webhook. Haz clic en Test.

Establece la notificación predeterminada para que se aplique a todos los nuevos monitores marcando Default Enabled. Así cada monitor que crees hereda el canal de notificación sin configuración manual.

¿Qué tipos de monitores de Uptime Kuma debería configurar?

Uptime Kuma soporta muchos tipos de monitores. Aquí están los cuatro más útiles para un stack auto-alojado:

  1. HTTP(s) - Comprueba una URL, opcionalmente busca una palabra clave en el cuerpo de la respuesta. Úsalo para aplicaciones web, APIs y paneles. Establece la palabra clave en una cadena que solo aparece cuando la aplicación está sana (ej.: el nombre de tu aplicación en el título HTML).
  2. TCP - Se conecta a un host:puerto. Úsalo para bases de datos (PostgreSQL en 5432), servidores de correo (SMTP en 587) o cualquier servicio sin endpoint HTTP.
  3. DNS - Resuelve un nombre de host y comprueba que el resultado coincide con una IP esperada. Detecta secuestro de DNS o registros mal configurados.
  4. Push / Heartbeat - Uptime Kuma genera una URL. Tu tarea cron o script de backup la llama en caso de éxito. Si la URL no se llama dentro del intervalo, Uptime Kuma dispara una alerta. Es la única forma de monitorizar scripts que no tienen un puerto en escucha.

Ejemplo de monitor push para tareas cron

Crea un monitor Push en Uptime Kuma. Establece el intervalo de heartbeat en la frecuencia de tu cron más un período de gracia. Copia la URL push.

Añade la llamada curl al final de tu script de backup:

#!/bin/bash
# /opt/scripts/backup.sh
pg_dump mydb | gzip > /backups/mydb-$(date +%F).sql.gz

# Signal success to Uptime Kuma
curl -fsS -o /dev/null "https://status.example.com/api/push/abc123?status=up&msg=backup-ok"

Si el script falla antes de llegar a la línea curl, o si cron no se ejecuta, Uptime Kuma marca el monitor como caído tras expirar el intervalo.

¿Cómo configuro alertas en Beszel para CPU y disco?

Las alertas de Beszel te notifican cuando las métricas del servidor superan un umbral. Haz clic en el icono de campana junto a cualquier sistema en el panel para configurar alertas.

Umbrales recomendados para un VPS pequeño (2-4 vCPU, 4-8 GB de RAM):

Métrica Advertencia Crítico Por qué
CPU > 70 % durante 5 min > 90 % durante 2 min CPU alta sostenida significa procesos descontrolados o instancia subdimensionada
RAM > 80 % durante 5 min > 90 % durante 2 min Linux empieza a hacer swap intensivo por encima del 85 %, destruyendo el rendimiento
Disco > 80 % > 90 % Las imágenes Docker, logs y bases de datos crecen silenciosamente. Al 100 % los servicios se caen
Ancho de banda > 80 % del límite del plan > 95 % Previene cargos por exceso o throttling

Estos umbrales son intencionalmente más bajos que los valores por defecto empresariales. En un VPS pequeño tienes menos margen. Un pico de 70 % a 100 % de CPU tarda segundos, no minutos.

Configurar SMTP para alertas de Beszel

Beszel usa PocketBase como backend. SMTP se configura a través del panel de administración de PocketBase:

  1. Ve a https://beszel.example.com/_/ (la URL de administración de PocketBase, observa el guion bajo).
  2. Inicia sesión con las credenciales de administrador que creaste durante la instalación.
  3. Ve a Settings > Mail settings.
  4. Activa Use SMTP mail server.
  5. Introduce tu host SMTP, puerto, nombre de usuario y contraseña.
  6. Establece la dirección del remitente.
  7. Haz clic en Save y Send test email.

¿Cómo creo una página de estado pública con Uptime Kuma?

Uptime Kuma puede servir páginas de estado públicas que muestran la disponibilidad de tus servicios. Son útiles para comunicar el uptime a tus usuarios sin exponer tu panel de monitorización.

  1. Ve a Status Pages en la barra lateral izquierda.
  2. Haz clic en New Status Page. Elige un nombre y slug (ej.: status).
  3. Añade grupos (ej.: "Servicios Web", "APIs", "Infraestructura").
  4. Arrastra monitores a cada grupo.
  5. Publica la página. Es accesible en https://status.example.com/status/<slug>.

Dominio personalizado para la página de estado

Si quieres que https://status.example.com sirva la página de estado directamente, establece la página de estado como predeterminada en la configuración de Uptime Kuma. La ruta raíz muestra entonces la página pública mientras el panel permanece en /dashboard.

Las páginas de estado no requieren autenticación. No pongas monitores en un grupo de página de estado si revelar la existencia del endpoint es una preocupación de seguridad.

Gestión de incidentes

Cuando un servicio se cae, Uptime Kuma lo muestra automáticamente como degradado en la página de estado. También puedes crear incidentes manuales:

  1. Ve a Status Pages, selecciona tu página, haz clic en Create Incident.
  2. Escribe un título y descripción (ej.: "Mantenimiento de base de datos, estimado 15 minutos").
  3. Establece el estilo en info, warning, danger o primary.
  4. Publica. El banner de incidente aparece en la parte superior de la página de estado pública.

Resuelve el incidente cuando termine. Uptime Kuma mantiene un historial de incidentes pasados para que tus usuarios puedan ver tu historial operativo.

¿Cómo monitorizo mi stack de monitorización desde fuera?

Si tu VPS se cae, Uptime Kuma y Beszel se caen con él. Te enteras de la caída al mismo tiempo que tus usuarios. La solución: un watchdog externo que monitorice tu instancia de Uptime Kuma desde otra ubicación.

Opción 1: UptimeRobot (nivel gratuito)

  1. Crea una cuenta gratuita en UptimeRobot.
  2. Añade un nuevo monitor: tipo HTTP(s), URL https://status.example.com/api/status-page/heartbeat/<slug>.
  3. Establece el intervalo de comprobación en 5 minutos.
  4. Configura notificaciones por email o Telegram.

El endpoint /api/status-page/heartbeat/<slug> devuelve un payload JSON con el estado. UptimeRobot lo comprueba y te alerta si tu instancia de Uptime Kuma se vuelve inaccesible.

Opción 2: Healthchecks.io (nivel gratuito)

Healthchecks.io funciona con el modelo push. Crea un check, copia la URL de ping y añade una tarea cron en tu VPS:

# /etc/cron.d/monitoring-heartbeat
*/5 * * * * root curl -fsS --retry 3 -o /dev/null https://hc-ping.com/your-uuid-here

Si el ping cron deja de llegar (porque tu servidor está caído), Healthchecks.io te envía una alerta. Esto cubre el escenario en que todo tu VPS se vuelve inaccesible.

Opción 3: monitorizar desde un segundo VPS

Si administras múltiples servidores, instala Uptime Kuma en un VPS diferente y haz que cada instancia monitorice a la otra. Este es el enfoque más fiable porque controlas ambos endpoints y no hay dependencia de un servicio gratuito de terceros.

Refuerzo de seguridad

Reglas de firewall

Si ejecutas el agente Beszel en modo standalone en un servidor remoto (sin usar el método de socket Unix), escucha en el puerto 45876. Solo el hub necesita alcanzar este puerto:

ufw allow from <hub-ip-address> to any port 45876 proto tcp comment "Beszel agent"
ufw status numbered
Status: active

     To                         Action      From
     --                         ------      ----
[ 1] 22/tcp                     ALLOW IN    Anywhere
[ 2] 80/tcp                     ALLOW IN    Anywhere
[ 3] 443/tcp                    ALLOW IN    Anywhere
[ 4] 45876/tcp                  ALLOW IN    <hub-ip-address>

No abras el puerto 45876 al mundo. El agente expone métricas del sistema sin autenticación en ese puerto. Se basa en la clave SSH del hub para la verificación, pero la restricción a nivel de red añade defensa en profundidad.

Para la configuración de un solo VPS en esta guía, el puerto 45876 no es necesario en absoluto porque el hub y el agente se comunican a través de un socket Unix.

Uptime Kuma: deshabilitar la autenticación por contraseña para la API

Si solo accedes a Uptime Kuma a través de su interfaz web, deshabilita el acceso API en Settings > Security > API Key. Menos endpoints expuestos, menos cosas que parchear.

Ocultar la versión

Uptime Kuma y Beszel exponen información de versión en su interfaz web por defecto. Tu reverse proxy no debería empeorar esto. En tu Caddyfile, Caddy ya omite las cabeceras Server por defecto. Si usas Nginx en su lugar:

server_tokens off;

La divulgación de versión ayuda a los atacantes a apuntar a vulnerabilidades conocidas. Mantenla al mínimo.

¿Cómo hago copias de seguridad de los datos de Uptime Kuma y Beszel?

Ambas herramientas usan bases de datos basadas en SQLite. Los archivos SQLite no se pueden copiar de forma segura mientras la aplicación escribe en ellos. Usa los métodos de backup adecuados.

Backup de Uptime Kuma

Uptime Kuma almacena todo en /app/data (mapeado a ./data en el archivo Compose). El backup integrado exporta un archivo JSON:

  1. Ve a Settings > Backup.
  2. Haz clic en Export. Guarda el archivo JSON fuera del servidor.

Para backups automatizados, detén brevemente el contenedor o usa el backup en línea de SQLite:

sqlite3 /opt/uptime-kuma/data/kuma.db ".backup '/opt/backups/kuma-$(date +%F).db'"

Backup de Beszel

Beszel usa PocketBase. Haz backup del directorio de datos:

sqlite3 /opt/beszel/beszel_data/data.db ".backup '/opt/backups/beszel-$(date +%F).db'"

Almacena los backups fuera del servidor. Un stack de monitorización que pierde su historial cuando el disco muere no está monitorizando nada. Copia de seguridad y restauración de volúmenes Docker en un VPS

¿Cómo actualizo Uptime Kuma y Beszel de forma segura?

Fija la versión menor, no latest. Esto evita que lleguen cambios incompatibles sin tu conocimiento.

# Update Uptime Kuma
cd /opt/uptime-kuma
docker compose pull
docker compose up -d
[+] Pulling 1/1
 ✔ uptime-kuma Pulled
[+] Running 1/1
 ✔ Container uptime-kuma  Started
docker compose ps

Comprueba que la columna STATUS muestre (healthy). Si la nueva versión causa problemas, fija la versión anterior en compose.yaml y recrea:

# In compose.yaml, change the image tag to the previous version:
# image: louislam/uptime-kuma:2.2.1
docker compose up -d

El mismo proceso se aplica a Beszel. Siempre haz backup antes de actualizar.

Estrategia de fijación de imágenes

La etiqueta louislam/uptime-kuma:2 sigue la última versión 2.x. Es conveniente pero significa que docker compose pull puede saltar de 2.2.1 a 2.3.0 sin aviso. En producción, fija una versión menor específica:

image: louislam/uptime-kuma:2.2

Consulta las notas de la versión antes de hacer pull. Uptime Kuma publica sus versiones en GitHub. Beszel hace lo mismo en su página de releases.

Suscríbete a las notificaciones de versiones de ambos repositorios (Watch > Custom > Releases en GitHub) para saber cuándo se publican parches de seguridad.

Límites de recursos, healthchecks y políticas de reinicio en Docker Compose

Solución de problemas

Uptime Kuma muestra (unhealthy) en docker compose ps:

docker compose logs uptime-kuma --tail 50

Causas comunes: base de datos SQLite corrupta (restaura desde backup), conflicto de puertos (otro servicio en 3001) o memoria insuficiente (aumenta el límite de recursos).

El agente Beszel no se conecta al hub:

docker compose logs beszel-agent --tail 50

Comprueba que la KEY en .env coincida con la clave mostrada en el diálogo Add System del hub. Si usas sockets Unix, verifica que la ruta del volumen compartido coincida en ambos servicios.

Beszel no muestra estadísticas de contenedores Docker:

Falta el montaje del socket Docker o la ruta del socket Docker es incorrecta. Comprueba:

ls -la /var/run/docker.sock
srw-rw---- 1 root docker 0 Mar 20 10:00 /var/run/docker.sock

El socket debe existir y el contenedor debe tener acceso de lectura. El montaje :ro en el archivo Compose se encarga de esto.

Las notificaciones no llegan:

Para SMTP: comprueba que el puerto 587 (o 465) saliente no esté bloqueado por tu proveedor de hosting. Algunos proveedores bloquean el SMTP saliente por defecto. Prueba con:

nc -zv smtp.example.com 587
Connection to smtp.example.com 587 port [tcp/submission] succeeded!

Para Telegram: verifica el token del bot y el chat ID. El chat ID debe ser un número, no el nombre de usuario del bot.

Alto uso de memoria:

El número de monitores importa. Uptime Kuma v2.x usa unos 100 MB en reposo. Cada monitor HTTP añade estado de conexión. Si superas los 100 monitores con un límite de memoria de 256 MB, aumenta el límite o reparte entre instancias.

Comprueba el uso real:

docker stats --no-stream uptime-kuma beszel-hub beszel-agent
CONTAINER ID   NAME           CPU %     MEM USAGE / LIMIT   MEM %     NET I/O       BLOCK I/O   PIDS
abc123         uptime-kuma    0.15%     99MiB / 256MiB      38.67%    1.2kB / 2kB   0B / 0B     8
def456         beszel-hub     0.08%     10MiB / 128MiB      7.81%     1kB / 1.5kB   0B / 745kB  8
ghi789         beszel-agent   0.05%     22MiB / 64MiB       34.38%    0B / 0B       0B / 0B     5

Rotación de logs en Docker: evite que los registros llenen el disco de su VPS