Storage
Storage is where marimohub keeps notebooks, version history, session records, and system metadata. There is no separate database, so the storage backend is the state you back up and recover.
Selector: MARIMOHUB_STORAGE_BACKEND. Full variables: Configuration -> Storage.
Choose a backend
| Backend | Selector | Durable | Use for |
|---|---|---|---|
| S3 | s3 | Yes | CoreWeave CAIOS, AWS S3, MinIO, Tigris, Ceph |
| GCS | gcs | Yes | Google Cloud Storage |
| Azure | azure | Yes | Azure Blob Storage |
| Filesystem | fs | Yes | Single-node self-hosting on a local disk |
| R2 | r2 | Yes | Cloudflare Workers through a platform binding |
| Memory | memory | No | Local development and tests only |
| External | library | Varies | Operator-provided Node adapter |
s3 is the default for the Node server. fs needs no external store but is single-replica only (see below). r2 is Workers-only because it uses a runtime binding instead of credentials. memory requires MARIMOHUB_ALLOW_EPHEMERAL_STORAGE=true so it cannot back a real deployment by accident.
Requirement: atomic conditional writes
marimohub relies on atomic conditional writes to update notebooks safely under concurrent edits. Your store must support them. The server checks this at startup and refuses to run on a store that ignores conditional writes.
Known-good options:
- CoreWeave CAIOS, AWS S3, R2, recent MinIO, and Tigris through S3
If-Match. - Google Cloud Storage through object generations (
ifGenerationMatch). - The
fsbackend enforces conditional writes within a single server process (and the server logs a startup warning saying so). That is safe for one replica; run multiple replicas only ons3,gcs, orazure.
Configure it
S3-compatible setup
Works with any S3-compatible store (AWS S3, MinIO, Tigris, Ceph, CoreWeave CAIOS, or Cloudflare R2 via its S3 endpoint).
- Create a bucket for marimohub.
- Get credentials with read/write on that bucket (an access key + secret), or rely on the instance/SDK default credential chain on AWS.
- Set the env:
MARIMOHUB_STORAGE_BACKEND=s3
MARIMOHUB_STORAGE_S3_BUCKET=orgname-marimohub
MARIMOHUB_STORAGE_S3_ENDPOINT=https://s3.us-east-1.amazonaws.com # omit for AWS
MARIMOHUB_STORAGE_S3_REGION=us-east-1
MARIMOHUB_STORAGE_S3_ACCESS_KEY_ID=… # secret — or use the SDK default chain
MARIMOHUB_STORAGE_S3_SECRET_ACCESS_KEY=… # secretYour store must support conditional writes
marimohub uses S3 If-Match to update notebooks safely and refuses to start on a store that doesn't honor it. AWS S3, R2, Tigris, CoreWeave CAIOS, and recent MinIO all qualify; very old MinIO/Ceph builds may not.
MinIO / Ceph need path-style addressing
Set MARIMOHUB_STORAGE_S3_FORCE_PATH_STYLE=true for MinIO and Ceph, or requests to the bucket will fail to resolve.
CoreWeave CAIOS
CAIOS (CoreWeave AI Object Storage) is CoreWeave's S3-compatible store and the storage half of the CKS deployment. Use the s3 backend and point the endpoint at CAIOS:
MARIMOHUB_STORAGE_BACKEND=s3
MARIMOHUB_STORAGE_S3_BUCKET=orgname-marimohub
MARIMOHUB_STORAGE_S3_ENDPOINT=https://cwobject.com
MARIMOHUB_STORAGE_S3_ACCESS_KEY_ID=… # CAIOS access key (secret)
MARIMOHUB_STORAGE_S3_SECRET_ACCESS_KEY=… # CAIOS secret key (secret)Google Cloud Storage
Native Google Cloud Storage over its JSON API.
- Create a bucket in your GCP project.
- Create a service account and grant it object read/write on the bucket (
roles/storage.objectAdmin, orobjectUser). - Download a JSON key for that service account.
- Set the env (pass the key's JSON contents):
MARIMOHUB_STORAGE_BACKEND=gcs
MARIMOHUB_STORAGE_GCS_BUCKET=orgname-marimohub
MARIMOHUB_STORAGE_GCS_SA_KEY='{ "type": "service_account", … }' # key JSON (secret)
# …or, instead of a key, a pre-minted token:
# MARIMOHUB_STORAGE_GCS_ACCESS_TOKEN=ya29.… # (secret)The key is minted into short-lived access tokens at runtime — no token rotation to manage. See Deploying → GCP for an end-to-end recipe.
Use this, not the S3 shim, on GCP
The native GCS backend gets safe concurrent writes via object generations. GCS's S3-compatible endpoint has weak conditional-write support, so prefer gcs over pointing the s3 backend at GCS.
Azure Blob Storage
Native Azure Blob Storage through the Azure SDK.
- Create a storage account and private container for marimohub.
- Grant the server identity
Storage Blob Data Contributoron that container or its storage account. - Set the container and Blob service URL:
MARIMOHUB_STORAGE_BACKEND=azure
MARIMOHUB_STORAGE_AZURE_CONTAINER=orgname-marimohub
MARIMOHUB_STORAGE_AZURE_ACCOUNT_URL=https://account.blob.core.windows.netThe server uses DefaultAzureCredential, so managed identity, workload identity, service-principal environment variables, and Azure developer credentials work without additional marimohub secrets.
For Azurite, local development, or a legacy deployment, use a connection string instead. Set either the connection string or the account URL. The server rejects configuration that sets both.
MARIMOHUB_STORAGE_BACKEND=azure
MARIMOHUB_STORAGE_AZURE_CONTAINER=orgname-marimohub
MARIMOHUB_STORAGE_AZURE_CONNECTION_STRING='…' # secretThe container must already exist. On startup, marimohub verifies that ETag conditions are enforced atomically and refuses to use a data-unsafe store.
Filesystem setup
Store everything in a directory on the host — no external store to run:
MARIMOHUB_STORAGE_BACKEND=fs
MARIMOHUB_STORAGE_FS_ROOT=/var/lib/marimohub/storageThe directory is created if missing, and objects appear in it as plain files (_system/…, projects/…) you can browse and back up directly. Keep the root on a single filesystem/volume — writes rely on atomic renames, which don't work across mount points.
When the server runs in a container, the root path is inside the container: mount a persistent volume at it (a Docker bind mount / named volume, or a PVC on Kubernetes) that the server's user can write, or the data disappears with the container.
Single replica only
Conditional writes (the compare-and-swap that protects concurrent notebook edits) are enforced within one server process. Never run two hub replicas against the same directory — concurrent edits could lose catalog updates. The server logs a preflight warning at startup to remind you. Use s3 or gcs for multi-replica deployments.
Pairs naturally with the local or docker compute backends on the same machine. Sandboxes can't reach the directory as an S3 bucket, so notebook file sync uses the hub-mediated fallback copy — which those backends already do. For the full one-box recipe, see Deploying on a single instance.
Memory (dev/tests)
No external store — set the selector plus the explicit opt-in:
MARIMOHUB_STORAGE_BACKEND=memory
MARIMOHUB_ALLOW_EPHEMERAL_STORAGE=true # required acknowledgement that this is volatileVolatile — data is lost on restart
Everything lives in process memory and disappears when marimohub stops. It's for local dev and tests only; the opt-in flag exists so it can never back a real deployment by accident. Use s3 or gcs for anything you want to keep.
External library
Set the backend and module:
MARIMOHUB_STORAGE_BACKEND=library
MARIMOHUB_STORAGE_LIBRARY=/etc/marimohub/storage.mjsThe module must default-export an API version 1 storage manifest. Its factory must return a complete Bucket. The server validates both at startup.
Only the Node server supports external adapters. Load only trusted code. It runs in-process with server privileges. A storage adapter must provide atomic conditional writes. It must implement verifyConditionalWrites() and set casScope to global or process.
Validate it
After deploy:
- Start the server and check that startup does not report a fatal storage preflight failure.
- Create a project.
- Create and save a notebook.
- Restart the server.
- Confirm the project and notebook are still present.
Production cautions
- Back up the object store. It is the database.
- Do not use
memoryoutside local development or tests. - With
fs, run exactly one hub replica and back up the storage root directory; keep it on a single filesystem/volume. - Keep bucket permissions narrow. The hub needs access only to its own prefix or bucket.
- Treat conditional-write failures as a storage compatibility issue, not as a transient app bug.
Troubleshooting
See Troubleshooting -> The server refuses to start and Operations -> Backups & restore.