Backups and Restore
HitKeep keeps its operational story simple, but there is an important distinction in 2.0.0:
- single-tenant installs can still feel like “one database”
- multiteam installs store analytics in multiple DuckDB files under one
data-path
That means your backup strategy must follow the real storage boundary, not an older “copy one hitkeep.db file” assumption.
The Backup Boundary
Section titled “The Backup Boundary”Single-tenant or default-tenant only
Section titled “Single-tenant or default-tenant only”If you only use the default tenant, your live data is primarily:
- the shared control-plane database at
{data-path}/hitkeep.db - your archive directory if retention archiving is enabled
Multiteam installs
Section titled “Multiteam installs”Once you use non-default teams, the live data footprint becomes:
- the shared control-plane database at
{data-path}/hitkeep.db - one analytics database per non-default team at
{data-path}/tenants/{team_id}/hitkeep.db - your archive directory if retention archiving is enabled
That is why the safe rule is:
In multiteam installs, back up the whole
data-pathtree, not onlyhitkeep.db.
Built-in Backups
Section titled “Built-in Backups”HitKeep can export live databases automatically using DuckDB’s EXPORT DATABASE.
export HITKEEP_DATA_PATH=/var/lib/hitkeep/dataexport HITKEEP_BACKUP_PATH=/var/lib/hitkeep/backupsexport HITKEEP_BACKUP_INTERVAL=60export HITKEEP_BACKUP_RETENTION=24
./hitkeepBuilt-in backups include:
- the shared database snapshot under
shared/{timestamp}/ - each non-default tenant snapshot under
tenants/{team_id}/{timestamp}/
Use this when you want consistent application-level snapshots without relying on filesystem-level tooling.
Restore Semantics
Section titled “Restore Semantics”Restore is an offline operation:
./hitkeep recover restore-backup \ -from /var/lib/hitkeep/backups \ -snapshot 2026-03-08T120000Z \ -db /var/lib/hitkeep/data/hitkeep.db \ -data-path /var/lib/hitkeep/data \ -yesFor S3-backed snapshots:
./hitkeep recover restore-backup \ -from s3://my-bucket/hitkeep/backups \ -snapshot 2026-03-08T120000Z \ -yesThe restore flow in 2.0.0 is intentionally WAL-safe:
- Existing database files are moved aside as
.pre-restore.{timestamp}safety copies. - HitKeep imports the snapshot into a temporary DuckDB file.
- It checkpoints and closes that temporary database.
- It refuses to promote the restore if the temporary database still has a
.wal. - Only then is the restored database renamed into place.
That means hitkeep recover restore-backup itself should not leave your restored database dependent on replaying a leftover WAL.
Local vs External Backup Tooling
Section titled “Local vs External Backup Tooling”Built-in backups are the easiest supported option, but external tooling is still valid.
Recommended external strategy
Section titled “Recommended external strategy”# Copy the full live data treersync -az /var/lib/hitkeep/data/ backup-host:/backups/hitkeep/data/
# Copy retention archives if you use themrsync -az /var/lib/hitkeep/archive/ backup-host:/backups/hitkeep/archive/Or with object storage:
rclone sync /var/lib/hitkeep/data/ remote:my-bucket/hitkeep/data/rclone sync /var/lib/hitkeep/archive/ remote:my-bucket/hitkeep/archive/For multiteam installs, do not back up only:
cp /var/lib/hitkeep/data/hitkeep.db /backups/That copies only the shared control plane and misses tenant-local analytics databases.
After a Restore
Section titled “After a Restore”After recover restore-backup finishes:
- Start HitKeep normally.
- Confirm login works.
- Open the dashboard for a site in the default tenant.
- Open at least one site from a non-default team if you use teams.
- Check that goals, funnels, ecommerce, and team-specific analytics still render.
The next normal startup may create a new, valid DuckDB .wal during runtime. That is expected. The thing to avoid is a restore that only works if an old or partial WAL is replayed.