Garava Selective Garmin-to-Strava Activity Sync

Garava is a free, open-source Python CLI that syncs Garmin Connect activities to Strava selectively. If you record with a cycling computer or gym app alongside a Garmin watch, Garava blocks the Garmin version so the better data source lands on Strava. Runs, hikes, swims and everything else sync automatically.

Sound familiar?

Garmin watches record every activity, but if you also use a Karoo cycling computer, Hevy for gym logging or another app that syncs to Strava, you end up with duplicates. Garava blocks the Garmin versions of activities you have a better source for.

Garmin watch + Karoo. You wear a Garmin for training load and ride with a Karoo. Both record the ride. The Karoo has your power meter and richer cycling data -- that's what you want on Strava. But Garmin pushes its version too and you get a duplicate (or Strava picks the wrong one).

Garmin watch + Hevy. You wear a Garmin during gym sessions for training load but log sets and reps in Hevy. Hevy syncs detailed workout data to Strava. Garmin also syncs a generic "Strength Training" entry with nothing but heart rate. Two entries, one is useless.

The fix is the same. Garava replaces Garmin's native Strava sync and blocks the activity types you have a better source for. Everything else still syncs automatically.

karoo cyclist + hevy gym user
Morning Run         → Strava
Road Cycling        → Blocked  (Karoo has it)
Strength Training   → Blocked  (Hevy has it)
Trail Run           → Strava
Open Water Swim     → Strava
Indoor Cycling      → Blocked  (Karoo has it)
Hiking              → Strava

How it works

Garava runs in the background on macOS, Linux or a server, syncing on the quarter hour (:00, :15, :30, :45). It fetches activities from Garmin Connect, checks them against your blocklist, downloads original FIT files for allowed activities and uploads them to Strava. If your machine is asleep, it picks up when it wakes.

1

Fetch

Pulls your latest activities from Garmin Connect. Syncs on the quarter hour — :00, :15, :30, :45.

2

Filter

Checks each activity against your blocklist. Block activity types you have a better source for -- cycling (Karoo handles it), gym (Hevy handles it), whatever applies to your setup.

3

Sync

Downloads the original FIT file and uploads it to Strava. Heart rate, power, GPS — everything comes through.

What else it does

Garava tracks every processed activity in a local SQLite database to prevent duplicates, automatically refreshes expired Garmin and Strava tokens, and supports deployment via launchd, systemd, cron or Docker.

You Pick the Blocklist

One environment variable, comma-separated list. Block cycling types if you ride with a Karoo, gym types if you log with Hevy, or whatever fits your setup.

Original FIT Files

Uploads the actual FIT file from Garmin, not a summary. Heart rate, power, GPS — it all comes through.

No Duplicates

Tracks every activity in a local database. If it already synced something, it won't sync it again.

Handles Its Own Errors

If a token expires or an upload fails, it sorts itself out. Failed activities get retried next cycle.

Run However You Want

One-off sync or continuous polling. Works with launchd, systemd, cron or Docker.

Docker If You Want It

Dockerfile and docker-compose included if you want to run it on a home server or VPS.

Get started

Garava requires Python 3.9+, a Strava API application and your Garmin Connect credentials. Before installing, disable Garmin's native Strava sync — Garava replaces it entirely, continuing to sync runs, hikes, swims and everything not on your blocklist.

# Clone and install
$ git clone https://github.com/lestat832/garava.git && cd garava
$ pip install -e .
 
# Configure your Strava API credentials
$ cp .env.example .env && nano .env
 
# Authenticate with Garmin + Strava
$ garava setup
 
# Sync your activities
$ garava run --once