Un stack TIG con Docker Compose te da un pipeline de datos MQTT completo en una sola máquina: Telegraf se suscribe a tu broker, InfluxDB guarda las lecturas y Grafana las representa. En esta guía crearás todos los archivos necesarios, arrancarás el stack con un solo comando y abrirás Grafana en un dashboard ya aprovisionado que muestra datos en vivo.
Qué vas a construir
El stack tiene tres contenedores y una dependencia externa, el broker MQTT:
devices / simulator --> MQTT broker (broker.connectmq.com)
|
Telegraf (mqtt_consumer)
|
InfluxDB 2 (bucket "sensors")
|
Grafana (provisioned data source + dashboard)
El broker queda fuera de Compose a propósito. Los brokers son infraestructura de larga duración que comparten muchos consumidores; el stack TIG es uno de esos consumidores. Telegraf se conecta al broker como un cliente MQTT corriente, lo que funciona igual con el servidor MQTT público gratuito que con uno privado.
Por qué Docker Compose para el stack TIG
Cada una de las tres herramientas puede instalarse de forma nativa, pero eso implica tres repositorios de paquetes, tres definiciones de servicio y archivos de configuración repartidos por el sistema. Compose mete todo el pipeline en una carpeta que puedes versionar, copiar a otra máquina o eliminar sin dejar rastro. Además fija las versiones de forma explícita, así que una actualización del sistema operativo nunca actualiza InfluxDB o Grafana a tus espaldas.
La contrapartida es que Compose es una herramienta de un solo host. Encaja bien en un portátil de desarrollo, un laboratorio, un gateway de un emplazamiento pequeño o un servidor on-premises que recoge datos de una planta. No te da alta disponibilidad; si el host cae, cae el stack. Para muchos casos de monitorización eso es aceptable, porque los dispositivos siguen publicando en el broker y Telegraf se reanuda cuando vuelve, y si cambias Telegraf a QoS 1 con persistent_session = true, el broker puede retener sus mensajes durante un corte breve.
Estructura del proyecto
Crea esta estructura de directorios:
tig-mqtt/
.env
docker-compose.yml
telegraf/
telegraf.conf
grafana/
provisioning/
datasources/
influxdb.yml
dashboards/
dashboards.yml
dashboards/
mqtt-sensors.json
Seis archivos en total. Cada uno se muestra completo a continuación.
Paso 1: variables de entorno
Los secretos y los nombres viven en .env, que Docker Compose lee automáticamente. Mantenlo fuera del control de versiones.
# .env
INFLUX_USERNAME=admin
INFLUX_PASSWORD=change-me-please
INFLUX_ORG=iot
INFLUX_BUCKET=sensors
INFLUX_TOKEN=replace-with-a-long-random-token
GRAFANA_PASSWORD=change-me-too
MQTT_SERVER=tcp://broker.connectmq.com:1883
MQTT_TOPIC_PREFIX=connectmq/demo/a1b2c3
MQTT_CLIENT_ID=telegraf-a1b2c3
MQTT_USERNAME=
MQTT_PASSWORD=
Genera un token real con openssl rand -hex 32. Sustituye a1b2c3 por tu propia cadena aleatoria para que tus topics queden separados en el broker público.
Paso 2: el archivo Docker Compose del stack TIG
# docker-compose.yml
services:
influxdb:
image: influxdb:2.7
restart: unless-stopped
ports:
- "8086:8086"
environment:
DOCKER_INFLUXDB_INIT_MODE: setup
DOCKER_INFLUXDB_INIT_USERNAME: ${INFLUX_USERNAME}
DOCKER_INFLUXDB_INIT_PASSWORD: ${INFLUX_PASSWORD}
DOCKER_INFLUXDB_INIT_ORG: ${INFLUX_ORG}
DOCKER_INFLUXDB_INIT_BUCKET: ${INFLUX_BUCKET}
DOCKER_INFLUXDB_INIT_RETENTION: 30d
DOCKER_INFLUXDB_INIT_ADMIN_TOKEN: ${INFLUX_TOKEN}
volumes:
- influxdb-data:/var/lib/influxdb2
- influxdb-config:/etc/influxdb2
healthcheck:
test: ["CMD", "influx", "ping"]
interval: 10s
timeout: 5s
retries: 12
telegraf:
image: telegraf:1.32
restart: unless-stopped
depends_on:
influxdb:
condition: service_healthy
environment:
INFLUX_TOKEN: ${INFLUX_TOKEN}
INFLUX_ORG: ${INFLUX_ORG}
INFLUX_BUCKET: ${INFLUX_BUCKET}
MQTT_SERVER: ${MQTT_SERVER}
MQTT_TOPIC_PREFIX: ${MQTT_TOPIC_PREFIX}
MQTT_CLIENT_ID: ${MQTT_CLIENT_ID}
MQTT_USERNAME: ${MQTT_USERNAME}
MQTT_PASSWORD: ${MQTT_PASSWORD}
volumes:
- ./telegraf/telegraf.conf:/etc/telegraf/telegraf.conf:ro
grafana:
image: grafana/grafana:11.3.0
restart: unless-stopped
depends_on:
- influxdb
ports:
- "3000:3000"
environment:
GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_PASSWORD}
INFLUX_TOKEN: ${INFLUX_TOKEN}
INFLUX_ORG: ${INFLUX_ORG}
INFLUX_BUCKET: ${INFLUX_BUCKET}
volumes:
- grafana-data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning:ro
- ./grafana/dashboards:/var/lib/grafana/dashboards:ro
volumes:
influxdb-data:
influxdb-config:
grafana-data:
El health check es importante. Sin él, Telegraf puede arrancar antes de que InfluxDB termine su configuración inicial, fallar en sus primeras escrituras y llenar los registros de errores. condition: service_healthy hace que Compose espere hasta que influx ping tenga éxito.
Paso 3: configuración de Telegraf
Telegraf sustituye las referencias ${VAR} desde su entorno, así que el archivo de configuración no contiene secretos.
# telegraf/telegraf.conf
[agent]
interval = "10s"
flush_interval = "10s"
omit_hostname = true
[[inputs.mqtt_consumer]]
servers = ["${MQTT_SERVER}"]
topics = ["${MQTT_TOPIC_PREFIX}/+/+/telemetry"]
qos = 0
client_id = "${MQTT_CLIENT_ID}"
username = "${MQTT_USERNAME}"
password = "${MQTT_PASSWORD}"
name_override = "sensor"
topic_tag = ""
data_format = "json"
tag_keys = ["device"]
json_time_key = "ts"
json_time_format = "unix_ms"
[[inputs.mqtt_consumer.topic_parsing]]
topic = "${MQTT_TOPIC_PREFIX}/+/+/telemetry"
tags = "_/_/_/line/_/_"
[[outputs.influxdb_v2]]
urls = ["http://influxdb:8086"]
token = "${INFLUX_TOKEN}"
organization = "${INFLUX_ORG}"
bucket = "${INFLUX_BUCKET}"
Fíjate en la URL http://influxdb:8086: dentro de la red de Compose, los contenedores se comunican por nombre de servicio, no por localhost. El patrón de tags supone un prefijo de tres niveles como connectmq/demo/a1b2c3; si tu prefijo tiene otra profundidad, ajusta el número de guiones bajos iniciales. Para una explicación línea a línea de estas opciones, consulta la guía del Telegraf MQTT consumer.
Paso 4: aprovisionar el origen de datos de Grafana
El aprovisionamiento crea el origen de datos de InfluxDB al arrancar, así que nadie tiene que ir haciendo clic por la interfaz. Grafana también expande las variables de entorno en los archivos de aprovisionamiento.
# grafana/provisioning/datasources/influxdb.yml
apiVersion: 1
datasources:
- name: InfluxDB
uid: influxdb
type: influxdb
access: proxy
url: http://influxdb:8086
isDefault: true
jsonData:
version: Flux
organization: ${INFLUX_ORG}
defaultBucket: ${INFLUX_BUCKET}
secureJsonData:
token: ${INFLUX_TOKEN}
El uid fijo permite que el dashboard haga referencia a este origen de datos de forma fiable.
Paso 5: aprovisionar un dashboard listo para usar
Primero indica a Grafana dónde están los dashboards:
# grafana/provisioning/dashboards/dashboards.yml
apiVersion: 1
providers:
- name: mqtt
folder: MQTT
type: file
options:
path: /var/lib/grafana/dashboards
Después añade el propio dashboard. Tiene una serie temporal de temperatura por dispositivo y un panel stat con el número de dispositivos que están informando.
{
"uid": "mqtt-sensors",
"title": "MQTT Sensors",
"schemaVersion": 39,
"refresh": "10s",
"time": { "from": "now-1h", "to": "now" },
"panels": [
{
"id": 1,
"type": "timeseries",
"title": "Temperature by device",
"gridPos": { "x": 0, "y": 0, "w": 18, "h": 10 },
"datasource": { "type": "influxdb", "uid": "influxdb" },
"fieldConfig": { "defaults": { "unit": "celsius" }, "overrides": [] },
"targets": [
{
"refId": "A",
"query": "from(bucket: \"sensors\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r._measurement == \"sensor\" and r._field == \"temperature\")\n |> group(columns: [\"device\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)"
}
]
},
{
"id": 2,
"type": "stat",
"title": "Reporting devices (last 5 min)",
"gridPos": { "x": 18, "y": 0, "w": 6, "h": 10 },
"datasource": { "type": "influxdb", "uid": "influxdb" },
"targets": [
{
"refId": "A",
"query": "from(bucket: \"sensors\")\n |> range(start: -5m)\n |> filter(fn: (r) => r._measurement == \"sensor\" and r._field == \"temperature\")\n |> group()\n |> distinct(column: \"device\")\n |> count()"
}
]
}
]
}
Si cambiaste el nombre del bucket en .env, cambia también "sensors" en las dos consultas. También puedes importar este JSON mediante Dashboards, luego New y luego Import en cualquier instancia de Grafana existente.
Paso 6: arrancar el stack y enviar datos
cd tig-mqtt
docker compose up -d
docker compose ps
docker compose logs -f telegraf
Ahora publica datos. La fuente continua más rápida es nuestro simulador de dispositivos MQTT en Python; ejecútalo con --prefix connectmq/demo/a1b2c3 para que coincida con MQTT_TOPIC_PREFIX. Un único mensaje de prueba también sirve:
mosquitto_pub -h broker.connectmq.com -p 1883 -i pub-a1b2c3 \
-t connectmq/demo/a1b2c3/line1/sensor-0001/telemetry \
-m "{\"device\":\"sensor-0001\",\"ts\":$(date +%s%3N),\"temperature\":22.4,\"humidity\":41.0}"
Abre http://localhost:3000, inicia sesión como admin con GRAFANA_PASSWORD y abre el dashboard MQTT Sensors en la carpeta MQTT.
Verifica cada capa en orden
Cuando el dashboard se queda vacío, resiste la tentación de cambiar varias cosas a la vez. Recorre el pipeline y confirma cada capa antes de pasar a la siguiente:
- Broker. Ejecuta
mosquitto_sub -h broker.connectmq.com -p 1883 -i check-a1b2c3 -t "connectmq/demo/a1b2c3/#" -v. Si no aparece nada, el problema está en el publicador, no en el stack. - Conexión de Telegraf.
docker compose logs telegrafdebería mostrar el plugin conectándose sin errores. Los fallos de autenticación y los errores de topic parsing aparecen aquí. - Interpretación en Telegraf. Añade temporalmente un bloque
[[outputs.file]]confiles = ["stdout"]y reinicia Telegraf. Las métricas interpretadas aparecen entonces en los registros como line protocol, lo que demuestra que el JSON y el mapeo de topics son correctos. - InfluxDB. Ejecuta
docker compose exec influxdb influx query 'from(bucket: "sensors") |> range(start: -5m) |> limit(n: 3)'. La CLI dentro del contenedor ya está configurada por la inicialización. - Grafana. Abre la página del origen de datos y haz clic en Save and test, y comprueba después que el rango de tiempo del dashboard cubre el periodo en que se escribieron los datos.
En la práctica, la mayoría de los problemas aparecen en el paso 1 o en el 3: un prefijo de topic que difiere en un carácter o un payload que no es el JSON que espera la configuración.
Errores de la primera ejecución
| Problema | Causa | Solución |
|---|---|---|
Se rechaza el token de .env |
Las variables de inicialización de InfluxDB solo se aplican a un volumen vacío | docker compose down -v para reiniciar, o crea un nuevo token en la interfaz |
| Telegraf: connection refused a localhost:8086 | Nombre de host incorrecto dentro de los contenedores | Usa http://influxdb:8086 |
| El dashboard muestra "No data" | El prefijo de topic de .env difiere de lo que se publica |
Compara con mosquitto_sub -t "connectmq/demo/a1b2c3/#" -v |
| Telegraf: error de topic parsing | Cambió la profundidad del prefijo pero no el patrón de tags |
Ajusta el número de segmentos _ a los niveles del topic |
| Error en el origen de datos de Grafana | Token u org no coinciden | Revisa la página del origen de datos; los valores vienen de .env |
El stack funciona, pero los datos desaparecen tras down -v |
Se eliminaron los volúmenes | Usa docker compose down sin -v |
Pasar a un broker privado
Para equipos reales, solo cambia .env. Apunta el stack a un broker MQTT gestionado como ConnectMQ:
MQTT_SERVER=ssl://broker.connectmq.com:8883
MQTT_TOPIC_PREFIX=connectmq/factory1/production
MQTT_CLIENT_ID=YOUR_CLIENT_ID
MQTT_USERNAME=YOUR_USERNAME
MQTT_PASSWORD=YOUR_PASSWORD
El certificado lo emite Let's Encrypt, así que Telegraf confía en él sin ajustes TLS adicionales. Vuelve a ejecutar docker compose up -d para recrear el contenedor de Telegraf con los nuevos valores. Coloca además Grafana detrás de un proxy inverso con HTTPS antes de exponerlo fuera de tu máquina, y haz copia de seguridad del volumen influxdb-data.
Cuando el dashboard esté listo, añade alertas de Grafana para datos de sensores IoT sobre las mismas consultas. Para opciones de cliente más amplias en el lado del dispositivo, consulta la guía de bibliotecas cliente MQTT, y la documentación de Docker Compose cubre en detalle los health checks y los archivos de entorno.
Empieza con ConnectMQ
Usa el servidor público para levantar hoy el stack con datos simulados. Para producción, ConnectMQ ofrece un broker MQTT gestionado con TLS, MQTT 5 y un portal donde puedes observar los topics en vivo antes de que Telegraf llegue a verlos. Crea un broker Serverless gratuito en app.connectmq.com sin tarjeta de crédito y pega las credenciales en .env. Si estás sopesando opciones de alojamiento, lee nuestra guía del broker MQTT en la nube.
Preguntas frecuentes
¿Puedo ejecutar el broker MQTT dentro del mismo archivo Docker Compose?
Puedes, pero un broker MQTT gestionado y compartido suele ser más sencillo de operar y permite que otros consumidores usen los mismos datos. Mantenerlo fuera de Compose también significa que reiniciar el stack nunca interrumpe a tus dispositivos.
¿Funciona esta configuración del stack TIG con Docker Compose con InfluxDB 3?
No sin cambios. InfluxDB 3 usa otra imagen, otro procedimiento de configuración y otros lenguajes de consulta, así que las variables de inicialización y las consultas Flux de aquí se aplican a InfluxDB 2.7.
¿Cómo añado más dashboards?
Coloca archivos JSON de dashboard adicionales en grafana/dashboards. Grafana los detecta automáticamente a través del proveedor de archivos.
¿Cuánto espacio en disco ocuparán los datos?
Depende del número de series y de la tasa de mensajes. La retención de 30 días del bucket mantiene acotado el crecimiento; redúcela o haz downsampling de los datos antiguos si vas justo de disco.



