🚀 Endurain: community package now available
-
Endurain: community package now availableTL;DR: Endurain is a self-hosted fitness tracking application for endurance sports, a place to keep your runs, rides and swims on your own server instead of a vendor's. It is now packaged for Cloudron, with single sign-on wired into its own login and the device upload path left open for phone apps. Unofficial and community-maintained. Please read the known issues before you trust it with anything you care about.
Links
- Project homepage: https://endurain.com
- Upstream source: https://codeberg.org/endurain-project/endurain
- Cloudron package: https://github.com/OrcVole/endurain-cloudron
Once installed it is a full web application: an activity feed, per-activity maps and charts, gear tracking, weight and health logging, and a search page.
How to installThe easy route is the dashboard. Click the Add custom app dropdown at the top right of the App Store, choose Community app, and paste this URL into the box:
https://raw.githubusercontent.com/OrcVole/endurain-cloudron/main/CloudronVersions.jsonApps installed that way receive automatic updates.
The CLI does the same thing:
cloudron install \ --versions-url https://raw.githubusercontent.com/OrcVole/endurain-cloudron/main/CloudronVersions.json \ --location endurain.example.comAddons used: postgresql, redis, sendmail, localstorage, oidc. No extra subdomains.
First run: upstream's first migration seeds a live
admin/adminaccount, so the package replaces that password with a generated one before the application ever serves a request, and writes it to/app/data/.secrets/admin-initial-passwordfor you to read through the dashboard's file manager. Change it and delete the file. Single sign-on, if you enable it, feeds Endurain's own OpenID Connect login rather than sitting in front of the app, so API clients keep working.
️ Known issues, up frontTwo faults in the application itself, both found while packaging and both reported upstream with evidence. Neither is caused by the packaging and neither can be fixed from outside the application.
Uploading several activities at the same time can leave database connections stranded in an open transaction, after which every request that needs the database hangs and the application does not recover on its own. Restarting clears it and loses nothing. If you are importing a backlog, upload a few at a time rather than in parallel.
Separately, if two activities end up sharing a start time, every later upload at that timestamp fails with a server error until one of them is deleted.
The package mitigates the operational half of the first one: its health check reads the database rather than returning a static response, so an instance in that state is detected and restarted by the platform automatically instead of sitting there looking healthy. That is worth knowing about even if you never hit it, because a health check that touches nothing cannot tell a working app from a dead one.
For usersWhy try it: your training history is yours, it stays on your server, and it backs up with everything else on the box.
What you get: FIT, GPX and TCX uploads, automatic upload from phone apps over an API key, per-user Strava and Garmin Connect linking, maps rendered server side, gear and weight tracking, and a multi-user instance with followers.
Cloudron wins: single sign-on into the app's own accounts, platform backups covering both the database and your uploaded activity files, and all state under
/app/data.Good fit if you want Strava-shaped features without Strava. Probably not if you need polished mobile apps of its own, since the phone side is third-party apps posting to the upload endpoint.
🧰 For packagers: what we learned
What helped: the addons covered every store, so there is nothing bundled and no
backupCommandat all. Postgres, Redis, mail and OIDC all came straight from the platform.What was tricky, and all four are worth stealing:
- The static file server refuses to serve a symlink whose target leaves the served directory. The built frontend has to be copied into a writable tree, not symlinked back into the read-only image, or every asset 404s while the page itself loads and the app is a blank white screen.
- Creating an OIDC provider record is not enough to switch single sign-on on. There is a separate server setting, defaulting to off, and without it the provider exists, the API lists it, the whole OAuth flow works by hand, and the login page shows no button.
- The application refuses server-side requests to URLs that resolve to private addresses, which is a good default that blocks Cloudron single sign-on outright, because an app container reaches the dashboard over the internal bridge and the dashboard hostname therefore resolves privately inside the container while resolving publicly everywhere else. The fix is the application's own allowlist, scoped to the issuer hostname alone and never to the bridge network.
CMD script.shleaves the shell as PID 1 for the whole boot, and a non-interactive shell does not act on SIGTERM while it waits for a foreground child. A stop during migrations burned the platform's entire grace period and ended in SIGKILL. Re-execing the script undertini -gon its first line fixes the whole window.
Still rough: the memory limit is sized from measurement, but on a shared host. Idle sits near 200 MiB and the heaviest load I could construct, a bulk import running alongside concurrent uploads, peaked at 384 MiB against the 1.5 GiB the package ships. Someone else's numbers on a quieter box would be a useful check.
️ For the Cloudron teamMaintenance burden is low: the package is a Dockerfile, a start script and a provisioning script, with no patches to upstream and no bundled services.
Friction worth knowing: an application whose health endpoint touches no dependency can be completely wedged while the platform reports it as running indefinitely. That is not a platform bug, but it is a sharp edge, and a note in the packaging documentation recommending that
healthCheckPathexercise the app's primary datastore would save the next packager discovering it the way this one did.
For Endurain's developersThank you for a genuinely pleasant application to package. Three small things would help every deployment method rather than only this one:
- The seeded
admin/adminaccount means every deployment has to independently notice it and do something before first serve. A generated password or a forced change on first login would remove that once, upstream. - A JSON API request without an
X-Client-Typeheader is rejected with the same 401 as a wrong password, which reads as a credentials problem and cost real time to diagnose. - A stated libc and Python floor would let packagers build against a documented fact rather than inferring one from the lockfile.
Package source and pull requests welcome. Happy to co-maintain.
SynergiesEndurain + Gadgetbridge or OpenTracks: record on the phone, upload straight to your own instance over an API key, with no vendor cloud in the path.
Endurain + wger: if you also track strength work, the two cover different halves of the same habit, and body weight is the obvious thing to keep in step between them.
Feedback, bug reports, and confirmations that it works on your install are all welcome below.
-
L LoudLemur referenced this topic
-
L LoudLemur referenced this topic
Hello! It looks like you're interested in this conversation, but you don't have an account yet.
Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.
With your input, this post could be even better 💗
Register Login