Uniform stock management for healthcare linen rooms: the coordinator app, the phone counter and the staff app, for your own server. Built from 794bab5 on 2026-09-16. Licensed under the Functional Source License (FSL-1.1-ALv2).
3.9 KiB
title, section, order, summary, role, keywords
| title | section | order | summary | role | keywords |
|---|---|---|---|---|---|
| Update to a new release | selfhost | 3 | Back up, fetch the release, rebuild, and let the migration run before the new app starts. Why a plain git pull does not work. | Self-hosting admin | update, upgrade, release, new version, git pull, fetch, reset, rebuild, migrations, schema, changelog, version |
How releases are published
Releases go out about once a week. Each one is a tag named after the date it was cut, such as v2026.09.17, with .2 added for a second release that day. Every release is listed with its notes on the repository's Releases page, and the same notes are in CHANGELOG.md. To hear about new ones on GitHub, choose Watch, then Custom, then Releases.
A release is one commit with no history behind it. It is exported from ThreadCount's own code with the parts that belong only to threadcount.tech removed, and it must type-check, build with EDITION=community and pass a smoke test before it is tagged. main points at the newest build. Every tag stays downloadable after main moves on, which is what makes going back possible.
The tag and the source commit are written to the COMMUNITY_VERSION file in the checkout:
community v2026.09.17 b36d739
A running server reports that line as version at /api/app-info.
Back up first
Before every update, take a database dump and a copy of the photos, as in backups:
docker/backup.sh /srv/backups/threadcount
Migrations only go forwards. prisma migrate deploy applies new migrations and has no step that undoes one, so the way back to an older release is to restore the dump you took before updating.
Fetch the release
Because each release replaces the last commit rather than adding to it, git pull refuses to merge the two. Fetch the tags, then move your checkout to the newest release:
cd /srv/threadcount
git fetch --tags --force origin
git reset --hard "$(git tag --sort=-v:refname | head -1)"
cat COMMUNITY_VERSION
To move to a particular release instead, name its tag: git reset --hard v2026.09.17.
.env is ignored by git, so git reset --hard leaves it alone.
Careful
git reset --hardthrows away any change you made to a file in the repository,docker-compose.ymlincluded. Keep local settings in.env, or copy your changes somewhere before you reset.
Rebuild and start
docker compose up -d --build
What happens, in order:
- The images are rebuilt. The build takes
NEXT_PUBLIC_SITE_URLandNEXT_PUBLIC_TURNSTILE_SITEKEYfrom.envagain. dbis checked. Compose waits until Postgres answerspg_isready.migrateruns. It runsnpx prisma migrate deployagainst the bundled database and applies any migration the database does not have yet.appstarts. Compose starts the new app only ifmigrateexited successfully.
If a migration fails, app is not started. Read what went wrong with:
docker compose logs migrate
Then check the app is healthy:
docker compose ps
curl -fsS https://uniforms.example.health/api/health
Go back to an older release
Migrations only go forwards, so going back needs the database dump you took before updating.
- Stop the app. Run
docker compose down. - Restore the dump. Follow backups.
- Move to the older tag. Run
git reset --hard v2026.09.17, naming the release you were on. - Rebuild. Run
docker compose up -d --build.
What changed
Each release's notes are on the repository's Releases page and in CHANGELOG.md in your checkout, newest first. They describe the Community edition only.
Phones after an update
The Android apps ask your server at /api/app-info for the oldest app version it still works with, given in minApp. A phone running an older app is told to update rather than failing. Phones using /m or /my in a browser get the new version the next time the page loads.