Files
threadcount-community/docs/manual/selfhost/backups.md
T
ThreadCount 0910bc32c1 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 38e16eb on 2026-09-15. Licensed under the Functional Source License (FSL-1.1-ALv2).
2026-09-16 07:57:54 +10:00

5.1 KiB
Raw Blame History

title, section, order, summary, role, keywords
title section order summary role keywords
Backups and restore selfhost 4 Nightly database dumps and photo copies from the compose file's own services and volumes, the in-app export as a second copy, and how to restore and test a restore. Self-hosting admin backup, restore, pg_dump, pg_restore, database, photos, signatures, volume, cron, export, import, disaster recovery, test restore

What has to be kept

A Community instance keeps its data in two Docker volumes. Back up both, together.

Volume Mounted at Holds
threadcount_db /var/lib/postgresql/data in db Every record: catalogue, stock, staff, issues, orders, requests, users
threadcount_photos /data/photos in app Signatures and damage photographs

The names come from docker-compose.yml: the project is named threadcount, and its volumes are db and photos.

Photos are not in the database. Each one is a file at <facility id>/<photo id>.jpg (or .png) under /data/photos, and the database holds only that path. A database dump without the photos restores every record but none of the signatures or photographs.

Nightly dumps

The checkout includes docker/backup.sh. Run it from the checkout and give it a destination directory:

cd /srv/threadcount
docker/backup.sh /srv/backups/threadcount

Each run writes a dated directory, for example 2026-09-15-033001, containing:

  • threadcount.dump: the database as a custom-format pg_dump, taken inside db as the threadcount user
  • photos.tgz: the photos directory copied out of app

It keeps the newest 14 directories and deletes older ones. Run it nightly from cron:

30 3 * * * cd /srv/threadcount && docker/backup.sh /srv/backups/threadcount >> /var/log/threadcount-backup.log 2>&1

Careful A backup on the same disk as the volumes is lost with them. Copy the backup directory to another machine.

The export as a second copy

An admin can download the whole facility as one JSON file from Settings Data & audit log with Export backup. It needs no access to the server's command line, and it can be imported on another server.

Part In the export
All facility records Yes
Photos The newest ones only: at most 2000 photos and 40 MB of image data. The screen says how many older photos were left out.
Users (admins and issuers) No
Staff app logins No. A restore keeps the existing logins and re-attaches them by staff number.

Import backup on the same screen asks you to confirm, then replaces all of that facility's data with the file's. Users are kept. See export and backup.

Because it leaves out older photos and users, the export is a second copy, not a replacement for the dump.

The Backup line on that screen shows the date of the last export. For admins, Settings in the menu shows ! when that export is more than 7 days old or has never been taken. A run of docker/backup.sh does not change that date.

Restore from a dump

This replaces everything on the server with the backup. Choose the backup directory first; these steps use 2026-09-15-033001.

  1. Extract the photos.
cd /srv/backups/threadcount/2026-09-15-033001
tar -xzf photos.tgz
  1. Stop the stack and remove both volumes.
cd /srv/threadcount
docker compose down
docker volume rm threadcount_db threadcount_photos
  1. Start only the database, then load the dump.
docker compose up -d db
docker compose exec -T db pg_restore -U threadcount -d threadcount --clean --if-exists < /srv/backups/threadcount/2026-09-15-033001/threadcount.dump
  1. Start everything. migrate runs first and finds nothing to apply that the dump does not already have.
docker compose up -d
  1. Put the photos back and give them to the app's user.
docker compose cp /srv/backups/threadcount/2026-09-15-033001/photos app:/data/
docker compose exec -u root app chown -R threadcount:threadcount /data/photos

Restore onto the release the dump was taken from, or a later one. Migrations only go forwards.

Careful docker volume rm deletes the live data. Take a fresh backup first if the current data might still be needed.

Test a restore

A backup that has never been restored is not proven. Restore into a second, separate stack on the same server, under another project name and port, so the live instance is untouched:

cd /srv/threadcount
APP_PORT=3100 docker compose -p tcrestore up -d db
docker compose -p tcrestore exec -T db pg_restore -U threadcount -d threadcount --clean --if-exists < /srv/backups/threadcount/2026-09-15-033001/threadcount.dump
APP_PORT=3100 docker compose -p tcrestore up -d --build
curl -fsS http://127.0.0.1:3100/api/health

Then check what came back:

  • Sign in. Forward the port with ssh -L 3100:127.0.0.1:3100 and open http://localhost:3100 on your own machine.
  • Compare the counts. The line at the top of Settings Data & audit log gives active staff, garments, issues and orders. Check it against the live instance.

When you are done, remove the test stack and its volumes:

docker compose -p tcrestore down -v