Backup and Recovery#
Trilium automatically creates SQLite database snapshots on a daily, weekly, and monthly schedule. Each backup is a complete copy of document.db — including all note content, metadata, and the encrypted data key needed to decrypt protected notes. Backups are overwrite-based: each schedule type maintains a single file that is replaced on the next scheduled run.
Backup Files#
| File | Schedule |
|---|---|
backup-daily.db | Every 24 hours |
backup-weekly.db | Every 7 days |
backup-monthly.db | Every 30 days |
backup-now.db | Manual trigger |
Backup files live in the backup/ subdirectory of the Trilium data directory. By default this is {TRILIUM_DATA_DIR}/backup/, but it can be overridden with the TRILIUM_BACKUP_DIR environment variable . The directory is created with 0o700 permissions on first use .
Files are identified by the pattern backup-*.db; in-progress SQLite journal files (e.g., *.db-journal) are explicitly excluded from the listing .
How Backups Are Created#
The server-side implementation is ServerBackupService which extends the abstract BackupService from @triliumnext/core.
-
Schedule: The scheduler calls
regularBackup()every 4 hours viasetInterval, with a first run 5 minutes after startup .regularBackup()internally checks elapsed time againstlastDailyBackupDate,lastWeeklyBackupDate, andlastMonthlyBackupDateoptions to decide which backup types are due . -
Concurrency safety: Each backup runs inside a sync mutex (
syncMutexService.doExclusively) to avoid capturing an inconsistent DB state mid-sync . -
Mechanism:
sql.copyDatabase(backupFile)performs the actual copy. This uses SQLite's online backup API viabetter-sqlite3, so the copy is safe to take on a live, running database. -
Manual backups: The
backupNow(name)method createsbackup-{name}.dbon demand. The name is sanitized to[a-zA-Z0-9_-]to prevent path traversal . -
Per-schedule enables: Each type is independently controllable via
dailyBackupEnabled,weeklyBackupEnabled, andmonthlyBackupEnabledoptions .
Encryption Keys in Backups#
Backups are full database copies, so they include the encryptedDataKey, passwordVerificationHash, passwordVerificationSalt, and passwordDerivedKeySalt options. This means:
- A backup made before a password reset retains the old encrypted data key. Restoring that backup and using the original password will recover access to protected notes .
- Conversely, if a password is reset after a backup was taken, the new key in the live database differs from the key in old backups — you must use the password active at backup time to decrypt .
The anonymization tool (anonymization.ts) scrubs these fields when creating a sanitized copy for bug reports, confirming they are present in standard backups.
Migration Backups#
Before any schema migration, Trilium creates a timestamped backup (before-migration-{dateSuffix}) rather than using the static backup-{type}.db names, ensuring migration backups are never silently overwritten by a scheduled run .
Recovery Procedure#
To restore from a backup:
- Stop Trilium completely.
- Copy your chosen backup file (e.g.,
backup-daily.db) from{TRILIUM_DATA_DIR}/backup/to{TRILIUM_DATA_DIR}/document.db, replacing the existing database. - Restart Trilium and log in using the password that was active when that backup was created .
Caution: Restoration reverts the entire database to the backup's point in time. Export any unprotected notes you want to keep before overwriting .
Backups can also be downloaded directly from the UI via Options → Backup which calls GET /api/database/backup/download?filePath=... . Path traversal is prevented server-side by validating the resolved path stays within BACKUP_DIR .
Key Source Files#
| File | Purpose |
|---|---|
apps/server/src/backup_provider.ts | Server BackupService implementation (schedule, copy, list) |
apps/server/src/services/data_dir.ts | Resolves BACKUP_DIR and other data paths |
apps/server/src/services/anonymization.ts | Anonymized DB copies (bug reports); strips sensitive backup fields |
packages/core/src/services/backup.ts | Abstract BackupService base class with regularBackup() logic |