CLI: cellucid serve quickstart#
cellucid serve is the fastest way to open the Cellucid viewer from a terminal.
It:
detects your input format (any file or folder Cellucid reads:
.h5ad, an AnnData Zarr folder or.zip,.h5muor MuData Zarr,.loom, a 10x.h5file or Cell Rangerouts/folder, a MatrixMarket folder, an.rds/.rda/.RData, gzipped or not),starts a small local HTTP server,
prints a Viewer URL you open in your browser,
and keeps serving data until you stop it.
At a glance#
Audience
Wet lab / non-technical: copy/paste the commands; you don’t need to write Python.
Computational: focus on direct input (AnnData, MuData, Loom, 10x, MatrixMarket, R objects) and SSH tunnel workflows.
Time
Local machine: ~5–10 minutes
Remote/HPC with SSH tunnel: ~15–30 minutes
Prerequisites
pip install cellucidOne of:
.h5ad, a Zarr store (folder or.zip),.h5mu,.loom, a 10x.h5orouts/folder, a MatrixMarket folder, an.rds/.rda/.RData
Step-by-step (fast path)#
Step 0 — Install#
pip install cellucid
Step 1 — Start the server (auto-detects format)#
cellucid serve /path/to/data.h5ad --dataset-name "My dataset" --dataset-id my-dataset
# or:
# cellucid serve /path/to/data.zarr --dataset-name "My dataset" --dataset-id my-dataset
The viewer reads the file itself — in place where the format allows it, once
from the start for an R object or a MatrixMarket folder; the server serves its
bytes (with sendfile and exact Range answers). --dataset-name and
--dataset-id default to the AnnData or MuData’s own
uns['cellucid']['name'] and ['id']; a dataset that declares none (any R
object, Loom, 10x or MatrixMarket dataset) is named after its file, its id
the file’s name without the extension, printed when the server starts. A file written by cellucid.prepare() declares both, so
cellucid serve pbmc_cellucid.h5ad is the whole command for one.
The same command serves the other formats:
cellucid serve cite.h5mu # a MuData from cellucid.prepare()
cellucid serve GSM4567_seurat.rds.gz --dataset-name "GSM4567" --dataset-id gsm4567
cellucid serve GSE164378_RAW/ --dataset-name "GSE164378" --dataset-id gse164378 # GSM…_matrix.mtx.gz files
cellucid serve outs/ --dataset-name "PBMC 1k" --dataset-id pbmc-1k # a Cell Ranger outs/ folder
You should see a banner like:
CELLUCID SERVER RUNNING
Local URL: http://127.0.0.1:8765
Viewer URL: http://127.0.0.1:8765/?viewer=true&viewerId=…&viewerToken=…&anndata=true
Copy the printed Viewer URL exactly; the bare Local URL is the server
origin for diagnostics, not the launch URL. Its viewerId and viewerToken
open the page’s viewer channel, through which an agent app on this computer
can work in it (cellucid mcp, Local agents (MCP): Claude Code, Claude Desktop, Cursor); the address without
them (http://127.0.0.1:8765/?anndata=true) opens the same dataset with no
channel.
Step 2 — Open the viewer in your browser#
Copy the Viewer URL into your browser address bar.
Note
You do not need to open cellucid.com manually. Before binding, the Python
server establishes its exact published web generation and then serves it from
the dataset-server origin.
Step 3 — Keep the terminal running#
If you close the terminal (or stop the process), the viewer will stop working.
Step 4 — Stop the server#
Press Ctrl+C in the terminal.
What startup prints#
Startup runs four numbered steps.
[1/4] Detecting format...
Path: /path/to/pbmc_demo.h5ad
Format: AnnData (.h5ad)
✓ Format detected
[2/4] Reading metadata...
Cells: 4,096
Genes: 512
obsm: X_umap
obs fields: 3
obsp: connectivities
layers: none
✓ Metadata read
[3/4] Checking launch choices...
✓ Launch choices checked
[4/4] Starting server...
✓ Server ready
Step 2 lists what the store holds, from its metadata; no array is read. Step 3
checks each launch choice you set (--embedding-2d, --knn, --expression,
…) against that metadata and lists it; a name the store does not have stops
the server here, before it binds. A format Python does not read (an R object,
Loom, 10x, a MatrixMarket folder) prints Contents: read by the browser when it opens the data in step 2, and its choices are checked only for their form
here and against the data by the viewer when it opens it. A choice you leave unset is taken from the
store’s uns['cellucid']['defaults'], else the viewer’s default rule — see
How the viewer reads AnnData (show_anndata() / serve_anndata()).
What the CLI auto-detects (important for debugging)#
cellucid serve <data_path> decides what you meant using these rules:
A regular file must be named as a format names it (
.h5ad, a.zipholding a Zarr store,.h5mu,.loom,.h5,.rds[.gz],.rda[.gz],.RData[.gz]); its first bytes then decide what it is (an HDF5 root markedmudatais a MuData whatever its name).A Zarr store folder must hold
zarr.json(Zarr v3) or.zgroup(Zarr v2) at its root; a MuData Zarr store says so in its root attributes.Any other folder must be a Cell Ranger
outs/(holdingfiltered_feature_bc_matrix.h5) or a MatrixMarket folder (a.mtx[.gz]file, whatever its name, such asGSM4567_matrix.mtx.gz, with its lists).A directory name ending in
.zarris not sufficient by itself, and a valid Zarr store does not need that suffix.Anything else → error:
Unable to detect format for: <path>.
If detection fails, the error message will tell you what it expected.
Most useful flags#
Run cellucid serve --help anytime.
Networking / ports#
--port, -p <int>: change the port (useful if8765is in use)--host, -H <host>:default:
127.0.0.1, which no other machine can reach0.0.0.0: accept connections on every interface. This is what a compute node needs when the tunnel terminates on a different machine, such as a cluster login nodea non-loopback bind has no authentication: every user who can reach the port reads the dataset (see Security, privacy, CORS, and networking)
Browser behavior / output#
--no-browser: don’t auto-open a browser tab--quiet, -q: suppress progress banners--verbose, -v: debug logging (useful when reporting bugs)
Dataset and launch choices#
--latent-key KEY: theadata.obsmlatent space (scVI, PCA) the outlier filter measures in, never a 2D/3D layout. Without it the store’s ownlatentKeyis used, else none: the filter then waits for one chosen in Data mapping (Outlier filtering (per categorical field))--dataset-name NAME: exact display name; defaults to the store’suns['cellucid']['name'], required when it has none--dataset-id ID: exact stable identifier; defaults to the store’suns['cellucid']['id'], required when it has none--obs-key KEY: show only thisadata.obscolumn; repeat once per column, in order. Every column is shown when it is omitted. Use it to leave out a column Cellucid cannot show, such as adatetime64collection date--vector-field-default FIELD_ID: exact vector field id the overlay opens on--modality NAME: the modality a multimodal dataset (a MuData) opens on; when omitted, the first gene-expression modality (rna,gex, …), else the first--embedding-1d/2d/3d KEY[:COLUMNS]: theobsmarray plotted in that dimension--normalize-embeddings/--no-normalize-embeddings: scale every embedding into the viewer’s coordinate range (the default), or show the coordinates the store holds--centroid-min-points N: the fewest cells a category needs for the outlier filter to measure it (10 when neither this nor the store says otherwise)--knn KEY: theobspneighbor graph the viewer draws, read only when a view draws it--gene-id-column COLUMN,--expression X|raw|layer:NAME: gene names and the expression matrix
A flag overrides the store’s own uns['cellucid']['defaults'] for this
server only; the file is not changed.
Remote server (HPC / cloud): recommended SSH tunnel recipe#
This is the safest way to use Cellucid when your data is on a remote machine.
On the remote machine#
cellucid serve /path/to/data.h5ad --dataset-name "My dataset" --dataset-id my-dataset --no-browser
Keep the default host (127.0.0.1). Leave this running.
On your laptop#
ssh -L 8765:localhost:8765 user@remote-host
Then open:
http://127.0.0.1:8765/?anndata=true
Serving from an HPC compute node#
On a cluster, the tunnel usually cannot terminate on the compute node itself:
you forward through the login node, and the login node opens the second
connection. The server therefore has to accept a connection that does not come
from its own machine, which is what --host 0.0.0.0 means.
# on the compute node
cellucid serve /path/to/data.h5ad --dataset-name "My dataset" --dataset-id my-dataset \
--host 0.0.0.0 --no-browser
# on your laptop
ssh -N -L 8765:<compute-node>:8765 <username>@<login-node>
Then open the loopback Viewer URL the banner printed, not the Machine URL
block:
http://127.0.0.1:8765/?anndata=true
Important
The bind is 0.0.0.0; the address you open is still 127.0.0.1. --host
decides which interfaces the server accepts on, and the browser address is
decided by where the tunnel ends — your own laptop. 0.0.0.0 resolves nowhere.
Whether you need this at all depends on the cluster, not on Cellucid: the Helmholtz Munich / ICB cluster and many other sites accept SSH on login nodes only, so a tunnel has nowhere else to terminate. If your laptop can SSH straight to the machine holding the data, keep the loopback bind instead.
Full guide (edge cases, firewalls, JupyterHub): Remote servers, SSH tunneling, and cloud.
Compute nodes, Slurm batch jobs, tmux, and the tunnel failure table:
HPC clusters: Slurm, compute nodes, and --host 0.0.0.0.
Debugging endpoints (high signal)#
If something looks wrong, open these in a browser (replace <port>):
http://127.0.0.1:<port>/_cellucid/health(is the server alive?)http://127.0.0.1:<port>/_cellucid/info(server config + version)http://127.0.0.1:<port>/_cellucid/datasets(the dataset id and name, the store’s route under_cellucid/anndata/, and the launch choices)http://127.0.0.1:<port>/_cellucid/protocol(which wire capabilities does thiscellucidaccept?)
Edge cases (common)#
Port already in use: the requested port fails. Choose another explicit port, or use
--port 0to request one operating-system-assigned port.--knn connectivitieson an object with no graph: startup fails in step 3 namingobsp['connectivities']instead of serving a dataset with no edges.No
uns['cellucid']identity and no--dataset-name/--dataset-id: startup fails asking for both.Viewer source blocked: startup fails instead of substituting a local generation; see Troubleshooting (viewing).
Troubleshooting#
Start here: Troubleshooting (viewing).