Deepnote research: our notes on building agents
Get started

Syncing a workspace with the Deepnote CLI

Mirror your Deepnote workspace to a local directory with deepnote sync, and push local notebook edits back to the cloud

deepnote sync mirrors your whole Deepnote workspace into a local directory and pushes local edits back. Every project becomes a directory holding one .deepnote file per notebook, laid out along your workspace's folder tree.

deepnote sync ./workspace

Prerequisites

  • The Deepnote CLI. Install it with npm install -g @deepnote/cli (or run it through npx @deepnote/cli).
  • An API token with access to the workspace you are mirroring.

Authentication

The CLI reads your token from the DEEPNOTE_TOKEN environment variable, or from an explicit --token flag. Create a token in your workspace under Settings & members → API tokens.

export DEEPNOTE_TOKEN="<your-token>"
deepnote sync ./workspace

Without a token the command exits with code 2 and prints where to get one.

Token safety

  • Prefer the environment variable. A token passed as --token is visible in your shell history and in the process list of a shared machine.
  • In CI or a cron job, use a secret from your provider's secret store, exposed as DEEPNOTE_TOKEN for that step only.
  • Rotate and revoke from the same settings page if a token is ever exposed.
  • The token decides which workspace you are mirroring. Pointing a sync directory at a different workspace's token is the most common cause of a confusing first run — see Safety rails.

What the local directory looks like

A project maps to a directory, because a project's export is one .deepnote document per notebook rather than a single file:

workspace/
├── .deepnote-sync.json          # sync state (safe to commit)
├── Analytics/                   # a workspace folder
│   └── Sales report/            # a project
│       ├── main.deepnote        # one file per notebook
│       ├── forecast.deepnote
│       └── .files/              # working-directory files (--all-files only)
│           ├── requirements.txt
│           └── data/input.csv
└── Scratch project/
    └── main.deepnote

.deepnote-sync.json is sync's state file. It maps project IDs to local directories and records fingerprints of what was last synced, which is how sync tells "you edited this" from "someone edited it in Deepnote." Projects are tracked by ID, not by name, so renaming a project or moving it between folders in Deepnote becomes a directory move locally rather than a delete and re-download. The file is plain JSON with sorted keys, so it diffs cleanly if you commit it.

Both directions

Sync decides per project which way to move, purely by comparing content:

  • Pull — the project changed in Deepnote, or does not exist locally yet. The exported documents are written down.
  • Push — the project changed only locally. The same documents are uploaded back, with lost-update protection: if the cloud copy moved since your last sync, the upload is rejected and becomes a conflict rather than a silent overwrite.
  • Unchanged — both sides match.
  • Conflict — the project changed both locally and in Deepnote.

Project names and integration attachments are applied from the documents on push. Notebook outputs are not part of the format, so they never sync.

Conflicts

By default sync asks, per project, whether to keep the cloud version (discarding your local changes) or skip the project for now.

# Answer up front instead of being prompted
deepnote sync ./workspace --on-conflict skip
deepnote sync ./workspace --on-conflict override
ModeBehavior
askPrompt per conflicting project (default)
skipLeave every conflicting project untouched
overrideOverwrite local changes with the cloud version

Working-directory files

By default sync handles notebooks only. --all-files also mirrors each project's working-directory files — data files, requirements.txt, anything else in the project — into a .files/ subdirectory:

deepnote sync ./workspace --all-files

Files follow their project's direction: a pulled project downloads changed files, a pushed project uploads them. Downloads are incremental, so unchanged files are not re-fetched.

Before uploading any file, sync checks its current state in Deepnote. A file that changed — or was deleted — since sync last recorded it goes through the same --on-conflict choice as a diverged notebook, and skipped files are reported as N file(s) kept from Deepnote. This matters because sync is not the only writer of a project's files: deepnote publish deploys into the static root, and anyone editing the project in Deepnote can change files too.

Options

OptionDescriptionDefault
--all-filesAlso sync working-directory files (download on pull, upload on push)false
--on-conflict <mode>Conflict handling: ask, skip, or overrideask
--delete-missing-notebooksOn push, delete cloud notebooks removed from the local projectfalse
--pruneDelete local files for projects and files that no longer exist in the cloudfalse
--dry-runReport what would be synced without writing anythingfalse
--token <token>API tokenDEEPNOTE_TOKEN
--url <url>API base URL (for single-tenant instances)https://api.deepnote.com
-o, --output <format>Machine-readable output: json or llmtext

Ownership of the static site directory

A project's published static website lives under _deepnote_static in the same file store that --all-files mirrors, so it appears in .files/_deepnote_static/ like any other project file. The two commands share one rule:

  • deepnote publish writes it. That is the deploy command, and the only one that should author those files. Its source of truth is your local build directory.
  • deepnote sync mirrors it and never silently overwrites it. The per-file check described above applies to the static root too, so a site someone republished after your last sync is surfaced as a conflict instead of being reverted to your older local copy.
  • A publish inside a synced workspace keeps the mirror in step. Publishing updates the local mirror and .deepnote-sync.json for you, so the next sync does not see the deploy as drift and re-download the whole site.

Deleting things

Sync is deliberately conservative about deletion, in both directions.

  • Pulls remove local .deepnote files for notebooks that no longer exist in the cloud export.
  • --prune is required before sync deletes a local directory for a project that no longer exists in the cloud, or a local working file that no longer exists in the project.
  • --delete-missing-notebooks is required before a push deletes cloud notebooks you removed locally.
  • Files removed locally are never deleted in the cloud by --all-files alone; that is too destructive to infer.

Safety rails

  • Sync never creates or deletes cloud projects. Local .deepnote files outside a tracked project directory are reported and left alone. Use deepnote open to import one.
  • Pruning is refused when none of the tracked project IDs match the workspace the API returned. That combination almost always means the token or --url points at a different workspace, and pruning on that assumption would delete a correct local mirror. Verify the connection and retry.
  • A stale tracking entry cannot delete a directory whose path is now used by a current cloud project; only the stale tracking is removed.
  • Sync does not run Git. It writes ordinary files. Committing, branching, and pushing are yours to do — which is the main difference from Deepnote file sync, where Deepnote drives the repository side.

Use --dry-run to see what a run would do before it does it:

deepnote sync ./workspace --all-files --dry-run

Automating it

--on-conflict skip plus -o json gives a run that never prompts and reports a parsable summary, which is what you want from a cron job or CI step:

deepnote sync ./workspace --all-files --on-conflict skip -o json

Exit codes

CodeMeaning
0Success — skipped conflicts are reported but do not fail the run
1One or more projects failed to sync
2Invalid usage — missing token or bad arguments

A single broken project does not abort the rest of the workspace; it is reported as an error and the run continues.

Choosing between the three

You want to…Use
Keep one project in a Git repo, synced automatically by DeepnoteDeepnote file sync
Mirror many projects to your machine on demand, and push notebook editsdeepnote sync
Deploy a built static site or app to a projectdeepnote publish

Related