💬 Feedback
v0.1.13 · Documentation

Run Saturn Lab

Turn an existing folder of Jupyter notebooks into a clear visual board. Pull two public Docker images, point at your folder, and start mapping the project.

Install

Requirements: Docker 20+ and Docker Compose v2. No source checkout needed — the images are public on GitHub Container Registry.

# 1 · grab the compose file + env template
$ curl -L -O https://saturnlab.pizner.com/downloads/docker-compose.prod.yml
$ curl -L -o .env https://saturnlab.pizner.com/downloads/saturn.env.example

# 2 · set at least SECRET_KEY in .env
$ nano .env

# 3 · launch and print the actual clickable address
$ docker compose -f docker-compose.prod.yml up -d && \
  SATURN_PORT="$(docker compose -f docker-compose.prod.yml port frontend 80 | tail -n 1 | sed 's/.*://')" && \
  echo "Saturn Lab → http://localhost:${SATURN_PORT}"

The command asks Docker for the resolved port, including values loaded from .env, and prints the correct clickable URL.

Open Saturn Lab at http://localhost → (default port)

That pulls ghcr.io/marekpizner/saturn-backend and …/saturn-frontend and starts everything: the web UI, the API, PostgreSQL, and a built-in JupyterLab.

Bring your own notebooks

Set NOTEBOOKS_PATH to an existing notebook directory. Saturn discovers .ipynb files recursively and opens the host files directly — no import directory and no notebook copies inside Docker. After login, the repository opens automatically without asking for a project name. If Saturn finds a root requirements.txt, it asks before installing anything into the persistent project environment used by Jupyter and pipeline runs.

Review before confirming. Python package installation can execute package build code. Install requirements.txt only when you trust the repository.
# .env
NOTEBOOKS_PATH=/Users/me/projects/my_analysis
# open an existing project → notebooks appear within a few seconds

Not reachable after up?

Almost always a leftover from a previous failed start (e.g. a port was busy). Reset cleanly:

$ docker compose -f docker-compose.prod.yml down
$ docker compose -f docker-compose.prod.yml up -d

If port 80 (or 8888) is taken, set PORT / JUPYTER_PORT in .env and try again.

The deployment files are hosted directly on this public website and pinned to the documented image version. You never need source-repository access. Source checkouts also include optional browser-opening launchers for macOS, Linux, and Windows.

Product tour

The maintained interface previews on the home page mirror the current product controls. The workflow is deliberately small:

1 · Discover
Open in place
Existing notebooks and supported data files appear without an import copy. A root requirements file always waits for confirmation.
2 · Organize
Lane · Change
Move a notebook with the explicit card control. Resize cards, multi-select them, or use the labeled layout actions.
3 · Run
Trace results
Open the real host file in Jupyter, connect execution order, compare run history, and retain model lineage.

See the current interactive product preview →

Configuration

Everything is set in .env — no image rebuild. Change ports freely:

PORT=8080          # web UI        → http://localhost:8080
JUPYTER_PORT=9999  # JupyterLab    → http://localhost:9999
BIND_IP=127.0.0.1: # localhost-only (trailing colon); empty = all interfaces
VariableDefaultDescription
SECRET_KEYDjango secret key — required, set a long random string
DB_PASSWORDsaturn_db_passPostgreSQL password
ADMIN_PASSWORDemptyIf set, auto-creates an admin. Empty → first-run Setup page
JUPYTER_TOKENderivedOptional override; a strong token is derived from SECRET_KEY by default
PORT80Web UI host port
JUPYTER_PORT8888JupyterLab host + container port
BIND_IPemptyBind address. Empty = all interfaces; 127.0.0.1: = localhost only
NOTEBOOKS_PATH.Host notebook workspace read and written directly by Saturn
TAG0.1.13Image version to run (latest for newest)
ALLOWED_HOSTS*Comma-separated allowed hostnames

First login

On a fresh install the first screen is a Setup page — choose the admin username and password (any length). After login, an existing mounted repository opens automatically. Only an empty workspace asks you to create and name a new project.

For unattended deploys, set ADMIN_PASSWORD in .env beforehand and an admin account is created automatically, skipping Setup.

Notebooks

Each node on the board is a Jupyter notebook. A tiny SDK wires data between steps — it's injected automatically, just from saturn import saturn:

from saturn import saturn

df  = saturn.read()            # primary upstream input
dfs = saturn.read_all()        # all inputs → {name: DataFrame}
tok = saturn.secret("API_KEY") # project secret (set in the sidebar)

# ... your work ...

saturn.save(df)                # output → shown on the canvas edge
saturn.log(accuracy=0.94)      # tracked metric
saturn.note("baseline model")  # lineage note

Loose notebooks first appear in the grey Notebooks lane. Files already inside a known lane folder are categorized automatically. Move a card into another category and Saturn moves the real file into that lane's host folder. Select a card to resize it from its edge and corner handles; the size persists.

The folder layout stays simple: lane directories such as processing/, filtration/, aggregation/, experiments/, and notebooks/ live directly below NOTEBOOKS_PATH. Existing folders such as Experiments/ are adopted instead of duplicated. New lane names produce readable folders, and deleting a lane moves every notebook safely into notebooks/.

Select one or more cards with Shift/Ctrl/Cmd, then choose Fork to copy their real notebook content and the connections between them. Existing host files are never overwritten or deleted from the board.

Upgrade & backup

Upgrade

$ docker compose -f docker-compose.prod.yml pull
$ docker compose -f docker-compose.prod.yml up -d

Migrations run automatically; your data volumes are preserved.

Back up your data

$ docker run --rm -v saturn_storage:/data -v $(pwd):/backup \
    alpine tar czf /backup/saturn_storage.tar.gz /data

Existing notebooks remain under NOTEBOOKS_PATH. Notebooks created or forked in Saturn are written into its direct lane folders. Runtime data and models live in the saturn_storage volume; the database in postgres_data. All survive down/up.

Licensing

Early access
Free
Free for noncommercial use under PolyForm Noncommercial 1.0.0: personal projects, study, research, education, nonprofits, and government. Contact me about commercial evaluation or use.

Read the PolyForm Noncommercial 1.0.0 terms. Have thoughts, found a bug, or want to evaluate Saturn commercially? Email marek@pizner.com.

Troubleshooting

Port already in use

Set PORT (and/or JUPYTER_PORT) in .env to a free port and run up -d again.

A node is stuck on "running"

Each notebook has a 10-minute timeout; after that it's marked error. Click the node to read the traceback.

View logs

$ docker compose -f docker-compose.prod.yml logs backend -f