{"slug":"back-up-tako-apps-to-s3-compatible-storage","url":"https://tako.sh/blog/back-up-tako-apps-to-s3-compatible-storage/","canonical":"https://tako.sh/blog/back-up-tako-apps-to-s3-compatible-storage/","title":"Back Up Tako Apps to S3-Compatible Storage","date":"2026-05-24T14:34","description":"Configure Tako backups with private S3-compatible storage, encrypted archives, manual restores, and deploy-time snapshots for app data.","author":null,"image":"f7c38670efaf","imageAlt":null,"headings":[{"depth":2,"slug":"what-gets-backed-up","text":"What gets backed up"},{"depth":2,"slug":"configure-a-private-s3-compatible-resource","text":"Configure a private S3-compatible resource"},{"depth":2,"slug":"what-happens-during-deploy","text":"What happens during deploy"},{"depth":2,"slug":"restore-is-the-feature","text":"Restore is the feature"},{"depth":2,"slug":"keep-the-small-server-story-honest","text":"Keep the small-server story honest"}],"markdown":"The cute version of persistent app storage is \"SQLite and uploads on a $5 VPS.\"\n\nThe grown-up version is \"and I can restore it when the disk disappears.\"\n\nTako 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](/blog/stateful-apps-sqlite-uploads-tako-data-dir/): deploys swap releases, but the data directory stays put.\n\nBackups 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.\n\nNo cron script. No rsync folder on the same machine. No \"I thought the VPS provider snapshots were enabled.\"\n\n## What gets backed up\n\nWhen 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:\n\n| Path in the backup      | What it contains                                              |\n| ----------------------- | ------------------------------------------------------------- |\n| `app/`                  | Your app data exposed through `TAKO_DATA_DIR` and SDK helpers |\n| `tako/workflows.sqlite` | Tako-owned durable workflow queue and run state               |\n\nThat matters because a modern app is rarely just one database file. A small project may have:\n\n| Data               | Common location         |\n| ------------------ | ----------------------- |\n| SQLite database    | `tako.dataDir/app.db`   |\n| Uploaded avatars   | `tako.dataDir/uploads/` |\n| Queue or job state | Tako workflow storage   |\n\nTako 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.\n\nThe 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.\n\nThe short version: Tako backs up the state your app needs to come back, and the object store receives encrypted archives.\n\nThe [deployment docs](/docs/deployment/) show where backups fit in the deploy flow, and the [`tako.toml` reference](/docs/tako-toml/) has the exact config shape.\n\n## Configure a private S3-compatible resource\n\nBackups are opt-in per environment:\n\n```toml\n[envs.production]\nroute = \"app.example.com\"\nservers = [\"prod-a\"]\nbackup = { storage = \"r2_backups\" }\n\n[storages.r2_backups]\nprovider = \"s3\"\nbucket = \"app-backups\"\nendpoint = \"https://<account>.r2.cloudflarestorage.com\"\nregion = \"auto\"\n```\n\nThe 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](https://developers.cloudflare.com/r2/api/s3/api/), which is why the example uses `region = \"auto\"` and an R2 endpoint.\n\nThe backup target must be private:\n\n| Rule                            | Why                                                     |\n| ------------------------------- | ------------------------------------------------------- |\n| It must use `provider = \"s3\"`   | Backups upload to S3-compatible object storage          |\n| It cannot use `local`           | A backup on the same local disk is not much of a backup |\n| It cannot set `public_base_url` | Backup objects are not public app assets                |\n\nIf 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.\n\nThen add credentials:\n\n```bash\ntako storages credentials r2_backups --env production\n```\n\nThat 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.\n\nIf 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:\n\n```bash\ntako storages add uploads \\\n  --env production \\\n  --resource r2_private \\\n  --provider s3 \\\n  --bucket app-data \\\n  --endpoint https://<account>.r2.cloudflarestorage.com \\\n  --region auto\n```\n\nThe [CLI reference](/docs/cli/) covers both `tako storages add` and `tako storages credentials`.\n\n## What happens during deploy\n\nBackups join the deploy path early enough to catch configuration mistakes before you wait on a build.\n\nDuring `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.\n\nWhen 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.\n\n```d2\ndirection: right\n\ndeploy: \"tako deploy\" {\n  style.fill: \"#FFF9F4\"\n}\n\nvalidate: \"validate backup config\\nand credentials\" {\n  style.fill: \"#9BC4B6\"\n}\n\nroll: \"rolling update\" {\n  style.fill: \"#FFF9F4\"\n}\n\nsnapshot: \"snapshot app data\" {\n  style.fill: \"#E88783\"\n}\n\narchive: \"tar.zst + AES-256-GCM\" {\n  style.fill: \"#9BC4B6\"\n}\n\ns3: \"private S3-compatible bucket\" {\n  shape: cylinder\n  style.fill: \"#FFF9F4\"\n}\n\ndeploy -> validate -> roll -> snapshot -> archive -> s3\n```\n\nAfter 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.\n\nAfter that, `tako-server` runs due backups roughly every 24 hours while it is running. Retention defaults to 30 days.\n\nManual commands use the same signed HTTP management path as other app operations:\n\n```bash\ntako backups status --env production\ntako backups now --env production\ntako backups list --env production\n```\n\n`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.\n\n## Restore is the feature\n\nA backup system is only useful if restore is boring.\n\nDownload an encrypted archive when you want a local copy:\n\n```bash\ntako backups download b123 \\\n  --env production \\\n  --server prod-a \\\n  --output ./backup.tar.zst.enc\n```\n\nRestore when you need the server back in place:\n\n```bash\ntako backups restore b123 \\\n  --env production \\\n  --server prod-a \\\n  --yes\n```\n\nIf 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.\n\nDuring 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.\n\nThe operational habit is simple:\n\n| Task                   | Command or config                                              |\n| ---------------------- | -------------------------------------------------------------- |\n| Enable backups         | `backup = { storage = \"r2_backups\" }`                          |\n| Set backup credentials | `tako storages credentials r2_backups --env production`        |\n| Verify state           | `tako backups status --env production`                         |\n| Take one now           | `tako backups now --env production`                            |\n| Restore one            | `tako backups restore <id> --env production --server <server>` |\n\n## Keep the small-server story honest\n\nTako 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.\"\n\nBackups 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.\n\nStart with the [`tako.toml` backup reference](/docs/tako-toml/), keep the [CLI backup commands](/docs/cli/) nearby, and read [what happens during `tako deploy`](/blog/what-happens-when-you-run-tako-deploy/) if you want the full deploy pipeline around it."}