From 65a48523c8b9a4d5e8dd7e2fed92382d92f4b1cf Mon Sep 17 00:00:00 2001 From: Daniel Woelfel Date: Tue, 11 Aug 2026 14:55:35 -0700 Subject: [PATCH 1/8] initial doc on backups --- client/www/app/docs/backups/page.md | 75 ++++++++++++++++++++++++++++ client/www/components/docs/Fence.jsx | 21 ++++++-- client/www/data/docsNavigation.js | 1 + 3 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 client/www/app/docs/backups/page.md diff --git a/client/www/app/docs/backups/page.md b/client/www/app/docs/backups/page.md new file mode 100644 index 0000000000..ae2271ea68 --- /dev/null +++ b/client/www/app/docs/backups/page.md @@ -0,0 +1,75 @@ +--- +nextjs: + metadata: + title: 'Backups' + description: 'How Instant creates backups, how to download them, and what a backup archive contains.' +--- + +Instant automatically generates a nightly backup that contains all of the data from your app. + +You can view backups for the last 7 days from the `Backups` page on the dashboard, or with the cli: + +```sh {% showCopy=true %} +npx instant-cli@latest backup list +``` + +## Downloading backups + +You can download the backup as a zip file from the dashboard or through the cli: + +```sh {% showCopy=true %} +npx instant-cli@latest backup download --latest +``` + +The backup contains your app's schema, rules, magic code email template, entities, and files. + +### Anatomy of an Instant backup + +The backup contains a `config.json` file that contains the schema, rules, and magic code email template. It also includes the number of entities for each table. + +#### **Entities directory** + +The `entities` directory contains an NDJSON file for each table in your schema that has at least one entity. + +The files are named after the table name, e.g. `$users.jsonl`. + +Each JSON line includes a `createdAt` field, formatted as a Unix timestamp in milliseconds. + +The `entity` field holds the key-value map with all of the fields for the entity. + +If the key in the entity map represents a has-one link, the value will be the entity id of the entity in the linked table. If it is a has-many link, then it will be a JSON array of entity ids in the linked table. + +```json {% filename="entities/$users.jsonl" %} +{"entity":{"email":"dww@instantdb.com","id":"81d4e04d-4057-4fc0-92f7-d99618fd540a","type":"user"},"createdAt":1772650963417} +{"entity":{"id":"0e212052-a4ba-4d3c-a679-3812ba22cde2","type":"guest"},"createdAt":1776458067727} +``` + +#### **Files directory** + +The `files` directory contains all of the file blobs for your app's `$files`. + +Each listing in the `files` directory will be a `UUID` that will match the `location-id` field of a JSON line in the `entities/$files.jsonl` entry. + +```json {% filename="entities/$files.jsonl" %} +{"entity":{"size":37240,"location-id":"30b051b6-cc5d-41ce-8538-8302d6fa2695","content-type":"image/png","id":"fd3a3356-f8d6-46cc-b56f-d3f470b44fbc","path":"profile.png"},"createdAt":1772604198270} +{"entity":{"size":13768,"location-id":"770755e9-5cf0-41d2-b1cc-6552255d2ba3","content-type":"image/png","id":"fda17e15-ba53-4a9e-9e9e-75e25c031191","path":"cat.png"},"createdAt":1773774285029} +``` + +```shell +$ ls -l files/ +size name +37240 30b051b6-cc5d-41ce-8538-8302d6fa2695 +13768 770755e9-5cf0-41d2-b1cc-6552255d2ba3 +``` + +# Restore a backup + +You can restore a backup zipfile that you downloaded from the dashboard or through the cli into a self-hosted Instant instance. + +Read the [Self hosting](/docs/self-hosting) guide for more information on how to set up a self-hosted instance. + +Ensure that you've set the deployment superuser via the `INSTANT_SUPERUSER_EMAIL` environment variable. + +Sign in to the dashboard with your superuser email, then visit `${your-selfhosted-dashboard-url}/intern/restore` to restore the app into your new self-hosted instance. + +If you have any OAuth clients set up with client secrets, go to the Auth dashboard for the restored app and update the secrets. The secrets will not carry over to the restored app. diff --git a/client/www/components/docs/Fence.jsx b/client/www/components/docs/Fence.jsx index 606f17a47f..76eba6816c 100644 --- a/client/www/components/docs/Fence.jsx +++ b/client/www/components/docs/Fence.jsx @@ -2,7 +2,10 @@ import { Fragment, useContext, useEffect, useState } from 'react'; import { CopyToClipboard } from 'react-copy-to-clipboard'; -import { ClipboardDocumentIcon } from '@heroicons/react/24/outline'; +import { + ClipboardDocumentIcon, + DocumentIcon, +} from '@heroicons/react/24/outline'; import Highlight, { defaultProps, Prism } from 'prism-react-renderer'; import { SelectedAppContext } from '@/lib/SelectedAppContext'; import { rosePineDawnTheme } from '@/lib/rosePineDawnTheme'; @@ -74,7 +77,7 @@ function parseLineHighlights(lineHighlight) { return highlights; } -export function Fence({ children, language, showCopy, lineHighlight }) { +export function Fence({ children, language, showCopy, lineHighlight, filename }) { const [copyLabel, setCopyLabel] = useState('Copy'); const app = useContext(SelectedAppContext); @@ -104,7 +107,19 @@ export function Fence({ children, language, showCopy, lineHighlight }) { > {({ className, style, tokens, getLineProps, getTokenProps }) => (
-
+          {filename && (
+            
+
+ )} +
             {tokens.map((line, lineIndex) => {
               const isHighlighted = highlightedLines.has(lineIndex + 1);
               let lineTokens = line
diff --git a/client/www/data/docsNavigation.js b/client/www/data/docsNavigation.js
index 273f1b7462..45107c937e 100644
--- a/client/www/data/docsNavigation.js
+++ b/client/www/data/docsNavigation.js
@@ -145,6 +145,7 @@ module.exports = [
       { title: 'Storage', href: '/docs/storage' },
       { title: 'Streams', href: '/docs/streams' },
       { title: 'Webhooks', href: '/docs/webhooks' },
+      { title: 'Backups', href: '/docs/backups' },
       { title: 'Stripe Payments', href: '/docs/stripe-payments' },
       { title: 'Admin HTTP API', href: '/docs/http-api' },
       { title: '(Experimental) Next.js SSR', href: '/docs/next-ssr' },

From efe25e15b07e7837d357e2e80df3f287fd9f17c2 Mon Sep 17 00:00:00 2001
From: Daniel Woelfel 
Date: Tue, 11 Aug 2026 15:57:34 -0700
Subject: [PATCH 2/8] cli reference

---
 client/www/app/docs/backups/page.md | 49 ++++++++++++++++++++++++++---
 client/www/markdoc/nodes.js         |  3 ++
 2 files changed, 48 insertions(+), 4 deletions(-)

diff --git a/client/www/app/docs/backups/page.md b/client/www/app/docs/backups/page.md
index ae2271ea68..24276127d2 100644
--- a/client/www/app/docs/backups/page.md
+++ b/client/www/app/docs/backups/page.md
@@ -23,11 +23,31 @@ npx instant-cli@latest backup download --latest
 
 The backup contains your app's schema, rules, magic code email template, entities, and files.
 
-### Anatomy of an Instant backup
+{% callout type="warning" %}
+
+A backup contains all of your app's data, including user records like email addresses. Store the downloaded zip somewhere secure and treat it as sensitive.
+
+{% /callout %}
+
+## Anatomy of an Instant backup
+
+A backup is a zip file with this layout:
+
+```text
+instant-backup-.zip
+├── config.json          # schema, rules, magic code email template, entity counts
+├── entities/            # one .jsonl file per table with data
+│   ├── $users.jsonl
+│   ├── $files.jsonl
+│   └── posts.jsonl
+└── files/               # raw blobs for $files, named by location-id
+    ├── 30b051b6-cc5d-41ce-8538-8302d6fa2695
+    └── 770755e9-5cf0-41d2-b1cc-6552255d2ba3
+```
 
 The backup contains a `config.json` file that contains the schema, rules, and magic code email template. It also includes the number of entities for each table.
 
-#### **Entities directory**
+### Entities directory
 
 The `entities` directory contains an NDJSON file for each table in your schema that has at least one entity.
 
@@ -44,7 +64,7 @@ If the key in the entity map represents a has-one link, the value will be the en
 {"entity":{"id":"0e212052-a4ba-4d3c-a679-3812ba22cde2","type":"guest"},"createdAt":1776458067727}
 ```
 
-#### **Files directory**
+### Files directory
 
 The `files` directory contains all of the file blobs for your app's `$files`.
 
@@ -62,7 +82,7 @@ size   name
 13768  770755e9-5cf0-41d2-b1cc-6552255d2ba3
 ```
 
-# Restore a backup
+## Restore a backup
 
 You can restore a backup zipfile that you downloaded from the dashboard or through the cli into a self-hosted Instant instance.
 
@@ -73,3 +93,24 @@ Ensure that you've set the deployment superuser via the `INSTANT_SUPERUSER_EMAIL
 Sign in to the dashboard with your superuser email, then visit `${your-selfhosted-dashboard-url}/intern/restore` to restore the app into your new self-hosted instance.
 
 If you have any OAuth clients set up with client secrets, go to the Auth dashboard for the restored app and update the secrets. The secrets will not carry over to the restored app.
+
+## CLI reference
+
+```shell
+# List the downloadable backups for your app
+npx instant-cli@latest backup list
+
+# Output the list as JSON
+npx instant-cli@latest backup list --json
+
+# Download a backup (interactive picker when no backup is given)
+npx instant-cli@latest backup download
+
+# Download the most recent backup
+npx instant-cli@latest backup download --latest
+
+# Download a specific backup to a path of your choosing
+npx instant-cli@latest backup download  --out my-backup.zip
+```
+
+All commands read the app ID from your `.env` file (`INSTANT_APP_ID`, or a framework-specific variant like `NEXT_PUBLIC_INSTANT_APP_ID`; see [App ID](/docs/cli#app-id)). Pass `--app ` to target a different one, and run `--help` on any command for the full list of flags.
diff --git a/client/www/markdoc/nodes.js b/client/www/markdoc/nodes.js
index 7b4827491b..f661c6463a 100644
--- a/client/www/markdoc/nodes.js
+++ b/client/www/markdoc/nodes.js
@@ -94,6 +94,9 @@ const nodes = {
       showCopy: {
         type: Boolean,
       },
+      filename: {
+        type: String,
+      },
     },
   },
   heading: {

From 54777124cd7d4934b391ffd8314c4e7859d519c5 Mon Sep 17 00:00:00 2001
From: Daniel Woelfel 
Date: Tue, 11 Aug 2026 15:59:58 -0700
Subject: [PATCH 3/8] fix formatting

---
 client/www/components/docs/Fence.jsx | 17 ++++++++++++++---
 1 file changed, 14 insertions(+), 3 deletions(-)

diff --git a/client/www/components/docs/Fence.jsx b/client/www/components/docs/Fence.jsx
index 76eba6816c..c898e6feec 100644
--- a/client/www/components/docs/Fence.jsx
+++ b/client/www/components/docs/Fence.jsx
@@ -77,7 +77,13 @@ function parseLineHighlights(lineHighlight) {
   return highlights;
 }
 
-export function Fence({ children, language, showCopy, lineHighlight, filename }) {
+export function Fence({
+  children,
+  language,
+  showCopy,
+  lineHighlight,
+  filename,
+}) {
   const [copyLabel, setCopyLabel] = useState('Copy');
 
   const app = useContext(SelectedAppContext);
@@ -110,9 +116,14 @@ export function Fence({ children, language, showCopy, lineHighlight, filename })
           {filename && (
             
-
)} From f31f1c57f2185a72aaa608d867a10f6da5131c16 Mon Sep 17 00:00:00 2001 From: Daniel Woelfel Date: Tue, 11 Aug 2026 16:07:02 -0700 Subject: [PATCH 4/8] path first --- client/www/app/docs/backups/page.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/www/app/docs/backups/page.md b/client/www/app/docs/backups/page.md index 24276127d2..67a30a8763 100644 --- a/client/www/app/docs/backups/page.md +++ b/client/www/app/docs/backups/page.md @@ -71,8 +71,8 @@ The `files` directory contains all of the file blobs for your app's `$files`. Each listing in the `files` directory will be a `UUID` that will match the `location-id` field of a JSON line in the `entities/$files.jsonl` entry. ```json {% filename="entities/$files.jsonl" %} -{"entity":{"size":37240,"location-id":"30b051b6-cc5d-41ce-8538-8302d6fa2695","content-type":"image/png","id":"fd3a3356-f8d6-46cc-b56f-d3f470b44fbc","path":"profile.png"},"createdAt":1772604198270} -{"entity":{"size":13768,"location-id":"770755e9-5cf0-41d2-b1cc-6552255d2ba3","content-type":"image/png","id":"fda17e15-ba53-4a9e-9e9e-75e25c031191","path":"cat.png"},"createdAt":1773774285029} +{"entity":{"size":37240,"location-id":"30b051b6-cc5d-41ce-8538-8302d6fa2695","path":"profile.png","content-type":"image/png","id":"fd3a3356-f8d6-46cc-b56f-d3f470b44fbc"},"createdAt":1772604198270} +{"entity":{"size":13768,"location-id":"770755e9-5cf0-41d2-b1cc-6552255d2ba3","path":"cat.png","content-type":"image/png","id":"fda17e15-ba53-4a9e-9e9e-75e25c031191"},"createdAt":1773774285029} ``` ```shell From 17060ffd71900eb3510a32b95881cd473acbe3ca Mon Sep 17 00:00:00 2001 From: Daniel Woelfel Date: Tue, 11 Aug 2026 16:19:26 -0700 Subject: [PATCH 5/8] use the existing convention --- client/www/app/docs/backups/page.md | 6 ++++-- client/www/components/docs/Fence.jsx | 32 +++------------------------- client/www/markdoc/nodes.js | 3 --- 3 files changed, 7 insertions(+), 34 deletions(-) diff --git a/client/www/app/docs/backups/page.md b/client/www/app/docs/backups/page.md index 67a30a8763..6b99a755dd 100644 --- a/client/www/app/docs/backups/page.md +++ b/client/www/app/docs/backups/page.md @@ -59,7 +59,8 @@ The `entity` field holds the key-value map with all of the fields for the entity If the key in the entity map represents a has-one link, the value will be the entity id of the entity in the linked table. If it is a has-many link, then it will be a JSON array of entity ids in the linked table. -```json {% filename="entities/$users.jsonl" %} +{% file label="entities/$users.jsonl" /%} +```json {"entity":{"email":"dww@instantdb.com","id":"81d4e04d-4057-4fc0-92f7-d99618fd540a","type":"user"},"createdAt":1772650963417} {"entity":{"id":"0e212052-a4ba-4d3c-a679-3812ba22cde2","type":"guest"},"createdAt":1776458067727} ``` @@ -70,7 +71,8 @@ The `files` directory contains all of the file blobs for your app's `$files`. Each listing in the `files` directory will be a `UUID` that will match the `location-id` field of a JSON line in the `entities/$files.jsonl` entry. -```json {% filename="entities/$files.jsonl" %} +{% file label="entities/$files.jsonl" /%} +```json {"entity":{"size":37240,"location-id":"30b051b6-cc5d-41ce-8538-8302d6fa2695","path":"profile.png","content-type":"image/png","id":"fd3a3356-f8d6-46cc-b56f-d3f470b44fbc"},"createdAt":1772604198270} {"entity":{"size":13768,"location-id":"770755e9-5cf0-41d2-b1cc-6552255d2ba3","path":"cat.png","content-type":"image/png","id":"fda17e15-ba53-4a9e-9e9e-75e25c031191"},"createdAt":1773774285029} ``` diff --git a/client/www/components/docs/Fence.jsx b/client/www/components/docs/Fence.jsx index c898e6feec..606f17a47f 100644 --- a/client/www/components/docs/Fence.jsx +++ b/client/www/components/docs/Fence.jsx @@ -2,10 +2,7 @@ import { Fragment, useContext, useEffect, useState } from 'react'; import { CopyToClipboard } from 'react-copy-to-clipboard'; -import { - ClipboardDocumentIcon, - DocumentIcon, -} from '@heroicons/react/24/outline'; +import { ClipboardDocumentIcon } from '@heroicons/react/24/outline'; import Highlight, { defaultProps, Prism } from 'prism-react-renderer'; import { SelectedAppContext } from '@/lib/SelectedAppContext'; import { rosePineDawnTheme } from '@/lib/rosePineDawnTheme'; @@ -77,13 +74,7 @@ function parseLineHighlights(lineHighlight) { return highlights; } -export function Fence({ - children, - language, - showCopy, - lineHighlight, - filename, -}) { +export function Fence({ children, language, showCopy, lineHighlight }) { const [copyLabel, setCopyLabel] = useState('Copy'); const app = useContext(SelectedAppContext); @@ -113,24 +104,7 @@ export function Fence({ > {({ className, style, tokens, getLineProps, getTokenProps }) => (
- {filename && ( -
-
- )} -
+          
             {tokens.map((line, lineIndex) => {
               const isHighlighted = highlightedLines.has(lineIndex + 1);
               let lineTokens = line
diff --git a/client/www/markdoc/nodes.js b/client/www/markdoc/nodes.js
index f661c6463a..7b4827491b 100644
--- a/client/www/markdoc/nodes.js
+++ b/client/www/markdoc/nodes.js
@@ -94,9 +94,6 @@ const nodes = {
       showCopy: {
         type: Boolean,
       },
-      filename: {
-        type: String,
-      },
     },
   },
   heading: {

From 51edb66fa42b0b4f2736c4af25f1da5af4c87289 Mon Sep 17 00:00:00 2001
From: Daniel Woelfel 
Date: Tue, 11 Aug 2026 16:22:44 -0700
Subject: [PATCH 6/8] format

---
 client/www/app/docs/backups/page.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/client/www/app/docs/backups/page.md b/client/www/app/docs/backups/page.md
index 6b99a755dd..f8f354e5fa 100644
--- a/client/www/app/docs/backups/page.md
+++ b/client/www/app/docs/backups/page.md
@@ -60,6 +60,7 @@ The `entity` field holds the key-value map with all of the fields for the entity
 If the key in the entity map represents a has-one link, the value will be the entity id of the entity in the linked table. If it is a has-many link, then it will be a JSON array of entity ids in the linked table.
 
 {% file label="entities/$users.jsonl" /%}
+
 ```json
 {"entity":{"email":"dww@instantdb.com","id":"81d4e04d-4057-4fc0-92f7-d99618fd540a","type":"user"},"createdAt":1772650963417}
 {"entity":{"id":"0e212052-a4ba-4d3c-a679-3812ba22cde2","type":"guest"},"createdAt":1776458067727}
@@ -72,6 +73,7 @@ The `files` directory contains all of the file blobs for your app's `$files`.
 Each listing in the `files` directory will be a `UUID` that will match the `location-id` field of a JSON line in the `entities/$files.jsonl` entry.
 
 {% file label="entities/$files.jsonl" /%}
+
 ```json
 {"entity":{"size":37240,"location-id":"30b051b6-cc5d-41ce-8538-8302d6fa2695","path":"profile.png","content-type":"image/png","id":"fd3a3356-f8d6-46cc-b56f-d3f470b44fbc"},"createdAt":1772604198270}
 {"entity":{"size":13768,"location-id":"770755e9-5cf0-41d2-b1cc-6552255d2ba3","path":"cat.png","content-type":"image/png","id":"fda17e15-ba53-4a9e-9e9e-75e25c031191"},"createdAt":1773774285029}

From 70fbe0f0af4d9e4b71aed83e590608d67868700d Mon Sep 17 00:00:00 2001
From: Daniel Woelfel 
Date: Tue, 11 Aug 2026 16:31:55 -0700
Subject: [PATCH 7/8] link to superuser

---
 client/www/app/docs/backups/page.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/client/www/app/docs/backups/page.md b/client/www/app/docs/backups/page.md
index f8f354e5fa..a5fa4bf470 100644
--- a/client/www/app/docs/backups/page.md
+++ b/client/www/app/docs/backups/page.md
@@ -92,7 +92,7 @@ You can restore a backup zipfile that you downloaded from the dashboard or throu
 
 Read the [Self hosting](/docs/self-hosting) guide for more information on how to set up a self-hosted instance.
 
-Ensure that you've set the deployment superuser via the `INSTANT_SUPERUSER_EMAIL` environment variable.
+Ensure that you've set the [deployment superuser](/docs/self-hosting#configure-the-deployment-superuser) via the `INSTANT_SUPERUSER_EMAIL` environment variable.
 
 Sign in to the dashboard with your superuser email, then visit `${your-selfhosted-dashboard-url}/intern/restore` to restore the app into your new self-hosted instance.
 

From 1e85917658719c232bf01efa3a06a7fd7c572aa1 Mon Sep 17 00:00:00 2001
From: Daniel Woelfel 
Date: Tue, 11 Aug 2026 16:40:26 -0700
Subject: [PATCH 8/8] link to backups docs

---
 client/www/app/docs/self-hosting/migrate/page.md | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/client/www/app/docs/self-hosting/migrate/page.md b/client/www/app/docs/self-hosting/migrate/page.md
index 7a5ddfb6f3..931c914e7d 100644
--- a/client/www/app/docs/self-hosting/migrate/page.md
+++ b/client/www/app/docs/self-hosting/migrate/page.md
@@ -37,11 +37,7 @@ Keep the Instant Cloud callback configured until the migration is complete.
 ### Restore a test backup
 
 Migrating without data loss will require some downtime. To get a sense of how
-much time it will take, we'll do the following:
-
-1. Export a backup from Instant Cloud
-2. [Restore the backup](/intern/restore) into your self-hosted Instant (you'll
-   need to be logged in as [your deployment superuser](/docs/self-hosting#configure-the-deployment-superuser) to have access to restore).
+much time it will take, first [restore a backup from Instant Cloud](/docs/backups#restore-a-backup).
 
 After restoring, verify that the following look correct: