Backup

Backup

A complete Tindra backup includes Postgres and the persistent data directory (DATA_DIR). Postgres holds telemetry and configuration; the data directory holds source maps and attachments. Keep the matching image tag or digest and deployment configuration with your recovery records.

backup.sh

The repository's backup.sh creates an archive containing postgres.dump, a data/ directory, and a restore manifest. Run it while both Compose services are running so it can copy the application container's /data directory:

curl -sSLO https://raw.githubusercontent.com/blendbyte/tindra/main/backup.sh
chmod +x backup.sh
./backup.sh /opt/tindra /backups

Create the output directory first. The arguments are the Compose directory and output directory; both default to the current directory. Use absolute paths for scheduled backups. A generated filename includes the date and time, for example tindra-backup-20260910-030000.tar.gz.

The script expects services named postgres and tindra, the tindra database/user, and container data at /data. Adapt it for custom deployments. If the application container is not running, the script warns and skips the data directory; that archive is not a complete backup.

The database dump and file copy happen sequentially. Avoid source-map uploads/deletions or other file changes during the backup if you need them to match exactly. In-memory ingestion queues are not included; data accepted but not yet committed is not in the database dump.

Manual backup

For a deployment with a host bind mount for DATA_DIR, stop the application gracefully so its stored files and database remain consistent during the backup, while leaving Postgres running:

docker compose stop tindra
docker compose exec -T postgres pg_dump -U tindra -Fc tindra > tindra-db.dump
sudo tar -czf tindra-data.tar.gz -C /path/to/DATA_DIR .
docker compose start tindra

Replace /path/to/DATA_DIR with the actual host mount. For a named Docker volume, use the script above or your volume-backup tooling; a named volume is not an arbitrary host directory. Keep the application's stop_grace_period: 75s so shutdown has time to drain pending work.

Debian and Ubuntu packages

The APT package keeps data in /var/lib/tindra and its configuration in /etc/tindra. Its page has backup and restore commands for the local database the installer creates.

Automated backups

For example, after creating /backups:

0 3 * * * /opt/tindra/backup.sh /opt/tindra /backups

Monitor the job's exit status and warnings, check that each archive contains both postgres.dump and data/, and periodically test a restore to an isolated instance. Apply a backup-retention policy appropriate to your recovery needs; telemetry retention does not manage backup archives.

Off-site replication

Copy completed archives to your backup destination, for example:

rclone copy /backups remote:tindra-backups --include 'tindra-backup-*.tar.gz'

Configure the remote destination beforehand. Database dumps and deployment configuration contain sensitive data and credentials; restrict access to both local and remote backups.

What is backed up

  • Errors, issues, transactions and spans, logs, profiles, and monitor history stored in Postgres.
  • Projects, user accounts, permissions, SSO links, end-user metadata, and setup receipts.
  • Tokens, alert rules, settings, and audit records.
  • Source maps and attachments in the persistent data directory.

The script does not include the Docker image or your Compose/environment files. Store the image version and configuration separately so a restore uses the correct database credentials and authentication policy. Already-expired telemetry and pending in-memory ingestion are not recoverable from a later backup.

Restoring from backup

Restore to an isolated stack first when possible. Step 2 drops the destination database, so make sure you're on the right host before you run it.

Every backup.sh archive lists these steps in its manifest.txt. Archives from older versions of the script suggest pg_restore --clean there. Use the steps below instead. The dump format hasn't changed, so they work for old archives too.

Extract the archive, point B at the extracted folder, then run everything else from the directory that holds docker-compose.yml:

tar -xzf tindra-backup-20260910-030000.tar.gz
B="$PWD/tindra-backup-20260910-030000"
cd /opt/tindra

1. Stop Tindra

Stop the application and make sure Postgres is up:

docker compose stop tindra
docker compose up -d postgres

2. Replace the database

docker compose exec -T postgres dropdb -U tindra --force --if-exists tindra
docker compose exec -T postgres createdb -U tindra tindra
docker compose exec -T postgres pg_restore -U tindra -d tindra --no-owner < "$B/postgres.dump"

Recreate the database rather than restoring with pg_restore --clean. --clean only drops objects that exist in the dump. Tables added by a newer Tindra version stay behind, and migrations break when Tindra starts on the restored schema. A fresh database lets Tindra migrate the backup forward cleanly.

3. Restore the data directory

docker compose up --no-start tindra
docker run --rm --volumes-from "$(docker compose ps -aq tindra)" -v "$B/data:/restore:ro" \
  alpine sh -c 'cp -a /restore/. /data/ && chown -R 65532:65532 /data'

This creates the application container without starting it, copies data/ into its /data mount, and hands ownership to the image's non-root user (UID/GID 65532:65532). It works for named volumes and bind mounts alike. If the archive has no data/ directory, the backup ran while Tindra was stopped. Skip this step and expect missing source maps and attachments.

4. Start Tindra

docker compose up -d
docker compose logs --tail=100 tindra

Use the image version from the backup, or a newer one. Tindra applies any pending migrations on start. Retention also runs at startup and can remove data outside the configured limits, so check those settings before starting a recovery instance.

Then verify sign-in, project data, a known source-mapped event, and fresh ingestion.

Manual bind-mount backups

For the manual backup above, follow the same steps with two changes. In step 2, restore tindra-db.dump instead of postgres.dump. In step 3, extract the file archive straight into the host data directory:

sudo tar -xzf tindra-data.tar.gz -C /path/to/DATA_DIR
sudo chown -R 65532:65532 /path/to/DATA_DIR

Managed instances

Managed Tindra instances are backed up by Blendbyte GmbH. Contact support for restore assistance; you do not need to run these self-hosting commands.

Monitor ingestion health

Use Ingestion Monitoring for authenticated Prometheus scrapes, queue and write metrics, and a recovery checklist when accepted data is not appearing. Pending in-memory data is not part of a database backup.