Skip to content

Backup and restore

kata export writes the host-local database as JSONL. It is an offline storage operation, not a remote-daemon API: KATA_SERVER, a remote workspace target, or --daemon makes it fail before opening any local database. Run exports on the daemon host with that daemon's storage configuration. kata import can rebuild a database from that file or add one scoped project snapshot to an existing database. Use these commands for backups, selective project restores, machine moves, and schema cutovers.

Use JSONL exports

Do not copy ~/.kata/kata.db while the daemon is running. kata uses SQLite WAL mode, so recent writes can live in kata.db-wal; a plain file copy can look successful while missing recent data.

The kata.db.bak.* files created by schema cutover are temporary rollback files, not scheduled backups.

Full backup

For an offline backup:

kata daemon stop
kata export --output backups/kata-$(date -u +%Y%m%d).jsonl
kata daemon start

Without --output, kata writes a timestamped file in the current directory.

For an online backup on the same host:

kata export --allow-running-daemon --output backups/kata-$(date -u +%Y%m%d).jsonl

Restore

Restore into a fresh SQLite database file:

kata import --input backups/kata-20260531.jsonl --target ~/.kata/restored.db

The target must not exist unless --force is set. To use the restored database, stop the daemon, point KATA_DSN or KATA_DB at the restored file, or move it into KATA_HOME as kata.db, then restart.

Without --merge, kata import creates a target database from the input snapshot. It does not add records to an existing database.

For Postgres, pass a DSN as the target:

kata import --input backups/kata-20260531.jsonl \
  --target 'postgres://kata_schema_owner@db.example/kata?sslmode=verify-full&sslrootcert=system'

A missing kata schema is installed before the snapshot is replayed. An initialized target is refused unless --force is set; forced replay replaces all kata-owned state atomically and retains unrelated schemas in the database. In a split-role deployment, --target must use the schema-owner credential: fresh restore may create schema objects, and forced restore requires table replacement privileges that the serving role intentionally lacks. Import overrides an ambient mode = "validate" only for this explicit offline schema-owner operation; it does not expand the runtime role's grants. Restore the runtime DSN and validation mode before restarting service. Stop every daemon using that database and schema before restore. Each serving daemon holds a database advisory lease for its lifetime, and replay requires the exclusive counterpart, so a daemon on another host cannot retain a pre-restore identity while replacement is in progress. Postgres credentials are redacted from command output and errors.

For a shared production database, also take a database-native snapshot before schema upgrades. JSONL is the portable logical backup; a managed snapshot or pg_dump archive is the exact-version rollback artifact. The split-role upgrade and restore ordering is documented in PostgreSQL operations.

Versioned backups

JSONL is plain text and diffs cleanly. A simple local backup workflow is:

mkdir -p ~/kata-backups
cd ~/kata-backups
git init -q
kata daemon stop
kata export --output snapshot.jsonl
kata daemon start
git add snapshot.jsonl
git commit -q -m "snapshot $(date -u +%FT%TZ)"

Run that with cron, launchd, or a systemd timer. Push the repository to a private remote for off-host storage.

Single-project export

Use --project or --project-id to scope an export:

kata daemon stop
kata --project example-project export --output backups/example-project.jsonl
kata daemon start

Round-trip into a fresh database:

kata import --input backups/example-project.jsonl \
  --target /tmp/example-project-only.db

This is useful for archiving one project, handing history to a collaborator who will set up a fresh kata install, or moving one project to another host.

Links may span projects, and a scoped export only contains the named project's issues. A fresh restore or project merge skips links whose peer is outside the snapshot, along with import-mapping records that reference those links, and prints an aggregate note: skipped N link record(s)… to stderr. A merge does not connect an imported issue to an existing project's issue, even when that peer is already in the target. This keeps the scoped import from changing another project's dependency graph. A full-database snapshot preserves links when it contains both endpoints.

Merge a single-project snapshot

Use --merge to add a scoped snapshot to an existing SQLite or Postgres database without replacing its other projects:

kata daemon stop
kata import --merge --input backups/example-project.jsonl \
  --target ~/.kata/kata.db
kata daemon start

The target must already exist, and every daemon using it must be stopped. For Postgres, pass the initialized database's schema-owner DSN as --target. --force and --new-instance cannot be combined with --merge.

The merge must contain exactly one non-system project. It runs in one transaction, allocates new numeric database IDs, and preserves the project's UIDs, issue short IDs, and event identity. Existing projects remain unchanged. If another project already has the imported name, kata assigns the next available suffix, such as example-project-2.

The import is refused without mutation if an imported project or object UID already exists. As a result, re-importing the same snapshot is not an incremental refresh. Run one merge per scoped snapshot when restoring several projects; use a full-database export when cross-project links must also be restored.

Imported issue-sync bindings remain disabled until re-enabled locally. Imported federation state is discarded so the project must join federation again with credentials for the target environment.

Beads import

kata import --source-format beads migrates issues from Beads. It does not read a file or build a separate database: it drives the bd CLI and merges issues into the current kata project. See Migrating from Beads for prerequisites and the field mapping.