Back Up Tako Apps to S3-Compatible Storage

Back Up Tako Apps to S3-Compatible Storage

Tako-kun ·

The cute version of persistent app storage is “SQLite and uploads on a $5 VPS.”

The grown-up version is “and I can restore it when the disk disappears.”

Tako already gives every app a persistent data directory for SQLite files, uploads, workflow state, transient channel replay logs, and other file-backed state. We covered that in Stateful Apps on Tako: deploys swap releases, but the data directory stays put.

Backups are the other half of that story. Add one private S3-compatible storage resource to tako.toml, give Tako encrypted credentials, and tako-server can archive app data after deploys, on a regular cadence, and on demand.

No cron script. No rsync folder on the same machine. No “I thought the VPS provider snapshots were enabled.”

What gets backed up

When backups are enabled, Tako backs up durable per-app state for that app and environment. That includes app-owned files and Tako-owned workflow state:

Path in the backupWhat it contains
app/Your app data exposed through TAKO_DATA_DIR and SDK helpers
tako/workflows.sqliteTako-owned durable workflow queue and run state

That matters because a modern app is rarely just one database file. A small project may have:

DataCommon location
SQLite databasetako.dataDir/app.db
Uploaded avatarstako.dataDir/uploads/
Queue or job stateTako workflow storage

Tako intentionally does not back up tako/channels.sqlite. Channel replay is a bounded reconnect buffer, not the app’s permanent event history. After restore, channel replay starts empty; apps that need canonical chat history, notifications, or audit events should keep that data in their own database and publish channels from it.

The archive format is designed for app data, not just raw file copying. SQLite files are snapshotted with SQLite’s online VACUUM INTO mechanism before archiving, so Tako does not need to include -wal and -shm companion files separately. The result is compressed as tar.zst, encrypted before upload with AES-256-GCM, and tracked with a SHA-256 manifest plus a remote JSON index.

The short version: Tako backs up the state your app needs to come back, and the object store receives encrypted archives.

The deployment docs show where backups fit in the deploy flow, and the tako.toml reference has the exact config shape.

Configure a private S3-compatible resource

Backups are opt-in per environment:

[envs.production]
route = "app.example.com"
servers = ["prod-a"]
backup = { storage = "r2_backups" }

[storages.r2_backups]
provider = "s3"
bucket = "app-backups"
endpoint = "https://<account>.r2.cloudflarestorage.com"
region = "auto"

The storage resource is normal Tako storage metadata: bucket, endpoint, region, and provider. provider = "s3" means an S3-compatible API, so the endpoint can point at an S3-compatible service such as Cloudflare R2. R2 documents its storage access through an S3-compatible API, which is why the example uses region = "auto" and an R2 endpoint.

The backup target must be private:

RuleWhy
It must use provider = "s3"Backups upload to S3-compatible object storage
It cannot use localA backup on the same local disk is not much of a backup
It cannot set public_base_urlBackup objects are not public app assets

If you already use S3-compatible storage for public uploads, you can still share the same bucket. Keep the backup resource private. If your upload resource has public_base_url, declare a second private resource that points at the same bucket and use that for backup = { storage = "..." }. Tako writes backup objects under its reserved _tako/backups/{app}/{env}/{server}/ prefix, so normal app objects and backup archives do not collide.

Then add credentials:

tako storages credentials r2_backups --env production

That command sets or rotates encrypted S3 credentials for a declared top-level storage resource without exposing it to app code. Backup storage does not become tako.storages.r2_backups; it is server infrastructure, not an app binding.

If you want one resource to be both an app storage binding and a backup target, add it with the normal storage command and keep it private:

tako storages add uploads \
  --env production \
  --resource r2_private \
  --provider s3 \
  --bucket app-data \
  --endpoint https://<account>.r2.cloudflarestorage.com \
  --region auto

The CLI reference covers both tako storages add and tako storages credentials.

What happens during deploy

Backups join the deploy path early enough to catch configuration mistakes before you wait on a build.

During tako deploy, the CLI validates the selected environment, server mappings, app secrets, storage credentials, and backup storage credentials before build and upload work starts. Expired storage credentials fail the deploy. Credentials expiring within 30 days produce a warning.

When backups are enabled, tako deploy and tako backups now also create backup encryption keys for the environment if none exist. Those backup keys live in .tako/secrets.json, encrypted with the same environment key model used by Tako secrets. The latest backup key is used for new archives, and each backup manifest records the key id it needs so older backups can still be restored while that key remains available.

Diagram

After a successful rolling update, the server creates a post-deploy app data backup. If that post-deploy backup fails, the failure is reported in the finalize response and logs, but Tako does not roll back an otherwise successful deploy. Your new release can still be serving traffic while you investigate the backup warning.

After that, tako-server runs due backups roughly every 24 hours while it is running. Retention defaults to 30 days.

Manual commands use the same signed HTTP management path as other app operations:

tako backups status --env production
tako backups now --env production
tako backups list --env production

status shows whether backups are enabled per mapped server, plus last and next backup timing. now creates an immediate backup. list reads the remote index and shows backup ids newest first.

Restore is the feature

A backup system is only useful if restore is boring.

Download an encrypted archive when you want a local copy:

tako backups download b123 \
  --env production \
  --server prod-a \
  --output ./backup.tar.zst.enc

Restore when you need the server back in place:

tako backups restore b123 \
  --env production \
  --server prod-a \
  --yes

If an environment runs on multiple servers, pass --server. Each server has its own app data tree, and Tako includes the server name in the backup prefix so one server’s archives do not overwrite another’s.

During restore, Tako stops the selected server’s app, replaces its data tree with the archive contents, clears transient channel replay storage, reconciles workflows, and restarts according to the app’s desired instance count. That is intentionally direct. Restoring production data should feel like a command you can understand while your pulse is not especially calm.

The operational habit is simple:

TaskCommand or config
Enable backupsbackup = { storage = "r2_backups" }
Set backup credentialstako storages credentials r2_backups --env production
Verify statetako backups status --env production
Take one nowtako backups now --env production
Restore onetako backups restore <id> --env production --server <server>

Keep the small-server story honest

Tako makes small servers useful: persistent app data, local SQLite, uploads, workflows, channels, routing, TLS, deploys, and scale-to-zero all live close to your app. But “small” should not mean “fragile.”

Backups to S3-compatible storage keep the one-machine workflow practical without pretending the machine is immortal. Your app keeps using local files. Tako handles the archive, encryption, object key prefix, deploy hook, daily cadence, and restore command.

Start with the tako.toml backup reference, keep the CLI backup commands nearby, and read what happens during tako deploy if you want the full deploy pipeline around it.