Files
threadcount-community/docs/manual/selfhost/updating.md
T
ThreadCount f72f0626b0
Release / release (push) Has been skipped
ThreadCount Community edition
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).
2026-09-17 05:49:16 +10:00

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 --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

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:

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.

  1. Stop the app. Run docker compose down.
  2. Restore the dump. Follow 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.