pace2fit
Generate Garmin FIT workout files from a simple text description.
A public instance is running at pace2fit.eu --
no install required, just open the page, type a workout, and download
the .fit file.
pace2fit generate 'warmup@6:00,3x(8min@threshold+2min@easy),cooldown@6:30' -o workout.fit
Quick start
No install needed -- run directly with uvx:
uvx pace2fit generate '10min@Z2'
Or from the git repo directly:
uvx --from git+https://codeberg.org/Solal/pace2fit pace2fit generate '10min@Z2'
Install
Requires Python 3.10+.
uv sync
Or install as a tool:
uv tool install .
Usage
Generate a workout
# Simple zone workout
pace2fit generate '10min@Z2'
# Structured workout with intervals
pace2fit generate 'warmup@6:00,3x(8min@threshold+2min@easy),cooldown@6:30' \
-o thursday.fit -n 'Thursday Threshold'
# Distance-based
pace2fit generate '5km@5:30-6:00' -o easy_run.fit
Options
pace2fit generate [OPTIONS] WORKOUT
Arguments:
WORKOUT Workout description string (required)
Options:
-o, --output PATH Output .fit file path (default: workout.fit)
-n, --name TEXT Workout name (default: the input string)
--unit [km|mi] Pace unit (default: km)
--help Show help and exit
Web interface
Start a local web UI for writing workouts in the browser:
pace2fit serve
pace2fit serve --port 3000
Open http://127.0.0.1:8000 in your browser. The page includes a DSL syntax cheatsheet with clickable examples. Dark mode follows your OS preference.
DSL Syntax
A workout is a comma-separated list of steps:
warmup@6:00, 10min@Z2, 3x(8min@4:30+2min@6:00), cooldown@6:30
Durations
| Syntax | Meaning |
|---|---|
10min |
10 minutes |
30sec or 30s |
30 seconds |
5km |
5 kilometres |
400m |
400 metres |
warmup |
Open duration (lap button), warmup intensity |
cooldown |
Open duration (lap button), cooldown intensity |
open |
Open duration (lap button) |
Targets
Targets are specified with @ after the duration.
Pace targets (min:sec per km):
| Syntax | Meaning |
|---|---|
@5:30 |
5:30/km (auto-creates a +/-5s range) |
@5:00-6:00 |
Pace range from 5:00 to 6:00/km |
Heart rate zones:
| Syntax | Meaning |
|---|---|
@Z1 .. @Z5 |
HR zone 1 through 5 |
Named pace zones:
| Name | Pace range |
|---|---|
easy |
6:00 - 6:30/km |
recovery |
6:30 - 7:00/km |
tempo |
5:00 - 5:15/km |
threshold |
4:30 - 4:45/km |
marathon |
5:00 - 5:20/km |
hm |
4:40 - 4:55/km |
interval |
3:45 - 4:15/km |
repetition |
3:30 - 3:45/km |
Repeats
3x8min@threshold # 3 repeats of a single step
3x(8min@5:00+2min@6:00) # 3 repeats of work + recovery
4x(1min@Z4+1min@Z2+30s@Z5) # 3 steps repeated 4 times
Full examples
# Easy 30 min run
pace2fit generate '30min@easy'
# 10k tempo
pace2fit generate 'warmup@6:00,10km@tempo,cooldown@6:30' -n '10k Tempo'
# Classic threshold session
pace2fit generate 'warmup@6:00,3x(8min@threshold+2min@easy),cooldown@6:30'
# Track intervals
pace2fit generate 'warmup@6:00,8x(400m@Z5+400m@Z1),cooldown@6:30' -n 'Track 400s'
# Pyramid
pace2fit generate 'warmup@6:00,2min@Z3,4min@Z4,6min@Z4,4min@Z4,2min@Z3,cooldown@6:30'
Output
The generated .fit file can be loaded onto Garmin devices or uploaded to Garmin Connect as a workout.
Development
# Install dependencies
uv sync
# Install pre-commit hooks
prek install
# Run tests
uv run pytest
# Run tests with verbose output
uv run pytest -v
# Run all hooks manually
prek run --all-files
This project uses prek for pre-commit hooks.
Hooks run automatically on every commit and include ruff (lint + format),
pytest, and basic file hygiene checks. See prek.toml for the full
configuration.
Deploying
pace2fit can be self-hosted using Docker behind a reverse proxy.
Prerequisites
- A server with Docker installed
- A reverse proxy (e.g. Caddy) with a domain pointing to your server
DNS
Add an A record for your domain pointing to your server's public IP:
@ IN A <your-server-IPv4>
Build and run
git clone ssh://git@codeberg.org/Solal/pace2fit.git
cd pace2fit
GIT_COMMIT=$(git rev-parse --short HEAD) docker compose up -d --build
This builds the image and starts the container on 127.0.0.1:8000.
Caddy configuration
Add a block to your Caddyfile:
pace2fit.eu {
reverse_proxy localhost:8000
}
Then reload Caddy:
sudo systemctl reload caddy
Caddy automatically provisions HTTPS via Let's Encrypt.
Updating
cd pace2fit
git pull
GIT_COMMIT=$(git rev-parse --short HEAD) docker compose up -d --build
Dependencies
Metadata
Release files for pace2fit 0.1.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| pace2fit-0.1.3.tar.gz | 28.6 kB | Without provenance | Details for pace2fit-0.1.3.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| pace2fit-0.1.3-py3-none-any.whl | Python 3 | none | any | Details for pace2fit-0.1.3-py3-none-any.whl |
Total release size: 61.0 kB
Release files / pace2fit-0.1.3.tar.gz
| Download URL | pace2fit-0.1.3.tar.gz |
|---|---|
| Size | 28.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
87ca03ac608d92cd55d0e8593eb4e35f11d897dc071af769d48cdf162b261d98
|
|
BLAKE2b-256 checksum How to use checksums |
fb2f994c2952242bfa60e59b8b73f7782e6f438411ec7808f8bda39c8078a1d9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / pace2fit-0.1.3-py3-none-any.whl
| Download URL | pace2fit-0.1.3-py3-none-any.whl |
|---|---|
| Size | 32.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
876255ffcc8eee39aaa5fffffa051a94a0d4e9c88d8e43a9756dd24678f90061
|
|
BLAKE2b-256 checksum How to use checksums |
dd359557262dac62f4fdbf6c7da08d6def989a50378089aa06b5575797274146
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for pace2fit 0.1.3
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.