# garmin-grafana: self-hosted Garmin health data in InfluxDB, charted with Grafana

> A Dockerized Python fetcher that logs into Garmin Connect, writes health metrics into InfluxDB, and ships Grafana dashboards for long-term trends. It is for people who want their own copy of the data, and it assumes you can run Docker and read a compose file.

**arpanghosh8453/garmin-grafana** — A Dockerized python Script to fetch Garmin health data and populate that in a InfluxDB Database, for visualization long term health trends with Grafana

- Repository: https://github.com/arpanghosh8453/garmin-grafana
- Stars: 3,478 · Forks: 229
- Language: Python
- License: BSD-3-Clause
- Published: 2026-09-23 · Updated: 2026-09-23 · Language: en
- Canonical page: https://hysenlabs.com/projects/arpanghosh8453-garmin-grafana

## The problem: Garmin holds your health history, and you only get to look at it through Garmin's app

Garmin Connect stores years of heart rate, sleep stages, SpO2, breathing rate, HRV, stress, Body Battery, steps, calories, activity minutes, HR zone time and GPS tracks. The app shows those numbers one screen at a time. You cannot put resting heart rate and sleep score on the same panel, you cannot zoom out to a two-year window, and you cannot query the raw samples yourself. Export exists, but it is a manual round trip, not a live feed.

garmin-grafana targets that gap. It is a container that logs into Garmin Connect on your behalf, pulls the metrics the README lists, and writes them into a local InfluxDB database. Grafana then reads InfluxDB and renders the dashboards shipped in the Grafana_Dashboard directory. The README frames the pitch as local ownership and visualization freedom: the data stays on hardware you control, and the panels are yours to edit.

The audience is narrow and specific. This is for someone who already runs Docker, is comfortable with a compose file and environment variables, and treats a Grafana instance as normal infrastructure. It is also for households: the README documents a multi-user instance setup for a second account, which is the kind of thing a hosted dashboard does not offer. If you have never run InfluxDB, the setup cost is real, and the README points less technical users at the helper script for exactly that reason.

## How the fetch, store and dashboard pipeline is put together

The runtime is small. The Dockerfile builds with uv against Python 3.13, copies the virtual environment and the src directory into a python:3.13-slim-bookworm image, creates an unprivileged appuser with uid 1000, and runs python garmin_grafana/garmin_fetch.py as the container command. There is no web server in the container and no exposed port. It is a scheduled job that talks outward to Garmin and to InfluxDB.

The dependency list in pyproject.toml tells you the shape of the data flow. garminconnect handles the Garmin Connect session and OAuth tokens. influxdb 5.3.2 and influxdb3-python 0.12.0 are both present, so the project supports the 1.x client and the newer 3.x client. pandas does the reshaping between what Garmin returns and what InfluxDB accepts as points. fitparse appears for reading FIT files, which is what the Garmin Connect export import path needs.

The measurements are named in the README's dashboard section rather than in a schema file: StrengthExerciseSet and StrengthHRZones back the separate strength training dashboard, which covers estimated 1RM progression, training volume, personal records, exercise balance, a set-level log and HR zone time. The main dashboard covers the daily and intraday metrics. Grafana_Datasource holds the datasource provisioning, so the Grafana side is configured from files rather than clicked together by hand.

The scheduling model is the part worth being honest about. The README describes automated fetching at a regular interval and calls it set and forget. That interval is a container concern, not an application concern: the container runs the fetch and exits, and whatever restarts it (a compose restart policy, a cron entry, a Kubernetes CronJob via the Helm chart in k8s/) decides the cadence. The README does not document a rollback path, and it does not document what happens to partially written points if InfluxDB is unreachable mid-fetch.

## Installing garmin-grafana with the helper script or docker compose

The README offers two paths. The automated path runs easy-install.sh on Linux or macOS, and the README states plainly that it is for initial setup only: once the garminconnect OAuth tokens are saved after the first successful fetch, you should not run it again. Upgrades go through the update to newer versions section instead. The script requires a Linux environment; on Windows the README directs you to Docker Desktop plus WSL and Ubuntu from the Microsoft Store, then to run the bash command inside WSL.

If Docker is not installed, the README links the official install docs and also mentions the convenience one-liner from the docker-install repository:

```bash
curl -fsSL https://get.docker.com -o get-docker.sh && sh get-docker.sh
```

That installs the Docker engine on Linux. On macOS the README points at Docker Desktop instead. Either way, verify with docker version before continuing, because the compose path assumes a working daemon.

The manual path is the one to read even if you use the script, since it is where the environment variables are defined. The repository ships compose-example.yml at the top level as the starting point. Copy it, fill in your Garmin credentials and InfluxDB connection details, and keep the token volume persistent. The exact service name and variable names live in compose-example.yml, the docs directory and the README's manual install section; read those files rather than guessing at keys.

The token directory matters more than it looks. The garminconnect OAuth tokens are what let the container skip a fresh login on every run, and the README ties the do-not-rerun-the-installer warning to the moment those tokens are first saved. If that path is not mounted persistently, you are back to a login each cycle.

For a first real use, bring the stack up and watch the first fetch complete, then open Grafana and confirm the datasource provisioned from Grafana_Datasource is reachable. Import the dashboard from Grafana_Dashboard. The README also documents exporting data to CSV files from Grafana, which is the fastest way to check that rows actually landed rather than trusting a green panel.

Kubernetes users have a separate route: the k8s/ directory contains a Helm chart and a Makefile for deployment, and the README mentions trying it with minikube. Synology users get a linked discussion thread instead of a section in the README, which tells you the NAS path is community-maintained rather than first-class.

## Where garmin-grafana breaks down

The hardest constraint is the one the project cannot fix: it depends on Garmin's own service. There is no official public API contract here. The garminconnect library talks to Garmin Connect the way the app does, and the README's own troubleshooting section exists because that surface moves. When Garmin changes authentication or endpoint behaviour, the fix arrives as a new garminconnect version and a new image. That is a dependency you inherit, not one you control.

The second limitation is operational. The container is a batch job with no health endpoint and no port, so your monitoring options are container exit codes and log inspection. If a fetch fails silently at 3 a.m., your dashboards simply stop gaining points. Nothing in the README describes an alerting hook for failed fetches, so detecting a stalled pipeline is on you.

The third is data coverage. The README says the project fetches almost everything from your Garmin watch, and then contrasts that with platforms limited to activity analytics. Almost is doing real work in that sentence. Metrics that Garmin does not expose through the Connect endpoints it uses will not appear, and the README does not publish a field-by-field coverage table. If one specific metric is the reason you are here, confirm it appears in the shipped dashboard before you build around it.

Finally, this is the wrong tool if you want zero infrastructure. Two databases and a dashboard is three moving parts. Someone who wants to glance at sleep score on a phone should stay in the Garmin app.

## Alternatives, and what actually differs

The nearest alternative in the same family is the author's own fitbit-grafana, which the README links as a sister project. The architecture is the same: fetch from the vendor's cloud, write to InfluxDB, render in Grafana. The difference is the source platform, and therefore the metric set and the authentication flow. If you wear a Fitbit rather than a Garmin, that project is the direct substitute, not a competitor.

The more instructive comparison is against Garmin Connect itself plus its export. Connect needs no server, no InfluxDB and no Grafana, and it will keep working regardless of what this project's dependencies do. What it will not do is let you combine arbitrary metrics on one panel, hold raw non-averaged samples over weeks, or run a second household account against the same dashboard. Those three capabilities are the entire reason to take on the infrastructure.

A third option is the general-purpose route: pull data through whatever library you prefer and write your own schema. That gives you total control over measurements and retention, at the cost of writing and maintaining the fetch, the backfill and the dashboard JSON yourself. garmin-grafana's value is that all three already exist, including the historical bulk update path and the Garmin Connect export importer, which are the parts most people underestimate when they start from scratch.

## Maintenance, licensing and what an upgrade actually costs

The project is BSD-3-Clause. That is a permissive licence: you can modify and redistribute it, including in modified form, provided the copyright notice and licence text are retained. The README asks for credit and support but that is a request, not a licence term. Nothing here is legal advice; if you plan to redistribute a modified image, read the LICENSE file at the repository root yourself.

On maintenance, the repository is not archived, and the last push was on 2026-08-12. Release history shows v0.3.0 in May 2025, v0.4.0 in March 2026 and v0.5.0 in April 2026, so the cadence is irregular rather than steady. Judge it on that, not on a description of activity.

The upgrade cost is mostly the token directory and the image tag. The README's update to newer versions section is the documented path, and the warning against re-running easy-install.sh after tokens exist is the trap to avoid. If you pin a digest instead of a floating tag, an upgrade is a deliberate edit plus a container recreate, and the persistent token volume is what keeps you from re-authenticating. The other ongoing cost is InfluxDB retention: this is a long-term trend tool, so the storage grows with every fetch interval you choose. The README documents backing up the InfluxDB database, which is the operation to rehearse before you accumulate data you care about.

## Conclusion

Adopt garmin-grafana if you already run Docker and Grafana, want the raw Garmin metrics in your own InfluxDB, and are willing to keep a container running on a schedule. Do not adopt it if you want a hosted service, if you need a documented rollback path (the README covers updating to newer versions but does not document rollback), or if you cannot keep the OAuth token directory persistent. Before committing, verify three things: that the garminconnect OAuth tokens survive a container restart, that your InfluxDB bucket and Grafana datasource match the shipped dashboard's expectations, and that the historical backfill window you need is actually reachable through the bulk update path.

## FAQ

### What exactly is Grafana?

In this project Grafana is the visualization layer: garmin-grafana writes Garmin health data into InfluxDB, and Grafana reads that database to render the dashboards shipped in the Grafana_Dashboard directory. The README notes that Grafana is a registered trademark of Grafana Labs and that the project is not affiliated with or endorsed by Grafana Labs.

### Does Garmin have a free API?

The README does not describe an official public Garmin API. It states that the container fetches data from Garmin servers using the garminconnect library and that you authenticate with your own Garmin Connect account, with OAuth tokens saved after the first successful data fetch.

### Is Grafana still free?

The README does not discuss Grafana's licensing or pricing. It only notes that Grafana is a registered trademark of Grafana Labs and that this project is independent and not affiliated with Grafana Labs.

## Sources

- [arpanghosh8453/garmin-grafana on GitHub](https://github.com/arpanghosh8453/garmin-grafana)
- [Issues](https://github.com/arpanghosh8453/garmin-grafana/issues)
- [License: BSD-3-Clause](https://github.com/arpanghosh8453/garmin-grafana/blob/main/LICENSE)
- [README](https://github.com/arpanghosh8453/garmin-grafana/blob/main/README.md)
- [Releases](https://github.com/arpanghosh8453/garmin-grafana/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/arpanghosh8453-garmin-grafana
