Testing locally
Run marimohub on your machine with no external services. This is the fastest way to evaluate the app and test changes before wiring real storage, compute, and auth providers.
For container-based evaluation, use the Docker Compose example. It uses persistent filesystem storage, kernels inside the hub container, and dev authentication. The development stack below defaults to memory storage.
Prerequisites
- Node >= 24.11
- pnpm 10.34.5
uvand Python, required only when you start local kernels
Check the installed versions:
node --version
pnpm --version
uv --version
python3 --versionRun the dev stack
Clone the repository if you do not already have a checkout, then run from its root:
git clone https://github.com/marimo-team/marimohub.git
cd marimohub
pnpm install --frozen-lockfile
pnpm devpnpm dev watches the TypeScript server and runs it with the Vite web server in parallel. It skips the production server build. The development entrypoint sets:
MARIMOHUB_STORAGE_BACKEND=memory
MARIMOHUB_ALLOW_EPHEMERAL_STORAGE=true
MARIMOHUB_COMPUTE_BACKEND=local
MARIMOHUB_AUTH_BACKEND=dev
MARIMOHUB_SUPER_ADMINS=user@localhost
MARIMOHUB_INTEGRATIONS=on
MARIMOHUB_INTEGRATIONS_PROBE=private
MARIMOHUB_DATA_BROWSER=full
MARIMOHUB_DATA_PREVIEW_IMAGE=ghcr.io/marimo-team/marimo-sandbox:latestThe entrypoint also generates a random local MARIMOHUB_SECRETS_KEK so integrations with inline secrets work without setup. With MARIMOHUB_DEV_PERSIST=true the key is kept in .context/dev-secrets-kek so persisted secrets stay decryptable across restarts. Set your own MARIMOHUB_SECRETS_KEK to override this value.
Startup is ready when the server process is listening on port 3000 and the web process prints a Vite local URL on port 5175. The server owns the API; the web dev server proxies /api requests to it.
The development API binds to 127.0.0.1 because development authentication grants every request a fixed super-admin identity. Set DEV_HOST only when you intend to expose that identity to other clients; a non-loopback value prints a warning. For example, DEV_HOST=0.0.0.0 pnpm dev accepts connections on every interface.
PORT overrides the API port and WEB_PORT overrides the Vite port. For parallel worktrees, set DEV_PORT_BASE; the API uses that port and the web app uses the next port. Explicit PORT and WEB_PORT values take precedence:
DEV_PORT_BASE=4100 pnpm devWhat the local backends mean
| Part | Local backend | Production swap |
|---|---|---|
| Storage | memory, volatile | CAIOS, S3, GCS, Azure, or R2 -> Storage |
| Compute | local, host process | CoreWeave, Modal, Kubernetes, Docker, Podman -> Compute |
| Auth | dev, fixed local user | OIDC, trusted SSO proxy, or Cloudflare Access -> Auth |
By default, state is held in memory and disappears on restart. The stack starts kernels on your machine, signs every request in as a fixed super admin, enables integrations and the full data browser (metadata, file previews, and DuckDB-Wasm SQL), and seeds a welcome notebook plus an org-wide local-development environment. Browsing real data requires a live service; start one with pnpm dev:services below. None of this changes deployed defaults.
To keep projects and notebooks across restarts, opt into filesystem storage:
MARIMOHUB_DEV_PERSIST=true pnpm devThis stores local state in .context/dev-storage, which is ignored by Git. Stop the dev stack before clearing it:
pnpm dev:resetLocal data services
Data features need something to browse. With Docker installed, start a local S3-compatible object store and an Iceberg REST catalog:
pnpm dev:servicesThis runs scripts/dev-services/compose.yaml:
- MinIO on
http://localhost:19000, with a web console onhttp://localhost:19001(log in withminioadmin/minioadmin). - An Apache Iceberg REST catalog on
http://localhost:18181, storing table data in the MinIOwarehousebucket.
Seed containers create a dev-data bucket with sample files and an empty demo.events Iceberg table. When these endpoints respond at startup, pnpm dev seeds two org integrations: local-minio (S3) and local-iceberg (Iceberg REST). Start the services before the dev stack, or restart pnpm dev after they are up. Set MARIMOHUB_DEV_SERVICES=off to skip the probe.
Stop the services with pnpm dev:services:down. Object data survives restarts in a named Docker volume; remove it with docker compose -f scripts/dev-services/compose.yaml down -v.
Run the server manually
The root pnpm dev script loads apps/server/.env when that file exists. Copy the example when you want persistent local overrides:
cp apps/server/.env.example apps/server/.envThe development entrypoint overrides storage, compute, auth, access, and feature values. The file can set other values such as MARIMOHUB_DEV_PERSIST=true. Set port overrides on the root command so the server and web proxy receive the same values.
Validate the local run
In another terminal, check the unauthenticated health endpoint:
curl --fail --silent http://localhost:3000/api/healthThe expected response is {"status":"ok"}. Then:
- Open
http://localhost:5175. - Open the seeded welcome notebook, or create a project and notebook.
- Start a kernel. If
uvand Python are unavailable, stop after step 2. - Stop and restart
pnpm dev.
Projects disappear after restart when you use the default memory storage. That is expected. Set MARIMOHUB_DEV_PERSIST=true for durable local state. Use a production storage configuration in a deployed environment.
MCP kernel integration tests
These tests use a real marimo server to check headless startup, failure recovery, and state preservation after browser attachment.
From the repository root:
uv venv .context/marimo-test
uv pip install --python .context/marimo-test/bin/python 'marimo==0.24.2'
MARIMO_INTEGRATION_PYTHON="$PWD/.context/marimo-test/bin/python" \
pnpm --filter @marimo-hub/core test src/services/runtime/kernelBootstrap.integration.test.tsFor another runtime, replace the marimo version. Without MARIMO_INTEGRATION_PYTHON, the normal test suite skips these tests.
Thumbnail and app security tests
The thumbnail tests use Chromium to render saved HTML and check that HTTP, WebSocket, and WebRTC requests cannot reach external listeners. Use the Python environment from the kernel integration tests:
uv pip install --python .context/marimo-test/bin/python 'playwright==1.58.0'
.context/marimo-test/bin/python -m playwright install --with-deps chromium
MARIMOHUB_THUMBNAIL_TEST_PYTHON="$PWD/.context/marimo-test/bin/python" \
pnpm --filter @marimo-hub/core test src/services/runtime/thumbnailProgram.live.test.tsWithout MARIMOHUB_THUMBNAIL_TEST_PYTHON, the normal test suite skips these tests. CI runs both kernel and thumbnail integration tests with pinned dependencies.
Run uv run scripts/test-app-source.py to check that app clients cannot retrieve notebook source. See App source protection for the covered surfaces.
Next
When you are ready to deploy, go to Getting started to choose production backends and generate configuration.