Skip to content

Backups and restore

This page covers three ways back from a mistake, and what each kind of data has behind it:

  • A deleted app or workspace can be restored for 30 days.
  • An app’s database can be put back as it was at a moment in the last 30 days, to within a minute.
  • Versions keep every deploy’s code, so a rollback puts earlier code live again.

Deleting an app takes it offline and revokes its bypass tokens and push tokens at once, as it always has. AI clients connected to it are disconnected, and a GitHub Actions run deploying it loses its credential. Everything else is kept for 30 days: its versions, its database, its files, its keys and queue, its secrets, its settings, its scheduled jobs, who it’s shared with and the agents it’s granted to.

Terminal window
jiayang app delete acme/hello --yes
Deleted acme/hello. You can restore it until 2026-11-03 14:05 UTC with `jiayang app restore acme/hello`.

While it’s deleted:

  • Nobody can open it. Its address and its preview links answer as if there were no app, and its scheduled jobs don’t run. Connections open when it was deleted close within a minute, and after that none of its code runs, so it uses no CPU time. A container app’s container stops within 10 minutes, once nothing has reached it.

  • It isn’t in jiayang app list or on the dashboard’s Apps page.

  • It doesn’t count toward your plan’s apps or storage, and isn’t billed.

  • Its slug is still its own, so no new app can take the name:

    error: acme/hello was deleted, and it can be restored until 3 November 2026. Restore it with `jiayang app restore acme/hello`, or pick another name.

See what you can restore:

Terminal window
jiayang app deleted acme
acme/hello deleted 2026-10-04 14:05 UTC by grace@example.com, restorable until 2026-11-03 14:05 UTC
Restore one with `jiayang app restore acme/<app>`.

Restore it:

Terminal window
jiayang app restore acme/hello
Restored acme/hello. It's at https://hello--acme.jiayang-apps.cloud again. Agents granted it have it back, and its scheduled jobs run when they're next due. Its bypass tokens stayed revoked: make new ones with `jiayang token create acme/hello`.

It comes back as it was, on the same address. Agents granted it can reach it again with the credentials they have, and its scheduled jobs run again, each when it’s next due: the runs missed while it was deleted aren’t made up. Its bypass tokens and push tokens stay revoked, a GitHub Actions run that was deploying it has to run again, and AI clients that were connected to it have to connect again. In the dashboard, deleted apps are under Recently deleted at the bottom of the Apps page.

Who can restore an app: the workspace’s owners and admins, and the app’s own owners. These are the same people who can delete it. A restore needs room on the plan, the same as making an app:

error: This workspace has 3 apps, and the Free plan allows 3. To restore hello, delete one or upgrade.

To delete an app’s data straight away, add --for-good:

Terminal window
jiayang app delete acme/hello --yes --for-good
Deleted acme/hello for good.

Its script, database and container come off Cloudflare at once, and so do its files, up to 5,000 of them. The rest go within two days, as deleting an app’s files says. It can’t be restored. The same command deletes an app already in Recently deleted for good, and in the dashboard it has a Delete for good button there. After 30 days we delete it for good ourselves. The audit log records each step: app.delete, then app.restore or app.purge.

Recently deleted holds as many apps as your plan allows: 3 on Free, 25 on Team, and 200 on Business and Enterprise. A deleted app keeps its data while it counts toward nothing, so once it’s full, deleting another app is refused until you delete one there for good:

error: Recently deleted in acme already holds 3 apps, as many as the Free plan allows, so hello can't wait there too. Delete it for good with `jiayang app delete acme/hello --yes --for-good`, or delete one of those for good first, the same way: `jiayang app deleted acme` lists them.

--for-good still works with it full, since the app doesn’t go there.

An owner deletes a workspace in the dashboard, under Settings, then Danger zone. Its apps go offline, every bypass token, agent credential, push token, GitHub Actions credential and SCIM token is revoked, AI clients connected to its apps are disconnected, members lose access, and a paid subscription is cancelled with a final invoice, as described in Plans and billing. Its apps and their data stay where they are for 30 days.

Every owner of the workspace gets an email saying who deleted it, until when it can be restored, and how. The owner who deleted it gets one too. Any of its owners can restore it in that time, from the page for making a new workspace in the dashboard, or with the CLI:

Terminal window
jiayang workspace deleted
acme deleted 2026-10-04 14:05 UTC, restorable until 2026-11-03 14:05 UTC
Restore one with `jiayang workspace restore <workspace>`.
Terminal window
jiayang workspace restore acme
Restored acme, on the Free plan. Its bypass tokens and agents' credentials stayed revoked: make new ones if you need them.

If the workspace required single sign-on, its owners sign in through its provider to restore it: choose Restore a deleted workspace on the sign-in page.

Every owner gets an email saying it’s back and who restored it. It comes back with its apps, data, members, groups, settings and audit log. Its tokens stay revoked, so GitHub Actions runs that were deploying have to run again, and AI clients have to connect again. An app whose deploy was cut off by the delete gets the version that was live before put back up, and answers 503 that it’s starting for a minute or so while that happens.

If it used SCIM, its SCIM token stays revoked too, and while the workspace was deleted your identity provider couldn’t tell us about anyone who left. So everyone it added, except the owner restoring it, is held: they’re still listed as members, marked as waiting for your identity provider, but they can’t open the workspace or its apps, and their GitHub Actions setups are removed, until your provider confirms them. The webhooks, audit streams and scheduled jobs they added stay on but wait: nothing goes out, a test included, and no job runs. Once your provider confirms them, the deliveries that waited go out, and each job runs when it’s next due. Someone held who signs in is told which workspace waits for their provider. The restore says how many, and every owner who isn’t held gets an email:

Restored acme, on the Free plan. Its bypass tokens and agents' credentials stayed revoked: make new ones if you need them. Its SCIM token stayed revoked too, so your identity provider needs a new one, which takes a plan with SCIM. Until it confirms them, the 12 people it added can't get in. To let someone in yourself, add them again with `jiayang workspace add`.

Your provider may also have taken away an owner it added. So if it added you, and the workspace has an owner it didn’t add, ask them to restore it: the restore refuses you. If it added every owner, any of them can, and the other owners get an email saying who restored it, those it holds included. An owner who is held can’t restore the workspace again or delete it for good, doesn’t see it among the deleted ones, and gets no other email about it, until your provider confirms them.

To let them back in, upgrade to a plan with SCIM, make a new token and give it to your provider, then start provisioning again (in Microsoft Entra ID, Restart provisioning). Each person your provider looks up or updates gets back in. Anyone it never asks about stays out, so someone who left while the workspace was deleted doesn’t come back. An owner or an admin can let someone in by hand by adding them again, or with Let in beside them on the Members page, which keeps their apps, groups and role. Only an owner can let another owner back in, and an owner can’t leave while every other owner is held. The Members page, and jiayang workspace members, show who is still waiting, and Provisioning (SCIM) in Settings says how many. Each person let back in gets an email saying so, and the audit log has a member.put entry with let_back_in for them, by your provider or by hand. Someone still waiting who opens the workspace, or runs a command on it, is told it waits for the provider, rather than that they aren’t in it. They aren’t emailed while they wait, since the provider may have taken them away while the workspace was deleted. It comes back on Free, because its subscription ended when it was deleted. Upgrade from the Billing tab to go back to a paid plan. A paid workspace can be restored once Stripe has closed its subscription, which takes up to two hours after the deletion:

error: Deleting this workspace ended its subscription, and Stripe is still closing it. Try again in a couple of hours.

To delete a kept workspace’s apps and data straight away, an owner uses Delete for good beside it on the page for making a new workspace in the dashboard, with the workspace’s short name typed back. After 30 days we delete it ourselves. Either way, every owner gets an email saying it was deleted for good and can’t be restored. So do the owners of a workspace whose deletion was final from the start. The CLI can’t delete a workspace itself, but it opens that page:

Terminal window
jiayang workspace delete acme --for-good
Opened https://app.jiayang.cloud/new
Choose Delete for good beside acme under Restore a deleted workspace, and type acme to confirm. Its apps and their data are deleted straight away, and it can't be restored. Owners only, signed in to the dashboard in the last 15 minutes.

A kept workspace still counts toward the 50 workspaces you can own, since its apps keep their data. At 50, making another is refused until you delete one for good:

error: You already own 50 workspaces, the most one account can make, and 2 of them are deleted ones you can still restore. Delete one of those for good (`jiayang workspace delete <workspace> --for-good` opens the page for it) to make room, deploy into one of the others, or write to support@jiayang.cloud to raise the limit.

Some deletions don’t wait:

  • Deleting your account. A deleted workspace that only you owned can’t be restored by anyone once your account is gone, so its apps and data are deleted within a minute. You get one email at your account’s address saying it was deleted, which workspaces you owned with others stay with them, and which deleted ones can or can’t still be restored. While you’re the only owner of an app in a deleted workspace another owner could restore, deleting your account is refused, as it is for a live one: hand the app to someone else once the workspace is back, or delete it.
  • Delete for good, on an app or a workspace, as above.
  • A workspace we close under the terms isn’t kept.
  • A deletion while someone it belongs to is on the terms dated 30 September 2026. Those terms say a deletion is immediate and permanent. It’s the owners’ data, so only the owners’ terms decide, whoever deletes it. Until those terms stop applying to anyone on 4 November 2026, an app’s deletion is final while an owner of the workspace or an owner of the app is still on them, and a workspace’s while any owner of it or of one of its apps is. Accept the current terms when the dashboard asks, and ask the other owners to. Before you delete, the dashboard and jiayang app delete without --yes say which applies. If an owner on those terms is added between that and the delete, the dashboard, and the CLI with --kept, delete nothing and say it would now be final. The dashboard tells owners and admins when an owner’s terms are why; anyone else is told only that it’s final.

The emails about deleting and restoring are about your account, so they come even if you’ve stopped the weekly email. They go to the workspace’s owners as it stands, and to anyone another owner removed or took down from owner in the last week, so nobody can be taken out of the email by being removed first. They don’t go to members or admins who were never owners, or to someone who left or stepped down themselves. An owner your identity provider still has to confirm after a restore gets only the email saying who restored it, and only when your provider had added the person who restored it.

A workspace deleted and restored over and over doesn’t fill anyone’s inbox. Each owner gets at most 4 emails a day about one workspace’s deletes and restores, at most 10 a day about what any one person did, and at most 20 a day about what people did in all. What one person does, deleting for good included, sends at most 100 emails a day, and one workspace’s deletes and restores send at most 50 a day between its owners. The emails saying we deleted a workspace for good once nobody restored it in time don’t count toward any of these.

You’re new to a workspace until you’ve been in it a week and opened it yourself, in the dashboard or with the CLI. Anyone can be made an owner without being asked, so each limit counts emails to new owners and to other owners apart, and emails to you as a new owner of one workspace never use up what you get about the others. Past a limit, an email to a new owner isn’t sent. An email to an owner who isn’t new is never dropped for a limit: the first of the day about each workspace goes whatever the limits, and a later one waits until the limit has room again, a day at most. If the workspace changes again while one waits, you get only the newer one, so you hear what’s true now.

So these emails can’t crowd out sign-in codes, we send only so many of them a day across everyone, the one saying an account was deleted included. Past that they wait their turn. Emails to owners who aren’t new go first, and then emails to new owners. In each, the email about something you did yourself goes after everyone else’s, and the rest take turns: one at a time for each person who made a change and for each address, oldest first. The email saying we deleted a workspace for good goes with the standing of the owner it’s to, and the one saying an account was deleted goes as a new owner’s while the account is less than a week old. An email still waiting 30 days after it was written is dropped, and we’re told when that happens.

To have your data erased, delete it for good, then delete your account. A record that the workspace existed stays in our own records for 30 days, as described in the privacy policy.

Every app’s database is Cloudflare D1, which keeps a history of the last 30 days. A workspace owner can put the database back as it was at a minute in that time, from a full minute after it was made up to its first restore (see after a restore):

Terminal window
jiayang db restore acme/notes --to 2026-10-04T09:00:00Z --yes
Restored the database of acme/notes to bookmark 00000084-00000240-00004c6d-0f1e2d3c4b5a69788796a5b4c3d2e1f0. To undo it, run:
jiayang db restore acme/notes --bookmark 00000085-0000024c-00004c6d-8e61117bf38d7adb71b934ebbf891683 --yes

--to takes an RFC 3339 time, or how long ago: 90m, 6h or 2d. Without --yes, nothing is restored, and you’re shown the point D1 would go back to:

error: This puts the database of acme/notes back to bookmark 00000084-00000240-00004c6d-0f1e2d3c4b5a69788796a5b4c3d2e1f0, D1's nearest point at or before 2026-10-04 09:00:00 UTC, and replaces everything written since. D1 keeps its history to the minute, so writes from up to a minute before 2026-10-04 09:00:00 UTC may not be in it: to keep a write, pick a time at least a minute after it. Run again with --yes

In the dashboard, it’s under the app’s Settings tab, in Your data, as Restore to a moment. Before you confirm, it shows the bookmark D1 would go back to. The page offers Undo straight after.

Only workspace owners can restore a database:

error: Only the workspace's owners can restore an app's database.

A moment more than 30 days ago, or one in the future, is refused:

error: A database can be restored to a moment in the last 30 days.

So is a moment before the database was made, or less than a full minute after, which D1 has no history for. The database is made at the first deploy that gives the app one:

error: The database was made at 2026-10-04 08:12:31 UTC, and D1 can restore it to 2026-10-04 08:14 UTC or later.

A time is first turned into D1’s own bookmark for it, which changes nothing. If D1 has no history for that time, the restore is refused before anything is touched:

error: D1 has no history for this database at 2026-10-04 08:14:05 UTC, so nothing changed. Pick a later moment in the last 30 days.

D1 turns a time into a point on the database’s history from before any restore, and always the same point. Once a database has been restored, a time after that restore would bring back what was there before it, not what your app had at that time. So a time after a database’s first restore is refused, and only a bookmark reaches what came after:

error: This database was restored at 2026-10-04 09:05:12 UTC. A time after that would bring back what was there before the restore, not what the app had then, so D1 can only go there by bookmark. Each restore's undo bookmark is in the activity log: jiayang audit acme --app notes. Or pick a moment from 2026-10-04 08:14 UTC to before 2026-10-04 09:05:12 UTC.

A time before the first restore still works, and so does every bookmark we gave for that database, for 30 days: the ones a restore printed, and the newest 1,000 shown for a time before you confirmed. Showing where a time goes works 60 times an hour for each app, however often the same time is asked, and never pushes out a bookmark a restore printed. A bookmark of the database as it was more than 30 days ago is refused too, since D1 no longer has that history. Any other bookmark, one from another app’s database too, is refused before D1 is asked, and nothing changes. So is one from before the app’s database was made again, since that’s another database:

error: That isn't a bookmark we gave for the database of acme/notes, so nothing changed. Each restore's undo bookmark is in the activity log: jiayang audit acme --app notes.

jiayang audit acme --app notes --json lists each restore with its previous_bookmark: the database as it was right before that restore.

A restore can’t run while the same database is being exported, and an export can’t start during a restore. The audit log records each restore three times. First app.database_restore_requested, with the time or bookmark, so a restore that may have run is always there. Then app.database_restore_started, right before D1 is asked, with the previous_bookmark that undoes it, so the way back is there even if the restore never says how it went. Then how it went: app.database_restore for one that ran, with the previous_bookmark that undoes it and, for a time, the restored_to bookmark it went to, or app.database_restore_refused for one that changed nothing, with D1’s status. Only a refusal from D1 means nothing ran. The bookmark that undoes a restore is saved before D1 is asked, apart from the log, so it works even when the log can’t be written later. If it can’t be saved and logged, the restore doesn’t start. When D1 fails a restore any other way, or never answers, it may still have run, even later on, so it’s recorded as app.database_restore with outcome set to unknown. It counts as a restore for the times after it, and its previous_bookmark is the database’s bookmark from the moment before it was asked for, which undoes it if it ran. You’re told so, with that bookmark:

error: D1 failed while restoring the database, so it may have been restored. To put it back as it was right before, run:
jiayang db restore acme/notes --bookmark 00000085-0000024c-00004c6d-8e61117bf38d7adb71b934ebbf891683 --yes

What each part of your data has behind it, and how much and how fast it can come back. The recovery point (RPO) is the newest state a restore can bring back, so anything written after it is lost. The recovery time (RTO) is how long until it’s back.

Data Where it lives Recovery point (RPO) Recovery time (RTO)
An app’s database Cloudflare D1 A minute in the last 30 days, which you choose, or after a restore, a bookmark You start the restore yourself, whenever you need it
A deleted app or workspace, with everything in it Where it was The moment it was deleted, for 30 days You restore it yourself, at once
An app’s code Each version, on Cloudflare Every version deployed A rollback, at once
An app’s files on R2 Cloudflare R2 None: a file the app overwrites or deletes is gone at once Not restorable
An app’s keys and queue Cloudflare Workers KV and Queues None: a key the app overwrites or deletes, or a message once it’s done, is gone at once Not restorable
Workspaces, members, sharing, settings, secrets and the recent audit log Our database, on Neon in AWS us-east-2 Any moment in the last 7 days, which we choose, to the second Our target is 4 hours from when we decide to restore
The audit log older than 30 days Cloudflare R2, encrypted with the workspace’s own key Not restored from a backup: it’s kept, not copied Read back as the log is

A few details:

  • Your app’s files. R2 keeps no versions or history of a file. Cloudflare designs R2 for 99.999999999% annual durability, so a file you wrote is kept, but one your app deletes or overwrites can’t be brought back. If your app needs that, write a new file rather than replacing one, and delete old ones when you’re sure.
  • A container’s own disk isn’t kept at all. It goes back to the image every time the container starts. Keep data in the app’s database or files.
  • Our own database. Neon keeps every change to it, and can put it back as it was at any moment in the last 7 days. We use that for our own disasters, never to restore something you deleted. Restoring it puts back only what’s in it: your apps’ databases and files on Cloudflare aren’t part of it and aren’t affected. It runs in one AWS region, and we keep no copy of it outside that region yet. If that whole region is down, so are the dashboard and the API, and since the edge checks access with them, apps answer 503 within a minute, until the region is back. Nothing in your apps’ databases or files is lost by that.
  • Exports. You can take your own copy at any time: the workspace export and each app’s database export, on every plan.
  • Database for the database itself and its export.
  • Storage for an app’s files.
  • Security for how your data is kept apart and protected.