A high-performance activity analytics dashboard for Garmin FIT files. Available as a Tauri v2 desktop app or a Docker-deployable web app. Built with Rust, DuckDB, and React.
Important
Garmin is a registered trademark of Garmin Ltd. or its subsidiaries. This project is an independent, open-source tool and is not affiliated with, endorsed by, sponsored by, or approved by Garmin Ltd.
Note
This project repository is dynamically mirrored in Codeberg as a backup. An alternative docker image is available at codeberg.org/arpanghosh8453/fit-dashboard.
- Features
- Getting Started
- Getting FIT Files from Garmin
- Usage
- Export Formats
- Tech Stack
- Project Structure
- API Reference
- Configuration
- Security
- Acknowledgements
- Love This Project?
- Declaration
- License
- FIT File Parsing: Native Rust parser using the
fitparsercrate. Supports all standard Garmin FIT activity files with automatic field extraction. - High-Performance Storage: DuckDB-powered analytical database with automatic downsampling for fast time-series queries. Handles hundreds of activities with millions of data points.
- Interactive Telemetry Charts: ECharts-powered visualization of speed, heart rate, cadence, altitude, power, and temperature. Synchronized zoom/pan with Ctrl+scroll guarding to prevent accidental navigation.
- Activity Map: MapLibre GL map with dynamic path coloring by metric (speed, HR, cadence, altitude, power, temperature, time). Hover tooltips show telemetry details at each point. Supports multiple map styles.
- Overview Dashboard: Aggregate statistics across all imported activities — total distance, total duration, activity count, and recent activity feed with interactive map overlay.
- Advanced Filtering: Filter by sport type, date range, duration range, and full-text search. Collapsible filter panel with bordered container design.
- Bulk Export: Export filtered activities as CSV, JSON, GPX, or KML. Uses the File System Access API to write directly to a chosen folder. Fallback to individual browser downloads when the API is unavailable.
- Single Export: Right-click any activity to export it individually via the context menu with format submenu.
- Bulk Delete: Delete all filtered activities at once with inline confirmation and progress overlay.
- Inline Management: Rename and delete activities with inline UI — no browser dialogs. All destructive actions require explicit confirmation.
- Session Persistence: Authentication tokens persist across browser refreshes with a configurable 72-hour TTL (web) or until logout (desktop).
- Dark & Light Themes: Modern glassmorphism design with CSS custom properties. Theme toggles instantly across the entire interface.
- Responsive Layout: Collapsible sidebar with a persistent expand strip. Works on desktop and tablet viewports.
- Import Queue: Batch import multiple FIT files with sequential processing for stability. Duplicate detection prevents re-importing the same file.
- Password Protection: Argon2-hashed credentials with session-based authentication. First-use onboarding flow for initial setup.
- Cross-Platform: Desktop builds for Windows, macOS, and Linux.
- Multi-language Support: Deutsch, English, Español, Français, Magyar, Italiano, Nederlands, Polski, Português, Turkce, 中文, 日本語, 한국어
Download desktop binaries from the Releases page and install for your platform:
- Linux:
.AppImage/.deb(depending on release artifacts) - macOS:
.dmg(requires an additional De-Quarantine step, read below) - Windows:
.msiandexeinstallers
After installation, launch FIT Dashboard and complete onboarding on first run.
If you downloaded the macOS build and see "FIT Dashboard is damaged and can't be opened", macOS Gatekeeper is blocking an unsigned app (this is a security warning, not a corrupted file). Use one of the methods below to open the app:
- Locate the app in your Applications folder (or wherever you placed it).
- Right-click (or Control+click) on
FIT Dashboard.app. - Select Open from the context menu and confirm Open in the dialog.
Open Terminal and run the following. Type xattr -cr , then drag the .app bundle onto the Terminal window (it will paste the full path), then press Enter:
xattr -cr <drag-and-drop-FIT-Dashboard.app-here>After running this, try opening the app again.
Note: Apple charges for developer signing; unsigned releases may require these steps. This warning means Gatekeeper prevented launch, not that the file is corrupted.
# Clone the repository
git clone https://github.com/your-username/fit-dashboard
cd fit-dashboard
# Install frontend dependencies
npm install
# Start the Rust backend (web server mode)
cd src-tauri
cargo run --features web
# Backend starts at http://localhost:8080
# In another terminal, start the frontend dev server
cd fit-dashboard
npm run dev
# Frontend starts at http://localhost:5173
# Tauri desktop dev mode
npm run tauri:dev
# Tauri production build
npm run tauri:buildOpen http://localhost:5173 in your browser. On first launch, the onboarding screen appears to set up your username and password.
Use the prebuilt GHCR image:
cd docker
docker compose up -dBuild locally from source instead:
cd docker
docker compose -f docker-compose-build.yml up --build -dOpen http://localhost:8088 in your browser. The Nginx reverse proxy serves the frontend and proxies API requests to the Rust backend.
All data (DuckDB database, config) is stored in a Docker named volume mapped to /data/fit-dashboard inside the container. Data persists across container restarts and image updates.
The desktop app runs the Rust backend natively with Tauri IPC — no web server needed.
If your activities are in Garmin Connect, you can export FIT files in two ways.
Use Garmin's data export portal:
Steps:
- Sign in with your Garmin account.
- Go to Data Management and choose Export Your Data.
- Request the export and wait for Garmin to prepare the archive.
- Download the ZIP archive when it's ready from email
- Extract the ZIP on your computer.
- Find activity files (FIT/TCX/GPX) in the extracted folders - The exported zip contains a folder (DI_CONNECT/DI-Connect-Fitness-Uploaded-Files) containing your raw FIT activity files.
- In FIT Dashboard, use Import to select or drag-and-drop those files.
When to use this option:
- You want a full history export.
- You are migrating from Garmin Connect to local storage.
Use the activity page in Garmin Connect:
Steps:
- Open the Activities page.
- Click an activity to open details.
- Open the gear menu (top-right on the activity page).
- Click Export Original (or Export TCX/GPX depending on availability).
- Repeat for each activity you want.
- Import downloaded files into FIT Dashboard.
When to use this option:
- You only need a few activities.
- You want to re-export specific workouts.
Tips:
- FIT is the preferred format when available, because it usually includes the richest telemetry.
- You can import
.fit,.tcx, and.gpxfiles in FIT Dashboard. - If duplicate files are imported, FIT Dashboard will skip them automatically. But if you have the save activity file in different format, it may still cause duplicate. We de-duplicate them based on file hash and exact start and end timestamp match of an activity.
If you prefer the command line, Garmin-CLI can list your activities and download the original Garmin export for a specific activity. This can be easily automated to batch download multiple activities.
Steps:
-
Install Garmin-CLI (
cargo install garmin-cli, or use the pre-built MacOS/linux binary). -
Sign in once with password and OTP (if applicable):
garmin auth login. -
List your activities and copy the activity ID you want:
garmin activities list. use--limit 20to limit the result to last 20 activities -
Download the original FIT export for that activity:
garmin activities download <activity-id> -
Unzip the downloaded archive to get the
.fitfile, then import that file into FIT Dashboard.
Tip
You are automate the whole process with this one liner (update the limit to match your need, here it's set as 50)
garmin activities list --limit 50 | awk 'NR>2 && $1 ~ /^[0-9]+$/ {print $1}' | while read -r id; do garmin activities download "$id" && unzip "activity_${id}.zip" && rm "activity_${id}.zip"; doneWhen to use this option:
- You want to batch pull individual activities without using the Garmin Connect web UI.
- You want a repeatable way to fetch specific activity files by ID.
- Import: Open the sidebar Import section and select one or more
.fitfiles - Browse: Activities appear in the sidebar, sorted by date. Use filters to narrow down
- Analyze: Click an activity to view telemetry charts, map path, and performance insights
- Export: Right-click an activity for single export, or use "Export filtered" for bulk export
- Overview: Switch to the Overview tab for aggregate statistics across all activities
| Format | Description |
|---|---|
| CSV | Full time-series with all telemetry fields. Metadata JSON embedded in the first row. Speed in both m/s and km/h. |
| JSON | Structured export with activity metadata and full records array. Pretty-printed for readability. |
| GPX | Standard GPS Exchange Format with track segments. Extensions include heart rate, cadence, and power. |
| KML | Google Earth format with 3D line path using absolute altitude mode. |
Single export: Right-click → Export → choose format → browser download.
Bulk export: Click "Export filtered" → choose format → select destination folder → files are written one by one with progress overlay.
| Component | Purpose |
|---|---|
| Tauri v2 | Desktop application framework (feature-gated behind tauri-app) |
| Axum | Web REST API server for Docker/web deployment (feature-gated behind web) |
| DuckDB | Embedded analytical database — fast aggregations over millions of records |
| fitparser | Native FIT file parsing — no external tools required |
| Argon2 | Password hashing for authentication |
| Component | Purpose |
|---|---|
| React 18 + TypeScript | UI framework |
| Vite | Build tool with HMR |
| Zustand | Lightweight state management |
| ECharts | Telemetry charts with synchronized zoom |
| MapLibre GL | Interactive map with cooperative gestures |
| Vanilla CSS | Custom design system with CSS variables, dark/light theming |
fit-dashboard/
├── src-tauri/ # RUST BACKEND
│ └── src/
│ ├── main.rs # Entry point (feature-gated: Tauri or Axum)
│ ├── server.rs # Axum REST API routes
│ ├── database.rs # DuckDB schema, queries, downsampling
│ ├── fit_parser.rs # FIT file parsing with fitparser crate
│ ├── models.rs # Shared data structures
│ ├── auth.rs # Password hashing & session management
│ ├── state.rs # Shared application state
│ └── tauri_app.rs # Tauri IPC command handlers
│
├── src/ # REACT FRONTEND
│ ├── components/
│ │ ├── Dashboard.tsx # Main layout — sidebar, header, content
│ │ ├── ActivityChart.tsx # ECharts telemetry visualization
│ │ ├── ActivityMap.tsx # MapLibre map with path coloring
│ │ ├── ActivityInsights.tsx # Derived statistics & heatmaps
│ │ ├── SettingsPanel.tsx # Slide-over settings drawer
│ │ ├── Onboarding.tsx # First-use setup flow
│ │ ├── UnlockScreen.tsx # Password unlock screen
│ │ └── DonationBanner.tsx # Support banner
│ ├── stores/
│ │ ├── activityStore.ts # Activity data & selection state
│ │ └── settingsStore.ts # Theme, units, map style settings
│ ├── lib/
│ │ ├── api.ts # Backend adapter (Tauri IPC / Axios)
│ │ └── exportUtils.ts # CSV/JSON/GPX/KML export builders
│ ├── types.ts # TypeScript type definitions
│ ├── styles.css # Complete design system
│ ├── App.tsx # Root component with auth flow
│ └── main.tsx # React entry point
│
├── docker/ # DOCKER CONFIG
│ ├── Dockerfile # Combined backend + frontend image build
│ ├── docker-compose.yml # Prebuilt GHCR image deployment
│ ├── docker-compose-build.yml# Local source build deployment
│ └── nginx.conf # Reverse proxy config
│
├── index.html # HTML entry point
├── vite.config.ts # Vite configuration
├── tsconfig.json # TypeScript configuration
└── package.json # Node.js dependencies
All endpoints require the X-Session header after authentication (except /api/status, /api/onboard, and /api/unlock).
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/status |
Server status and onboarding check |
POST |
/api/onboard |
First-time user setup (username + password) |
POST |
/api/unlock |
Authenticate and receive session token |
POST |
/api/logout |
Invalidate current session |
POST |
/api/import-fit |
Import FIT file (multipart form data) |
GET |
/api/activities |
List all activities |
GET |
/api/overview |
Aggregate statistics |
GET |
/api/records/:id |
Telemetry records with ?resolution_ms= downsampling |
PATCH |
/api/activities/:id |
Rename activity |
DELETE |
/api/activities/:id |
Delete activity |
| Setting | Default | Description |
|---|---|---|
| Session TTL | 72 hours | Web session token lifetime. Desktop tokens persist until logout. |
| API Base | http://localhost:8080 |
Backend URL, configurable via VITE_API_BASE env var |
| Resolution | 10,000 ms | Default telemetry downsampling interval for chart queries |
| Export Resolution | 1,000 ms | Higher-resolution records used during export operations |
FIT Dashboard is designed as a local-first application. The web/Docker mode does NOT include TLS/HTTPS.
- Passwords are hashed with Argon2 before storage
- Session tokens are generated server-side and validated on every request
- For internet-facing deployments, use a reverse proxy (Nginx, Caddy, Traefik) with TLS termination
- fitparser for robust FIT decoding.
- DuckDB for fast embedded analytics.
- MapLibre GL JS for rendering activity maps.
- Tile/map providers used by built-in styles:
- ECharts for charting.
- Tauri and Axum for desktop and web runtime foundations.
If FIT Dashboard helps your training workflow, consider supporting ongoing development:
- Give this repository a star.
- Share it with other athletes, coaches, and data enthusiasts.
- Get a supporter badge via Ko-fi.
FIT Dashboard is a personal open-source project maintained in public. I started this project as an offline alternative to my other project garmin-grafana
- It is not affiliated with Garmin, Strava, Golden Cheetah, or any map/tile provider.
- All trademarks and product names are the property of their respective owners.
- Map data and tiles remain subject to each provider's attribution and usage terms.
- This project is AI assisted. Some feature implementations includes using Claude Opus 4.5 and Codex 5.3. Although design, artitecture, behaviour and implementation decisions are always made by myself. I test out features throughly before each release and try my best to address any reported issue within a reasonable timeline.
Copyright © 2025 Arpan Ghosh. Licensed under the GNU Affero General Public License v3.0.


