jovandeginste/workout-tracker: a self-hosted GPX workout tracker you run yourself
A workout tracking web application for personal use (or family, friends), geared towards running and other GPX-based activities
At a glance
- What is it?
- A Go web application that stores your GPX, TCX and FIT files in your own database, with route segments, equipment tracking and a heatmap. It is built for one person or a small household, not for a training plan.
- Who is it for?
- Adopt it if you run or ride with a GPS watch, want your GPX history in a database you control, and are comfortable running a Docker container or a single Go binary. Do not adopt it if you need a coached training plan, structured interval targets, or a phone app that works offline.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What problem workout-tracker solves, and who it is for
The README describes the project in one line: a workout tracking web application for personal use, or for family and friends, geared towards running and other GPX-based activities. That scope matters more than the feature list. This is not a training platform and it does not prescribe workouts. It is a place to put activity files and get statistics back.
The target user is someone who already records runs, rides or walks on a device that exports GPX, TCX or FIT, and who does not want that history sitting inside a vendor account. The README lists the activities it handles: uploaded workout records, personal daily stats such as weight and step count, manually created records for weight lifting, push-ups or swimming, route segments, equipment tracking, statistics and a heatmap. Manual entries matter here, because a push-up session has no GPS trace and the project still counts it.
The multi-user wording is deliberate but narrow. The README says personal use, or family and friends, and the login section notes that new users have to be activated by an admin and that registration can be disabled. That is a household deployment, not a shared club server with roles and permissions.
How the Go server, the database and the GPX files fit together
The repository layout is a single Go module, github.com/jovandeginste/workout-tracker/v2, with the web layer in cmd and pkg, templates in views, frontend assets in frontend, and a docs directory that contains a swagger.json. The dependency list in go.mod shows the shape of the thing: echo/v5 for HTTP, templ for server-rendered views, gorm with drivers for sqlite, postgres and mysql, and three separate parsers for the file formats it accepts, gpxgo, galeone/tcx and muktihari/fit.
So the data flow is file in, parsed track in the database, rendered map and statistics out. There is no separate analytics service and no external queue. Geo work is done in-process: orb for geometry, tzf for time zones, geomag for magnetic calculations. That is why the project ships as one binary and one container, and why the README can say self-hosted, everything included.
Two details are worth flagging. The API is documented with swagger, and the README gives Fitotrack as an example of a client that uploads automatically, so the intended ingestion path is not only the browser. Second, route segments are detected by matching your workouts against a segment you defined, according to the feature list. The README does not describe the matching tolerance, so how close a run must be to count as a segment match is something you would have to read in the code.
Installing workout-tracker with Docker and uploading a first file
The README gives Docker as the primary path. The image is published on GitHub Container Registry, with amd64 and arm64 builds for the latest and release tags, and the current directory is mounted as the data directory. Run it and open http://localhost:8080.
docker run -p 8080:8080 -v .:/data ghcr.io/jovandeginste/workout-tracker:latestThat starts the server with the working directory as storage. To keep data and sessions across restarts, the README passes a JWT encryption key and a named data directory. Without that key, the README implies sessions are not persisted.
docker run -p 8080:8080 \
-e WT_JWT_ENCRYPTION_KEY=my-secret-key \
-v $PWD/data:/data \
ghcr.io/jovandeginste/workout-tracker:masterThe key can also come from a file, which is the better fit for a secrets mount. The README shows WT_JWT_ENCRYPTION_KEY_FILE pointing at a mounted file.
docker run -p 8080:8080 \
-e WT_JWT_ENCRYPTION_KEY_FILE=/run/secrets/jwt_encryption_key.txt \
-v $PWD/jwt_encryption_key.txt:/run/secrets/jwt_encryption_key.txt \
-v $PWD/data:/data \
ghcr.io/jovandeginste/workout-tracker:masterFor a longer-lived install the README recommends docker compose, with separate compose files for sqlite and postgres. The sqlite variant downloads two files, the postgres variant downloads three, including postgres.env, which the README says you must configure. Once the container is up, register through the login page, have an admin activate the account, then upload a GPX, TCX or FIT file from the workout overview. The details view shows a zoomable, draggable map of the track with per-point details and summarized statistics.
mkdir -p /opt/workout-tracker
cd /opt/workout-tracker
curl https://raw.githubusercontent.com/jovandeginste/workout-tracker/master/docker/docker-compose.base.yaml --output docker-compose.base.yaml
curl https://raw.githubusercontent.com/jovandeginste/workout-tracker/master/docker/docker-compose.sqlite.yaml --output docker-compose.yaml
docker compose up -dThere is also a native path: download a pre-built binary from the releases page, unpack it, and run it. The README states that this creates a new database file in the current directory and starts the web server at http://localhost:8080, and that WT_JWT_ENCRYPTION_KEY or WT_JWT_ENCRYPTION_KEY_FILE persists sessions there too.
Where workout-tracker is the wrong tool
The largest gap is training prescription. Nothing in the README mentions structured workouts, interval targets, FTP or pace zones, plan builders or a coaching dashboard. If you want a system that tells you what to run on Thursday, this project will store the result of that run and nothing more. The statistics are retrospective.
The second gap is mobile. The feature list describes a web application and an API. There is no mention of a native phone app or offline capture, so the workflow is device to file to upload. The README points at Fitotrack as an example of a client that pushes through the API, which suggests third-party apps fill that role, but the project itself does not ship one.
Third, the project is honest that its test corpus is incomplete. The README asks users to donate workout files, including raw files from devices, and warns donors to strip personally identifiable information such as a home address. A project soliciting real files for testing is telling you that format edge cases from different devices are exactly where bugs live. If you own an unusual watch, expect to be the one who finds the parsing problem.
Finally, the licence situation deserves a direct look. The repository metadata reports NOASSERTION, while the README carries an MIT badge and the repository contains a LICENSE file. That mismatch is not a reason to avoid the project, but it is a reason to open LICENSE before you depend on it.
How it differs from Garmin Connect, Strava and a spreadsheet
The obvious alternatives are hosted services and manual logs, and the difference is not features. It is custody and effort.
Against a hosted service such as Garmin Connect or Strava, the split is straightforward: those run the parsing, storage, social features and mobile apps for you, and you accept an account, their retention rules and their terms. workout-tracker runs on your hardware with your database, and in exchange you operate the container, back up the data directory, and supply the upload path yourself. The README's Docker section is the whole deployment story, which is short, but it is still a server you now own.
Against a spreadsheet, the difference is the file format. A spreadsheet holds numbers you typed. This project parses GPX, TCX and FIT, keeps the track geometry, draws the map, and computes segments and a heatmap from that geometry. You cannot reproduce segment matching or a heatmap in a spreadsheet without writing the parser yourself.
Against other self-hosted trackers, the distinguishing choice visible in go.mod is breadth of format support in one process: gpxgo, tcx and fit parsers plus orb for geometry. A tool built around one format will be simpler to operate and will reject the file your watch produced. This one accepts three formats and asks you to donate the odd ones.
Maintenance, upgrades and what the licence file actually says
The last push to master was on 2026-09-02, and the most recent tagged releases are v2.9.0 and v2.8.0, both dated 2026-07-12, with v2.7.0 before them on 2026-06-25. Releases are tagged and signed through the Makefile release target, which runs git tag -s -a and then prints the push and gh release create commands, so versioning is deliberate rather than continuous.
Upgrade cost is low but not zero. The container is the unit of deployment, so an upgrade is a new image tag plus a restart against the same mounted data directory. The README does not document a rollback procedure or a migration step, and it does not state whether a newer version can read a database written by an older one. That is the thing to check before you upgrade a database you care about: take a copy of the data directory first, because the README is silent on going backwards.
The dependency list is broad, covering HTTP, ORM, templating, three file parsers, geo libraries and a JWT library. Each of those is a thing that can need updating. The Makefile has an install-deps target that runs npm install in frontend, and a watch/tailwind target, so frontend assets are built with Node tooling even though the server is Go. Building from source therefore needs both toolchains, which the pre-built binaries and the container avoid.
On licensing, the README badge says MIT and points at opensource.org/licenses/MIT, and the repository root contains a LICENSE file. The metadata field for this repository reports NOASSERTION, which means the licence was not automatically identified. Read the LICENSE file and, if you plan to redistribute or embed the project, get your own advice rather than relying on the badge.
Editorial conclusion
Adopt it if you run or ride with a GPS watch, want your GPX history in a database you control, and are comfortable running a Docker container or a single Go binary. Do not adopt it if you need a coached training plan, structured interval targets, or a phone app that works offline. Before you commit, verify that the licence file matches the MIT badge in the README, and decide where the data directory and the JWT encryption key live, because sessions depend on that key.
Frequently asked questions
What is jovandeginste/workout-tracker?
It is a self-hosted workout tracking web application written in Go, described in the README as being for personal use or for family and friends and geared towards running and other GPX-based activities. It stores uploaded GPX, TCX and FIT files, daily stats, manual workouts, route segments, equipment and a heatmap.
How do I install jovandeginste/workout-tracker?
The README gives two paths: run the published container image from GitHub Container Registry with port 8080 mapped and a data directory mounted, or download a pre-built binary from the releases page and run it, which creates a database file in the current directory. Docker compose files are provided for both sqlite and postgres.
Does jovandeginste/workout-tracker need a JWT encryption key?
The README says to pass WT_JWT_ENCRYPTION_KEY, or WT_JWT_ENCRYPTION_KEY_FILE pointing at a file, in order to persist data and sessions. Without it, sessions are not persisted across a restart.
What file formats can I upload to jovandeginste/workout-tracker?
The feature list names gpx, tcx and fit files, uploaded manually or automatically through the API, with Fitotrack given as an example client. Manual workout records can also be created for activities without a track, such as weight lifting, push-ups or swimming.
Is jovandeginste/workout-tracker free?
The README carries an MIT licence badge and the repository contains a LICENSE file, but the repository metadata reports the licence as NOASSERTION, so the badge and the metadata do not agree. Check the LICENSE file itself before relying on either.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/jovandeginste-workout-tracker)