Files
threadcount-community/docs/self-hosting.md
T
ThreadCount 1bc2de655a ThreadCount Community edition
Uniform stock management for healthcare linen rooms. Licensed under the GNU AGPL v3.
2026-09-13 08:45:19 +10:00

109 lines
5.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Self-hosting ThreadCount (Community edition)
The Community edition is the hosted product with `EDITION=community` in its environment. This
page says what that changes, how to run it, how to keep it, and what is not included.
## What `EDITION=community` changes
| | Hosted (threadcount.tech) | Community |
|---|---|---|
| Features | All | All |
| Plans, staff ceiling, read-only for non-payment | Yes | None. Every facility behaves as a grandfathered one. |
| Public website at `/` | The marketing site | Redirects to sign-in |
| Demo facility | Yes | Off |
| Operations console (`ops.` hostname) | Yes | Off unless `OPS_SESSION_SECRET` and the ops database roles are set up |
| Cloudflare Turnstile on sign-in and sign-up | Required | Optional. Set both keys to enforce it; without it the per-address rate limits stand alone. |
| Error reports | To ThreadCount's own GlitchTip, scrubbed | Nowhere, unless you set `NEXT_PUBLIC_GLITCHTIP_DSN` to your own |
| Usage statistics | To ThreadCount's own Umami, scrubbed | Nowhere. The tracker only mounts on `*.threadcount.tech`. |
| Single sign-on for a facility | Via ThreadCount's Jackson broker | Run your own [BoxyHQ Jackson](https://boxyhq.com/docs/jackson/overview) and set `JACKSON_URL` and `JACKSON_API_KEY`; otherwise no SSO |
| Transactional email | ThreadCount's SMTP | Your SMTP, or none |
Everything the legal pages on threadcount.tech say applies to the hosted service. Your instance
is yours: publish your own privacy statement for your staff. The pages under `/privacy`,
`/terms` and so on still render on a Community instance, but they describe threadcount.tech.
## Running it
`docker-compose.yml` runs three services: `db` (Postgres 16, in a named volume), `migrate` (a
one-shot `prisma migrate deploy` that runs before the app on every `up`), and `app` (Next.js's
standalone server, port 3000, photographs in a second named volume). Put TLS in front of port
3000. A minimal Caddyfile:
```
uniforms.example.health {
reverse_proxy 127.0.0.1:3000
}
```
The image is built on your machine, not pulled, because `NEXT_PUBLIC_SITE_URL` and the optional
Turnstile site key are compiled into the browser bundle. If you change either, rebuild:
`docker compose up -d --build`.
### The first facility
Open the hostname. Create an account; that creates a facility and makes you its administrator.
Under Settings, name your staff groups first (nothing works until a facility has them), then load
the catalogue, departments, staff register, barcodes and opening stock from CSV under Settings
Data. Add a second administrator under Settings Account Users before you sign out — a
forgotten password with one admin and no SMTP locks the facility.
Then set `SIGNUPS_DISABLED=1` in `.env` and `docker compose up -d`. Your instance is now one
facility, and nobody else can create another.
## Upgrading
```sh
git pull
docker compose up -d --build
```
`migrate` applies any new schema migrations before the new app starts; the old app keeps
serving until then. Take a backup first (below). Releases that need more than that say so in
their notes.
## Backups
`docker/backup.sh <directory>` writes one directory per run: the database as a custom-format
`pg_dump` and the photographs as a tarball, keeping the last fourteen. Run it nightly from cron:
```
30 3 * * * cd /srv/threadcount && docker/backup.sh /srv/backups/threadcount >> /var/log/threadcount-backup.log 2>&1
```
Copy that directory somewhere off the machine. A backup on the same disk as the database is not a
backup.
Separately, an administrator can download the whole facility as one JSON file from Settings
Data at any time, and restore it into a fresh instance from the same screen. That file is the
portable form; the `pg_dump` is the fast one.
### Restoring
```sh
docker compose down
docker volume rm threadcount_db threadcount_photos # only if you mean to replace everything
docker compose up -d db
docker compose exec -T db pg_restore -U threadcount -d threadcount --clean --if-exists < 2026-09-13-033001/threadcount.dump
docker compose up -d
docker compose cp 2026-09-13-033001/photos app:/data/ # after extracting photos.tgz
```
## The Android apps
The two Play apps (ThreadCount, the counter; ThreadCount Staff) load their screens from
`https://threadcount.tech` and cannot be pointed at another host. On a self-hosted instance use
the same screens in a phone's browser: `https://your-host/m` for the counter (camera scanning
works in Chrome and Edge) and `https://your-host/my` for staff. Add either to the home screen.
## Environment reference
`.env.example` lists every variable the application reads, with a comment on each. The ones that
matter for a Community instance are `SESSION_SECRET`, `POSTGRES_PASSWORD`, `NEXT_PUBLIC_SITE_URL`,
`EDITION`, the `SMTP_*` group, and `SIGNUPS_DISABLED`. Leave the operations console, Cloudflare
Access, Listmonk, Umami and GlitchTip variables empty unless you run those yourself.
## Getting help
Open an issue on the repository. If you would rather someone else ran it, that is what
[threadcount.tech/pricing](https://threadcount.tech/pricing) is for.