Running a shared server

noodlelab serve on your own machine needs no setup: no logins, one local user. This page is for a server other people use.

Which pages may use the server

A browser lets any website send requests to a server on your machine, so the server checks where they come from:

  • Without logins (the default on your own machine), it answers only to this machine’s names: localhost, a loopback address and the --host it was started with. With --host 0.0.0.0 --auth none it also answers to IP addresses, so reach it from other machines as http://192.168.1.5:8000, not by a host name. This stops DNS rebinding, where another site points its own name at your machine.

  • Pages from other websites cannot open the editor’s WebSocket or save, run, upload or delete anything: those requests must come from the editor’s own page. On a shared server, the page’s host name must be the one the request was sent to (Host, or X-Forwarded-Host from a reverse proxy that rewrites Host).

Programs (the CLI, the MCP server, scripts) send no Origin header and are not affected.

Remote storage

Admins can mount S3, Google Cloud Storage, Azure, SFTP or a network share under a short name that graphs use in file paths (lab-s3://2026/run1.csv), for every workspace or only some. Remote storage explains how. A folder on the server itself is a mount too, with the URL local:///data/shared: on a shared server this is the only way to reach files outside a workspace.

Slurm clusters

A shared server can send runs to a Slurm cluster as batch jobs. Turn it on under Admin → Cluster: set the partition, account, time limit and resources, add setup lines such as module load, and press Test connection. People who may run graphs then get a Run on menu next to Run, with the choices On the server and On the cluster.

A cluster run looks the same in the editor: progress, previews and reports show up as usual, with the job state (Slurm job 1234 · pending) until it starts, and Stop cancels the job.

It needs:

  • the workspaces on a file system shared with the compute nodes;

  • noodlelab installed where the jobs run;

  • a server that can run sbatch, directly or through ssh.

docs/slurm.md explains the setup and what a job looks like.

Runs and memory

Each run executes in a process of its own, not in the server’s. When a run needs more memory than there is, it fails on its own: the node that was running shows Ran out of memory in node ‘Load table’ [3]: the run used 3.8 GB, over its limit of 3.6 GB, the run’s record says the same, and the server and everyone else’s runs carry on. (A run in the server’s process could not do this: when memory runs out, the system kills the process that holds it, and with it the server, or the whole container.)

  • The limit. A run is stopped just before it uses more than run_memory_mb (Admin → Settings). Left at 0, it is the memory noodlelab may use, its container’s limit or else the machine’s, less 10 % (at least 512 MB) kept for the server. Processes a run starts count towards it. If a run takes memory faster than it is measured (five times a second), the system stops the run’s process instead, never the server’s, and the node says so.

  • The spare process. A process with the node packs already loaded waits for the next run, so runs start as quickly as before. It takes memory while it waits: roughly what the server itself takes with the same packs. When two runs start at the same moment, the second waits a few seconds for a process of its own to load them.

  • Results between runs. A run’s process ends with the run, so what it computed is kept in its checkpoints (checkpoints_enabled), not in the server’s memory. Rerunning reads results back from disk, and previews while editing use checkpoints of up to 32 MB. Results that were quick to compute and larger than that are computed again.

  • Turning it off. isolated_runs (Admin → Settings) off runs graphs in the server’s process again: reruns reuse results from memory, but a run that runs out of memory stops the server. NOODLELAB_ISOLATED_RUNS=0 turns it off for the whole server.

Runs from the command line (noodlelab run, verify) and from an agent’s MCP server already have processes of their own, and are unchanged. The Resources panel of a run shows its own process (Run process), with the limit.

In Docker, give the container a memory limit (mem_limit in docker-compose.yml): runs then stop at that limit, less the server’s share, rather than at the machine’s. As a systemd service, set OOMPolicy=continue in the unit: by default systemd stops the whole service once the system has killed any of its processes for memory, a run’s included.

Docker

You need Docker and git.

Step 1. Get the code.

git clone https://github.com/nkalis/noodlelab.git
cd noodlelab

Step 2. Make an admin token (your admin password).

echo "NOODLELAB_ADMIN_TOKEN=$(openssl rand -hex 32)" > .env

Step 3. Start it. This runs the released image; add --build to build it from the code instead.

docker compose up -d

Step 4. Open http://localhost:8000 and sign in with the token from .env (cat .env shows it). Create a token for each person under Admin → Users.

Later

  • Update: from the editor (see Updates), or docker compose pull && docker compose up -d

  • Stop: docker compose down (your data stays in the noodlelab-data volume)

  • Logs: docker compose logs -f

Options

None of these is needed for the steps above.

  • Another port: add NOODLELAB_HOST_PORT=8080 to .env.

  • Existing project folders: uncomment the /srv/projects volume line in docker-compose.yml, then add the folder as a workspace at /admin.

  • The real version number in provenance records: start with make docker-up instead of step 3 (needs make and uv). Otherwise the build reports 0.0.0.

  • The image includes Claude Code for the Agent panel (see below).

  • A public demo: set NOODLELAB_DEMO=1 for guests only, one run at a time and a 25 MB file limit. render.yaml deploys it on Render’s free plan.

Updates

noodlelab looks for new versions of itself and of its official plugins on PyPI once a day (Admin → Settings: check_for_updates, update_check_hours, update_prereleases). When there are some, the plugin light in an admin’s editor turns amber. File → Check for updates… (or Admin → System) shows what is new, with a link to the release notes, and a preview of every package the update would change. Update installs it and restarts the server once no runs are active; every open editor reloads into the new version by itself, and unsaved changes are kept as drafts. auto_update installs updates without asking: official plugins only, or noodlelab too.

How it installs depends on how noodlelab runs; the dialog says which:

  • A virtualenv or a uv tool install: the update goes into that environment, and the server restarts into it. History → Roll back puts the previous versions back. The same from a terminal: noodlelab update.

  • Docker: an update is a new image. noodlelab pulls ghcr.io/nkalis/noodlelab:<version>-<tier>, then a short-lived helper container replaces this one: same name, volumes, ports, networks and settings, on the new image. If the new container does not report healthy within three minutes, the old one is started again and the editor says why. Plugins you installed are installed again from the lockfile when the new container starts.

    This needs the Docker socket, and the socket gives the container control of Docker on the host, as root would have. So it is off until you turn it on, in docker-compose.yml: mount /var/run/docker.sock, set NOODLELAB_DOCKER_UPDATES: "1", and add the socket’s group (stat -c %g /var/run/docker.sock) under group_add. Without it, the dialog shows the commands to run instead. An image you built yourself (docker compose up --build) is updated by building it again.

  • uvx, a source checkout, or an environment noodlelab cannot write to: the dialog shows the command to run.

The Agent panel on a shared server

The Agent panel runs Claude Code or Codex CLI as the server’s user, so it is offered to admins only when logins are on, never on a demo server (NOODLELAB_DEMO=1), and only while Admin → Settings → agent terminal is on. Each agent session gets an API token of its own, revoked when the session ends.