ThreadCount Community edition
Release / release (push) Has been skipped

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).
This commit is contained in:
ThreadCount
2026-09-17 05:49:16 +10:00
commit f72f0626b0
481 changed files with 59411 additions and 0 deletions
+92
View File
@@ -0,0 +1,92 @@
---
title: Update to a new release
section: selfhost
order: 3
summary: Back up, fetch the release, rebuild, and let the migration run before the new app starts. Why a plain git pull does not work.
role: Self-hosting admin
keywords: 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](/docs/selfhost/backups):
```sh
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:
```sh
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 --hard` throws away any change you made to a file in the repository, `docker-compose.yml` included. Keep local settings in `.env`, or copy your changes somewhere before you reset.
## Rebuild and start
```sh
docker compose up -d --build
```
What happens, in order:
1. **The images are rebuilt.** The build takes `NEXT_PUBLIC_SITE_URL` and `NEXT_PUBLIC_TURNSTILE_SITEKEY` from `.env` again.
2. **`db` is checked.** Compose waits until Postgres answers `pg_isready`.
3. **`migrate` runs.** It runs `npx prisma migrate deploy` against the bundled database and applies any migration the database does not have yet.
4. **`app` starts.** Compose starts the new app only if `migrate` exited successfully.
If a migration fails, `app` is not started. Read what went wrong with:
```sh
docker compose logs migrate
```
Then check the app is healthy:
```sh
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.
1. **Stop the app.** Run `docker compose down`.
2. **Restore the dump.** Follow [backups](/docs/selfhost/backups).
3. **Move to the older tag.** Run `git reset --hard v2026.09.17`, naming the release you were on.
4. **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.