Backup & Restore
Chatto provides built-in CLI commands for backing up and restoring all instance data. Backups capture the full state of your NATS JetStream storage — users, spaces, rooms, messages, memberships, assets, and more — into a single .tar.gz archive. Backups can optionally be encrypted with a passphrase using age encryption.
What’s included
Section titled “What’s included”Backups contain all persistent JetStream streams, KV buckets, and object stores:
- Instance data — users, spaces, memberships, roles, permissions, notifications
- Per-space data — rooms, messages, threads, reactions, message bodies
- Assets — user avatars, space icons, message attachments
What’s excluded
Section titled “What’s excluded”Certain streams are intentionally excluded:
| Stream | Reason |
|---|---|
| Encryption keys | Security: keeps backup data encrypted at rest. Back up keys separately. |
| User presence | Ephemeral (memory-only), reconstructed at runtime |
| Call state | Ephemeral (memory-only), reconstructed by LiveKit webhooks |
| Link preview cache | Regeneratable on demand |
| Asset cache | Regeneratable (resized image variants) |
| Auth tokens | Security: prevents session token leakage via backup files |
Creating a backup
Section titled “Creating a backup”Run chatto backup while your Chatto instance is running. The command connects to NATS via the client configuration in chatto.toml.
# Default: saves to backups/<timestamp>.tar.gzchatto backup -c chatto.toml
# Custom output pathchatto backup -c chatto.toml -o /mnt/backups/chatto-daily.tar.gzEncrypted backups
Section titled “Encrypted backups”Use --encrypt to protect the backup archive with a passphrase. You’ll be prompted to enter and confirm the passphrase:
chatto backup -c chatto.toml --encryptEncrypted backups use .tar.gz.age as the file extension. The encryption format is age — a modern, audited file encryption tool. This means you can also inspect or decrypt your backups using the standalone age CLI if needed (see Using the age CLI below).
For scripted or automated backups, pass the passphrase via flag or pipe it from stdin:
chatto backup -c chatto.toml --encrypt --passphrase "$BACKUP_PASSPHRASE"
# Or pipe from a secrets managervault kv get -field=passphrase secret/chatto | chatto backup -c chatto.toml --encryptRestoring a backup
Section titled “Restoring a backup”-
Stop the Chatto server
Ensure no other process is accessing the NATS data directory.
-
Run the restore command
Terminal window chatto restore backup.tar.gz -c chatto.tomlFor encrypted backups, encryption is auto-detected — you’ll be prompted for the passphrase:
Terminal window chatto restore backup.tar.gz.age -c chatto.tomlYou can also pass the passphrase via flag:
Terminal window chatto restore backup.tar.gz.age -c chatto.toml --passphrase "$BACKUP_PASSPHRASE" -
Start Chatto normally
Terminal window chatto start -c chatto.toml
Conflict handling
Section titled “Conflict handling”By default, restore fails if any stream already exists. Use --conflict to change this:
| Flag | Behavior | Use case |
|---|---|---|
--conflict=error | Fail if any stream exists (default) | Fresh instance restore |
--conflict=skip | Skip existing streams | Partial restore / merge |
--conflict=overwrite | Delete and recreate existing streams | Full restore over existing data |
# Restore missing streams only, keep existing datachatto restore backup.tar.gz -c chatto.toml --conflict=skip
# Full overwrite (destructive!)chatto restore backup.tar.gz -c chatto.toml --conflict=overwriteEncryption key backup
Section titled “Encryption key backup”Encryption keys are excluded from data backups by design — since Chatto always encrypts message bodies, this ensures that a leaked backup archive doesn’t expose message content. To fully restore messages, you need to export the keys separately.
Exporting keys
Section titled “Exporting keys”chatto keys export -c chatto.toml -o keys.backupYou’ll be prompted for a passphrase. The export file is encrypted using age, so it’s safe to store — but use a strong passphrase.
For scripted/automated backups, pass the passphrase via flag or pipe it from stdin:
chatto keys export -c chatto.toml -o keys.backup --passphrase "$KEY_BACKUP_PASSPHRASE"
# Or pipe from a secrets managervault kv get -field=passphrase secret/chatto | chatto keys export -c chatto.toml -o keys.backupImporting keys
Section titled “Importing keys”After restoring a data backup, import the encryption keys:
chatto keys import keys.backup -c chatto.tomlExisting keys are never overwritten — only keys for users that don’t already have one are imported. This makes it safe to re-run.
Complete disaster recovery
Section titled “Complete disaster recovery”For a full restore with encrypted message recovery:
-
Restore the data backup
Terminal window chatto restore backup.tar.gz.age -c chatto.toml -
Import the encryption keys
Terminal window chatto keys import keys.backup -c chatto.toml -
Start the server
Terminal window chatto start -c chatto.toml
Automated backup script
Section titled “Automated backup script”Here’s a complete example backup script suitable for a cron job:
#!/bin/bashset -euo pipefail
CONFIG="/etc/chatto/chatto.toml"BACKUP_DIR="/mnt/backups/chatto"PASSPHRASE_FILE="/etc/chatto/backup-passphrase" # chmod 600RETENTION_DAYS=14
# Read passphrase from filePASSPHRASE=$(cat "$PASSPHRASE_FILE")
# Create timestamped backupTIMESTAMP=$(date -u +%Y-%m-%dT%H-%M-%SZ)BACKUP_FILE="$BACKUP_DIR/$TIMESTAMP.tar.gz.age"
chatto backup -c "$CONFIG" --encrypt --passphrase "$PASSPHRASE" -o "$BACKUP_FILE"chatto keys export -c "$CONFIG" -o "$BACKUP_DIR/$TIMESTAMP-keys.age" --passphrase "$PASSPHRASE"
# Remove backups older than retention periodfind "$BACKUP_DIR" -name "*.age" -mtime +$RETENTION_DAYS -delete
echo "Backup complete: $BACKUP_FILE"Using the age CLI
Section titled “Using the age CLI”All Chatto encrypted files (backups and key exports) use the standard age format. This means you can work with them using the age CLI tool, which is useful for verification, key rotation, or emergency access.
Installing age
Section titled “Installing age”# macOSbrew install age
# Debian/Ubuntuapt install age
# From sourcego install filippo.io/age/cmd/...@latestDecrypting a backup manually
Section titled “Decrypting a backup manually”If you need to access a backup without chatto restore — for example, to inspect the manifest or extract specific files:
# Decrypt the archive (prompts for passphrase)age -d backup.tar.gz.age > backup.tar.gz
# Then extract normallytar xzf backup.tar.gz
# Inspect the manifestcat backup/manifest.json | jq .Decrypting a key export manually
Section titled “Decrypting a key export manually”# Decrypt the key export (prompts for passphrase)age -d keys.backup > keys.json
# View the exported keys (careful — contains sensitive data!)cat keys.json | jq .Re-encrypting with a different passphrase
Section titled “Re-encrypting with a different passphrase”# Decrypt with the old passphraseage -d backup.tar.gz.age > backup.tar.gz
# Re-encrypt with a new passphraseage -p backup.tar.gz > backup-new.tar.gz.age
# Clean up the unencrypted filerm backup.tar.gzBackup strategy recommendations
Section titled “Backup strategy recommendations”Frequency: Schedule backups based on your tolerance for data loss. For active instances, daily backups are a reasonable starting point.
Retention: Keep multiple backup archives. A common pattern is daily backups retained for 14 days, plus weekly backups retained for 8 weeks.
Storage: Store backups on a different volume or remote storage (S3, GCS) from the NATS data directory. A backup on the same disk as the data doesn’t protect against disk failure.
Encryption: Always use --encrypt for backups stored on shared or remote storage. Even though message bodies are already encrypted, backups contain sensitive metadata (user info, space structure, membership lists).
Key separation: Store key exports separately from data backups when possible. The two files together unlock everything — keeping them in different locations adds defense in depth.
Testing: Periodically test restores to a separate instance to verify your backups are valid and your passphrase works.