
Build a Grafana Weather Dashboard: Visualise Your Station Data
Step-by-step guide to connecting your weather station to Grafana for real-time dashboards, historical overlays, and alerting β from data ingestion to polished panels.
Most weather-station dashboards feel like spreadsheets that someone accidentally published. Rows of numbers, maybe a wind-rose GIF from 2004, and a refresh interval measured in geological epochs. If that description hits a little close to home, this guide is for you.
Over the past decade I have wired up Grafana dashboards for half a dozen personal stations, and the workflow I outline here β GraphWeather CSV export β Telegraf β InfluxDB β Grafana β is the one that has survived every hardware swap and OS migration I have thrown at it. By the time you reach the bottom of this page you will have a live, auto-refreshing dashboard with temperature, humidity, pressure, wind, and rain panels, plus alerting rules that fire when a sensor goes silent.
We will cover the full pipeline: choosing a time-series database, configuring data ingestion, building your first panels, polishing the layout, setting up alerts, and avoiding the mistakes that cost me entire weekends. If you are new to GraphWeather or weather-station publishing in general, start there for the bigger picture. For the FTP side of getting pages onto a web server, the WeatherLink and templates guide fills in the blanks.
Quick-Answer Summary
| Component | Role | Why this choice |
|---|---|---|
| GraphWeather | Station software β reads sensors, logs data, exports CSV | Reliable scheduler, configurable export paths |
| Telegraf | Collection agent β tails CSV, parses fields, ships to database | Lightweight, plugin-rich, handles format quirks |
| InfluxDB 2.x | Time-series store β retains years of minute-resolution data | Purpose-built for metrics; excellent retention policies |
| Grafana | Visualisation β dashboards, overlays, alerting | Industry standard, free tier covers everything here |
If you already run Prometheus for server monitoring you can substitute it for InfluxDB β the Grafana side barely changes. But for pure weather data, InfluxDB's native timestamp handling and downsampling tasks make life noticeably easier.
Prerequisites
Before you start, confirm you have:
- A working GraphWeather installation that is already logging data to CSV (even a single day of logs is enough to test with).
- A Linux or Windows machine that can run Docker β or bare-metal installs of Telegraf, InfluxDB, and Grafana if you prefer.
- Basic comfort with a terminal. We will use Docker Compose for the stack, so one
docker compose up -dgets you running. - Roughly 1 GB of free disk space for the database and dashboard stack. Weather data is tiny compared to application metrics; a full year of one-minute readings occupies under 200 MB in InfluxDB.
Step 1 β Understand the GraphWeather CSV Export
GraphWeather writes CSV files on a configurable schedule β every 60 seconds by default. A typical line looks like this:
2026-03-17T14:32:00Z,8.3,72,1018.4,12.6,NNW,0.2,0.0
The columns map to: timestamp, temperature (Β°C), humidity (%), pressure (hPa), wind speed (km/h), wind direction, rain rate (mm/h), rain daily (mm). Confirm your column order in GraphWeather's export settings β some station drivers add UV index or solar radiation columns.
Two things matter here: the timestamp format and the file rotation policy. GraphWeather can write ISO 8601, Unix epoch, or a locale-dependent format. Pick ISO 8601 (YYYY-MM-DDTHH:MM:SSZ) and stick with it. Telegraf's csv parser handles ISO 8601 out of the box; locale formats require custom patterns that break the moment someone changes a regional setting.
For file rotation, configure GraphWeather to keep a single rolling file (e.g., current.csv) rather than daily files. Telegraf's tail input tracks file position with an offset, so appending to one file is simpler than chasing a new filename every midnight.
Step 2 β Spin Up the Stack with Docker Compose
Create a docker-compose.yml:
version: "3.9"
services:
influxdb:
image: influxdb:2.7
ports:
- "8086:8086"
volumes:
- influxdb-data:/var/lib/influxdb2
environment:
- DOCKER_INFLUXDB_INIT_MODE=setup
- DOCKER_INFLUXDB_INIT_USERNAME=admin
- DOCKER_INFLUXDB_INIT_PASSWORD=changeme-please
- DOCKER_INFLUXDB_INIT_ORG=weather
- DOCKER_INFLUXDB_INIT_BUCKET=station
- DOCKER_INFLUXDB_INIT_RETENTION=365d
telegraf:
image: telegraf:1.30
volumes:
- ./telegraf.conf:/etc/telegraf/telegraf.conf:ro
- /path/to/graphweather/export:/data:ro
depends_on:
- influxdb
grafana:
image: grafana/grafana-oss:10.4.0
ports:
- "3000:3000"
volumes:
- grafana-data:/var/lib/grafana
depends_on:
- influxdb
volumes:
influxdb-data:
grafana-data:
Replace /path/to/graphweather/export with the actual directory where GraphWeather writes current.csv. On Windows, if GraphWeather and Docker Desktop share the same machine, use a bind mount like C:/GraphWeather/export:/data:ro.
Run docker compose up -d. After thirty seconds all three containers should report healthy.
Step 3 β Configure Telegraf
Create telegraf.conf in the same directory as your Compose file:
[agent]
interval = "60s"
flush_interval = "60s"
[[inputs.tail]]
files = ["/data/current.csv"]
from_beginning = false
data_format = "csv"
csv_header_row_count = 0
csv_column_names = [
"timestamp", "temperature", "humidity",
"pressure", "wind_speed", "wind_dir",
"rain_rate", "rain_daily"
]
csv_timestamp_column = "timestamp"
csv_timestamp_format = "2006-01-02T15:04:05Z"
csv_tag_columns = ["wind_dir"]
[[outputs.influxdb_v2]]
urls = ["http://influxdb:8086"]
token = "$INFLUX_TOKEN"
organization = "weather"
bucket = "station"
A few notes:
csv_timestamp_formatuses Go's reference-time layout.2006-01-02T15:04:05Zis Go's way of saying ISO 8601 with UTC. If your station writes local time, append the offset (e.g.,2006-01-02T15:04:05-07:00) β but I strongly recommend logging in UTC and converting at the Grafana layer.csv_tag_columnspromoteswind_dirfrom a field to a tag so you can group or filter by compass direction later.$INFLUX_TOKENβ grab this from the InfluxDB UI athttp://localhost:8086after the first-run setup wizard completes.
Restart Telegraf after saving the config: docker compose restart telegraf.
Step 4 β Verify Data in InfluxDB
Open http://localhost:8086, navigate to Data Explorer, select the station bucket, and run:
from(bucket: "station")
|> range(start: -1h)
|> filter(fn: (r) => r._measurement == "tail")
You should see rows appearing every 60 seconds. If the table is empty, check Telegraf's logs (docker compose logs telegraf) β the most common culprit is a timestamp-format mismatch or an incorrect file path.
Step 5 β Build Your Grafana Panels
Log into Grafana at http://localhost:3000 (default credentials: admin / admin). Add InfluxDB as a datasource β choose Flux as the query language, point it at http://influxdb:8086, and paste the same token and org.
Temperature Panel
Create a new dashboard, add a Time series panel, and paste:
from(bucket: "station")
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|> filter(fn: (r) => r._field == "temperature")
|> aggregateWindow(every: v.windowPeriod, fn: mean)
Set the unit to Celsius in the panel options. Enable gradient fill with a colour range from blue (0 Β°C) to red (35 Β°C) β it makes cold snaps and heat waves jump off the screen.
Humidity & Pressure
Duplicate the temperature panel twice. Swap the field name to humidity (unit: Percent (0β100)) and pressure (unit: Hectopascal). For pressure, narrow the Y-axis to 980β1040 hPa; the default auto-range compresses the trace into a flat line.
Wind
A Bar gauge works well for current wind speed, while a Windrose community plugin shows directional distribution over the selected time window. Install the plugin from Grafana's plugin catalogue β search for "Windrose".
Rain
Use a Stat panel for daily rain total (latest value of rain_daily) and a Time series for rain rate to see individual bursts.
Step 6 β Dashboard JSON Tips
Once your layout looks right, export the JSON model (Dashboard settings β JSON Model). Store it in version control alongside your Telegraf config. This gives you a one-command recovery path if you ever rebuild the stack.
Inside the JSON, look for datasource objects β they reference a UID that changes per Grafana instance. Replace them with the variable syntax "${DS_INFLUXDB}" and declare a dashboard-level datasource variable so the JSON is portable.
Step 7 β Alerting Rules for Sensor Drop-Outs
Sensor failures rarely announce themselves. A thermistor drifts, a rain gauge clogs, or the USB cable works loose β and you discover it three weeks later when someone asks why your website shows 0.0 mm for the entire month.
In Grafana, create an alert rule on the temperature panel:
- Condition:
WHEN last() OF query(A, 10m, now) IS BELOW -60β no terrestrial station should read below β60 Β°C, so this catches null/zero misreadings. - Add a second condition:
WHEN count() OF query(A, 15m, now) IS BELOW 1β fires if no data arrives for 15 minutes. - Route the alert to an email or Slack contact point.
Repeat similar rules for humidity (flag readings stuck at exactly 0 or 100 for over an hour β a classic sign of a dead capacitive sensor) and pressure (flag values outside 870β1085 hPa).
Common Mistakes
- Wrong timestamp format. This is the number-one cause of empty dashboards. If Telegraf silently drops every line, check
csv_timestamp_formatagainst an actual line from your CSV. - Timezone confusion. Log in UTC. Convert to local time in Grafana's dashboard settings. Mixing timezones in the pipeline creates phantom gaps at DST transitions.
- Missing retention policy. InfluxDB will keep data forever unless you set a retention policy. For minute-resolution data, 365 days is generous; use a downsampling task to roll older data into hourly averages if you want multi-year trends without ballooning storage.
- Grafana auto-refresh too aggressive. A 5-second refresh on a 60-second data interval wastes queries and can cause the panel to flicker. Match refresh to your station's reporting interval.
- Running Telegraf as a user without read access to the CSV directory. On Linux, add the
telegrafuser to the group that owns the export folder.
Related Reading
- GraphWeather overview β feature list, download, and getting-started path.
- Publishing your station pages β once the dashboard is built, learn how to push static pages to your web host.
- WeatherLink and templates guide β template variable reference that also applies when you export data for Grafana consumption.
FAQ
Can I use Prometheus instead of InfluxDB?
Yes. Write a small exporter (or use Telegraf's prometheus_client output) to expose metrics on an HTTP endpoint. The Grafana side is nearly identical β you swap the datasource and use PromQL instead of Flux. InfluxDB is friendlier for pure time-series weather data because it handles irregular timestamps and retention natively.
Do I need a dedicated server? No. The entire stack runs comfortably on a Raspberry Pi 4 with 4 GB RAM for a single station. If you add heavy alerting or long retention, consider 8 GB.
How do I overlay data from two stations?
Add a station tag in Telegraf (use the [global_tags] section) and filter by tag in each Grafana panel. You can overlay traces from multiple stations in one Time series panel by grouping on the tag.
What if my station uses a proprietary binary format instead of CSV? GraphWeather can export to CSV regardless of the internal format. Configure the export scheduler and point Telegraf at the resulting file. If you are publishing pages via FTP as well, the FTP publishing guide walks through parallel export paths.
Can I embed Grafana panels on my weather website?
Absolutely. Grafana supports anonymous access and iframe embedding. Enable it in grafana.ini under [auth.anonymous] and [security] β set allow_embedding = true. Then use the panel's Share β Embed link on your published pages.