--- title: "Dashboards IoT con Grafana y PostgreSQL en Ubuntu 24.04" description: "Crea dashboards IoT con Grafana y PostgreSQL en Ubuntu 24.04: guarda lecturas de sensores, grafícalas con variables y recibe alertas de temperatura por email." category: "Automatización del Hogar e IoT" language: es source: https://cubepath.com/es/docs/automatización-del-hogar-e-iot/grafana-para-dashboard-y-visualizacion-iot updated: 2026-09-25 --- # Dashboards IoT con Grafana y PostgreSQL en Ubuntu 24.04 Grafana es la herramienta de visualización más usada para series temporales: se conecta a una base de datos y convierte las lecturas en gráficas, indicadores y alertas. En este tutorial montarás en Ubuntu 24.04 un panel IoT completo: una base de datos PostgreSQL con lecturas de sensores, Grafana instalado desde su repositorio oficial y publicado con HTTPS, una fuente de datos aprovisionada por archivo, un dashboard con una variable para elegir sensores y una alerta cuando la temperatura supera un umbral. ## Requisitos previos - Un servidor con Ubuntu 24.04 LTS y al menos 2 GB de RAM, por ejemplo un VPS de CubePath. - Un usuario no root con privilegios `sudo`. - Un dominio o subdominio (en esta guía `your_domain`) con un registro DNS A apuntando a la IP del servidor. - UFW activo con SSH permitido. - Opcional: una cuenta SMTP para enviar las alertas por correo. En esta guía los datos de ejemplo se generan con SQL. En un proyecto real los insertaría tu pasarela IoT, por ejemplo Node-RED o Telegraf leyendo de un broker MQTT. ## Paso 1: Preparar la base de datos de sensores PostgreSQL es una buena base para datos de sensores: la consulta con SQL estándar y Grafana la soporta de forma nativa. Si más adelante necesitas compresión y particionado automático, la extensión TimescaleDB funciona sobre el mismo esquema. Instala PostgreSQL 16 desde los repositorios de Ubuntu: ```bash sudo apt update sudo apt install postgresql ``` Abre una sesión de `psql` como el usuario administrador `postgres`: ```bash sudo -u postgres psql ``` Crea la base de datos, la tabla de lecturas y un índice por sensor y fecha: ```sql CREATE DATABASE iot; \c iot CREATE TABLE sensor_readings ( time timestamptz NOT NULL, sensor_id text NOT NULL, temperature double precision, humidity double precision ); CREATE INDEX ON sensor_readings (sensor_id, time DESC); ``` Inserta dos días de lecturas simuladas, una por minuto para tres sensores: ```sql INSERT INTO sensor_readings (time, sensor_id, temperature, humidity) SELECT t, s, 20 + 5 * sin(extract(epoch FROM t) / 3600) + random(), 50 + 10 * random() FROM generate_series(now() - interval '2 days', now(), interval '1 minute') AS t, unnest(ARRAY['salon', 'cocina', 'exterior']) AS s; ``` ```output INSERT 0 8643 ``` Crea un usuario de solo lectura para Grafana. Grafana no necesita escribir, y así un error en una consulta no puede modificar datos: ```sql CREATE ROLE grafana_reader LOGIN PASSWORD 'your_strong_password'; GRANT CONNECT ON DATABASE iot TO grafana_reader; GRANT SELECT ON sensor_readings TO grafana_reader; \q ``` Comprueba el acceso con el nuevo usuario (te pedirá su contraseña): ```bash psql "host=127.0.0.1 dbname=iot user=grafana_reader" -c "SELECT sensor_id, count(*) FROM sensor_readings GROUP BY 1;" ``` ```output sensor_id | count -----------+------- cocina | 2881 exterior | 2881 salon | 2881 (3 rows) ``` ## Paso 2: Instalar Grafana desde el repositorio oficial Añade la clave y el repositorio de Grafana: ```bash sudo apt install gnupg wget sudo install -m 0755 -d /etc/apt/keyrings wget -q -O - https://apt.grafana.com/gpg.key | gpg --dearmor | sudo tee /etc/apt/keyrings/grafana.gpg > /dev/null echo "deb [signed-by=/etc/apt/keyrings/grafana.gpg] https://apt.grafana.com stable main" | sudo tee /etc/apt/sources.list.d/grafana.list ``` Instala Grafana OSS: ```bash sudo apt update sudo apt install grafana ``` ## Paso 3: Configurar Grafana para funcionar detrás de un proxy Grafana escuchará solo en localhost y Nginx servirá HTTPS. Edita el archivo principal: ```bash sudo nano /etc/grafana/grafana.ini ``` En la sección `[server]`, descomenta y ajusta estas líneas (en este archivo los comentarios empiezan por `;`): ```ini [server] http_addr = 127.0.0.1 http_port = 3000 domain = your_domain root_url = https://your_domain/ ``` En la sección `[users]`, confirma que el registro abierto está desactivado: ```ini [users] allow_sign_up = false ``` Arranca Grafana y habilítalo al inicio: ```bash sudo systemctl daemon-reload sudo systemctl enable --now grafana-server ``` Comprueba que responde en local: ```bash curl -s http://127.0.0.1:3000/api/health ``` ```output { "database": "ok", "version": "12.2.0", "commit": "..." } ``` ## Paso 4: Publicar Grafana con Nginx y HTTPS Instala Nginx y Certbot: ```bash sudo apt install nginx certbot python3-certbot-nginx ``` Crea el sitio: ```bash sudo nano /etc/nginx/sites-available/grafana ``` ```nginx map $http_upgrade $connection_upgrade { default upgrade; '' close; } server { listen 80; listen [::]:80; server_name your_domain; location / { proxy_pass http://127.0.0.1:3000; proxy_set_header Host $host; } location /api/live/ { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; } } ``` La ruta `/api/live/` es el WebSocket que Grafana usa para actualizaciones en vivo. Activa el sitio, abre el firewall y solicita el certificado: ```bash sudo ln -s /etc/nginx/sites-available/grafana /etc/nginx/sites-enabled/ sudo rm /etc/nginx/sites-enabled/default sudo nginx -t sudo systemctl reload nginx sudo ufw allow 'Nginx Full' sudo certbot --nginx -d your_domain ``` Abre `https://your_domain`, entra con el usuario `admin` y la contraseña `admin`, y define una contraseña nueva cuando Grafana la pida. Hazlo nada más publicar el servicio. ## Paso 5: Aprovisionar la fuente de datos PostgreSQL Puedes añadir la fuente de datos desde la interfaz, pero definirla en un archivo de aprovisionamiento la hace reproducible y la mantiene bajo control de versiones. Crea el archivo: ```bash sudo nano /etc/grafana/provisioning/datasources/iot.yaml ``` ```yaml apiVersion: 1 datasources: - name: IoT PostgreSQL type: grafana-postgresql-datasource uid: iot-postgres url: 127.0.0.1:5432 user: grafana_reader jsonData: database: iot sslmode: disable postgresVersion: 1600 secureJsonData: password: your_strong_password isDefault: true ``` `sslmode: disable` es aceptable porque la conexión no sale del servidor. Protege el archivo, que contiene la contraseña, y reinicia Grafana para que lo cargue: ```bash sudo chown root:grafana /etc/grafana/provisioning/datasources/iot.yaml sudo chmod 640 /etc/grafana/provisioning/datasources/iot.yaml sudo systemctl restart grafana-server ``` En Grafana, ve a **Connections > Data sources > IoT PostgreSQL** y pulsa **Save & test**. Debe aparecer el mensaje `Database Connection OK`. ## Paso 6: Crear el dashboard de sensores ### Variable para elegir sensores Crea un dashboard nuevo (**Dashboards > New > New dashboard**) y, antes de añadir paneles, abre **Settings > Variables > Add variable**: - **Type**: Query. - **Name**: `sensor`. - **Data source**: IoT PostgreSQL. - **Query**: ```sql SELECT DISTINCT sensor_id FROM sensor_readings ORDER BY 1 ``` Activa **Multi-value** e **Include All option** y guarda. Aparecerá un desplegable en la parte superior del dashboard con `salon`, `cocina` y `exterior`. ### Panel de temperatura Añade una visualización de tipo **Time series**. En el editor de consultas cambia a modo **Code**, elige el formato **Time series** y pega: ```sql SELECT $__timeGroupAlias(time, $__interval), sensor_id AS metric, avg(temperature) AS value FROM sensor_readings WHERE $__timeFilter(time) AND sensor_id IN ($sensor) GROUP BY 1, 2 ORDER BY 1 ``` Las macros hacen el trabajo pesado: | Macro | Qué hace | |---|---| | `$__timeFilter(time)` | Filtra por el rango de tiempo seleccionado en el dashboard. | | `$__timeGroupAlias(time, $__interval)` | Agrupa en intervalos que se ajustan al zoom, para no enviar miles de puntos al navegador. | | `$sensor` | Se sustituye por los sensores elegidos en el desplegable, ya entre comillas. | La columna `metric` genera una serie por sensor. En las opciones del panel, fija **Unit** en `Celsius (°C)`. ### Panel de última lectura Añade un panel **Stat** para ver la temperatura actual de cada sensor, con formato **Table**: ```sql SELECT DISTINCT ON (sensor_id) sensor_id, temperature FROM sensor_readings WHERE sensor_id IN ($sensor) ORDER BY sensor_id, time DESC ``` `DISTINCT ON` devuelve la fila más reciente de cada sensor gracias al índice creado en el paso 1. En **Value options**, elige **Show**: `All values` para que se muestre un valor por fila (uno por sensor) y **Fields**: `temperature`. Añade umbrales (**Thresholds**) en 25 y 30 para que el color cambie de verde a naranja y rojo. Guarda el dashboard. Cambia el rango de tiempo a **Last 2 days** y verás la oscilación de los datos simulados en cada sensor. ## Paso 7: Configurar una alerta de temperatura Para enviar alertas por correo, añade la configuración SMTP en `/etc/grafana/grafana.ini`: ```bash sudo nano /etc/grafana/grafana.ini ``` ```ini [smtp] enabled = true host = smtp.example.com:587 user = alertas@example.com password = your_smtp_password from_address = alertas@example.com from_name = Grafana IoT ``` Reinicia Grafana con `sudo systemctl restart grafana-server`. Después, en **Alerting > Contact points**, edita el punto de contacto por defecto, añade tu dirección de correo y pulsa **Test** para comprobar que llega el mensaje. Crea la regla en **Alerting > Alert rules > New alert rule**. Como consulta, en modo **Code** y formato **Table**: ```sql SELECT sensor_id, avg(temperature) AS value FROM sensor_readings WHERE time > now() - interval '5 minutes' GROUP BY sensor_id ``` Cada fila genera una alerta independiente con la etiqueta `sensor_id`, así sabrás qué sensor ha superado el límite. Configura la condición como **Threshold** `IS ABOVE 30`, elige una carpeta y un grupo de evaluación cada 1 minuto con un periodo pendiente (**Pending period**) de 5 minutos para evitar avisos por picos puntuales. Para probarla, inserta una lectura alta: ```bash sudo -u postgres psql -d iot -c "INSERT INTO sensor_readings VALUES (now(), 'cocina', 45, 40);" ``` La regla pasa a estado **Pending** en el siguiente minuto y a **Firing** cuando se cumple el periodo pendiente, si la media de los últimos cinco minutos sigue por encima del umbral. Inserta varias lecturas si la media no llega a 30. ## Solución de problemas **`Database Connection OK` no aparece**: comprueba la contraseña del archivo de aprovisionamiento y que PostgreSQL escucha en `127.0.0.1:5432` con `sudo ss -tlnp | grep 5432`. El log de Grafana lo detalla con `sudo journalctl -u grafana-server -n 50 --no-pager`. **El panel muestra "No data"**: el rango de tiempo no cubre los datos o la variable `sensor` no tiene ningún valor seleccionado. Pulsa **Query inspector** en el panel para ver el SQL final con las macros sustituidas. **Redirecciones infinitas o recursos que no cargan**: `root_url` no coincide con la URL pública. Debe ser exactamente `https://your_domain/`. **Gráficas lentas con muchos datos**: no quites `$__timeGroupAlias`; sin agrupación Grafana descarga cada punto. Con millones de filas, valora TimescaleDB y sus agregados continuos. ## Conclusión Tienes Grafana funcionando en Ubuntu 24.04 detrás de Nginx con HTTPS, conectado a PostgreSQL con un usuario de solo lectura, un dashboard de sensores filtrable por variable y una alerta por correo cuando la temperatura supera el umbral. El esquema sirve tal cual para datos reales. Como siguientes pasos puedes: - Enviar lecturas reales desde tus dispositivos a un broker MQTT y guardarlas en `sensor_readings` con Node-RED o Telegraf. - Exportar el dashboard como JSON y aprovisionarlo desde `/etc/grafana/provisioning/dashboards` junto a la fuente de datos. - Instalar la extensión TimescaleDB y convertir la tabla en hypertable cuando el volumen de datos crezca.