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.
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. The database command below replaces existing database objects, so confirm the destination before running it.
For a backup.sh archive, extract it and enter its timestamped directory. Stop the application, leave Postgres running, then restore the dump:
docker compose -f /opt/tindra/docker-compose.yml stop tindra
docker compose -f /opt/tindra/docker-compose.yml exec -T postgres \
pg_restore -U tindra -d tindra --clean --if-exists < postgres.dump
Restore the extracted data/ contents to the application's /data mount using your volume tooling, or copy them into an existing stopped application container:
docker compose -f /opt/tindra/docker-compose.yml cp data/. tindra:/data
For a fresh stack, create the application container and its mount without starting it before copying. Ensure the restored directory is writable by the runtime UID/GID 65532:65532; copied files can otherwise retain unsuitable ownership. For host bind mounts, set ownership on the actual host directory. For named volumes, use volume-management tooling that can set ownership inside that volume.
For the manual bind-mount backup above, restore tindra-db.dump with the same pg_restore command and extract the file archive into the actual host data directory:
sudo tar -xzf tindra-data.tar.gz -C /path/to/DATA_DIR
sudo chown -R 65532:65532 /path/to/DATA_DIR
Use the image version matching the backup, then start the application and inspect startup logs:
docker compose -f /opt/tindra/docker-compose.yml start tindra
docker compose -f /opt/tindra/docker-compose.yml logs --tail=100 tindra
Verify sign-in, project data, a known source-mapped event, and fresh ingestion. Starting a newer image can apply migrations to the restored database. Retention also runs at startup and can remove data outside the configured limits, so check those settings before starting a recovery instance.
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.