Skip to content

Backups ​

TinyCMS provides operator commands that create validated directory bundles. There are no backup or restore HTTP endpoints. A schema export or revision history is not a full deployment backup.

What a backup contains ​

IncludedNotes
Whole SQLite databaseAccounts, sessions, content definitions, revisions, live pointers, languages, permissions, options, CSS, API-key records, audit events, schedules, releases, webhook data, projections, and migration history when present
Media originalsImages, documents, video, and audio, including trashed and unreferenced originals
Profile imagesStored files that cannot be reconstructed from database rows
Content-rebuild exportsExisting files under paths.contentBackups
Staging and quarantine evidenceFor inspection only; do not restore into active upload-processing directories
Manifest and checksumsVersion fingerprints, database inventory, directories, file lengths, SHA-256 values, and warnings

Image caches are regenerable and excluded. Environment files, deployment JSON, application builds, and external secrets are excluded. Preserve the matching release, original BETTER_AUTH_SECRET, webhook encryption key, email credentials, and client-side API-key plaintext separately in a secure recovery store.

The database snapshot uses SQLite VACUUM INTO, including committed WAL data. Do not substitute a raw copy of a live database file.

Prepare backup storage ​

Set backup.directory in the complete deployment JSON. The production example uses /var/backups/tinycms, owned by tinycms with mode 0700. There is no implicit backup directory when the setting is absent.

Keep it outside public assets and outside database, media, cache, staging, quarantine, and configuration paths. Storage roots must be distinct and non-overlapping. Symlinks, hardlinks, and special files in source storage or bundles are rejected. Ensure enough free space for the new bundle and validation scratch copies.

Use the prebuilt backend from the exact release matching the deployment being backed up. Preserve its resources and backup compatibility metadata with the release.

Stop all writers ​

  1. Suspend public writes and automated restarts/deployment tooling.
  2. Stop the service, external cron, migrations, and every other writer sharing these paths.
  3. Wait for graceful process exit.
  4. Keep writers stopped until the complete backup command finishes.

For the example systemd deployment:

sh
sudo systemctl stop tinycms
sudo systemctl is-active tinycms

is-active should report inactive and exits nonzero in that state. Confirm the process really exited before continuing. A database transaction cannot freeze media files, so stopping the application is required for the entire backup.

Create and validate ​

For an extracted release with the correct backend .env, run from the release root:

sh
pnpm --filter backend run backup:create -- --stopped
pnpm --filter backend run backup:validate -- --bundle /absolute/path/to/tinycms-EXACT-NAME

Use the exact bundle path returned by creation. --stopped acknowledges that external writers and restart mechanisms are stopped; it does not stop them for you.

For the production layout, explicitly load the protected environment. Run the following from /srv/tinycms/app/apps/backend using the actual Node executable path:

sh
sudo -u tinycms /usr/bin/node --env-file=/etc/tinycms/backend.env dist/backup/cli.js create --stopped
sudo -u tinycms /usr/bin/node --env-file=/etc/tinycms/backend.env dist/backup/cli.js validate --bundle /var/backups/tinycms/tinycms-EXACT-NAME

These invoke the same CLI as the workspace scripts. Creation reports status: complete only after copy, checksum, database, and reference checks succeed. Validation reports status: validated. All errors exit nonzero. Read warnings as well as status.

After success, or after resolving a failed operation, restart through the normal service manager and verify health. Downtime includes shutdown, database snapshot, file copying, validation, and restart. Measure it with representative data.

Keep an independent copy ​

Copy the complete finalized bundle to independently managed storage using your approved transfer/encryption tooling. Preserve private permissions and every file, then validate the transferred copy with the matching application build. Keep access restricted: bundles include credentials-related database records and unpublished content.

Record bundle identity, release/build identity, time, location, warnings, and validation outcome. Choose frequency and retention based on how much content loss and recovery time the organization can tolerate. The application does not automatically schedule backups or copy them off-host.

Retention ​

Retention is a guarded manual command. Stop writers and restarts, review a dry-run, then explicitly apply. For an extracted release using .env:

sh
pnpm --filter backend run backup:prune -- --keep 7
pnpm --filter backend run backup:prune -- --keep 7 --apply

For the production layout, from the backend workspace:

sh
sudo -u tinycms /usr/bin/node --env-file=/etc/tinycms/backend.env dist/backup/cli.js prune --keep 7
sudo -u tinycms /usr/bin/node --env-file=/etc/tinycms/backend.env dist/backup/cli.js prune --keep 7 --apply

Seven is an example policy. The command validates all completed candidates before deleting anything, retains the newest successful bundle, rejects nonpositive retention, and aborts on incompatible or invalid candidates. Use a matching release to manage older bundles. Confirm an independent copy before reducing retention; never replace pruning with wildcard deletion.

Locks and interrupted operations ​

The server, backup, and pruning share an exclusive <database-path>.tinycms-lock directory. External database clients, old builds, and direct filesystem writes do not honor it and must be stopped separately.

A crash can leave a lock or .partial-* directories. Read the lock's owner.json, verify the owner and every other writer are stopped, and suspend restart mechanisms before removing only that exact stale lock. PID absence alone is insufficient. Partial bundles are not recovery inputs; inspect explicitly identified leftovers before cleanup.

Finish by performing an isolated recovery drill. A completed backup is useful only when its recovery procedure is understood and tested.