Skip to content

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

BackendSelectorDurableUse for
S3s3YesCoreWeave CAIOS, AWS S3, MinIO, Tigris, Ceph
GCSgcsYesGoogle Cloud Storage
AzureazureYesAzure Blob Storage
FilesystemfsYesSingle-node self-hosting on a local disk
R2r2YesCloudflare Workers through a platform binding
MemorymemoryNoLocal development and tests only
ExternallibraryVariesOperator-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 fs backend 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 on s3, gcs, or azure.

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).

  1. Create a bucket for marimohub.
  2. Get credentials with read/write on that bucket (an access key + secret), or rely on the instance/SDK default credential chain on AWS.
  3. Set the env:
bash
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=   # secret

Your 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:

bash
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.

  1. Create a bucket in your GCP project.
  2. Create a service account and grant it object read/write on the bucket (roles/storage.objectAdmin, or objectUser).
  3. Download a JSON key for that service account.
  4. Set the env (pass the key's JSON contents):
bash
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.

  1. Create a storage account and private container for marimohub.
  2. Grant the server identity Storage Blob Data Contributor on that container or its storage account.
  3. Set the container and Blob service URL:
bash
MARIMOHUB_STORAGE_BACKEND=azure
MARIMOHUB_STORAGE_AZURE_CONTAINER=orgname-marimohub
MARIMOHUB_STORAGE_AZURE_ACCOUNT_URL=https://account.blob.core.windows.net

The 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.

bash
MARIMOHUB_STORAGE_BACKEND=azure
MARIMOHUB_STORAGE_AZURE_CONTAINER=orgname-marimohub
MARIMOHUB_STORAGE_AZURE_CONNECTION_STRING='…'  # secret

The 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:

bash
MARIMOHUB_STORAGE_BACKEND=fs
MARIMOHUB_STORAGE_FS_ROOT=/var/lib/marimohub/storage

The 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:

bash
MARIMOHUB_STORAGE_BACKEND=memory
MARIMOHUB_ALLOW_EPHEMERAL_STORAGE=true   # required acknowledgement that this is volatile

Volatile — 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:

bash
MARIMOHUB_STORAGE_BACKEND=library
MARIMOHUB_STORAGE_LIBRARY=/etc/marimohub/storage.mjs

The 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:

  1. Start the server and check that startup does not report a fatal storage preflight failure.
  2. Create a project.
  3. Create and save a notebook.
  4. Restart the server.
  5. Confirm the project and notebook are still present.

Production cautions

  • Back up the object store. It is the database.
  • Do not use memory outside 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.

Provider-agnostic. Deploy anywhere.