Packages in your runtime

Every runtime starts from its Environment image, which already contains the packages you listed when you built it. Anything you install on top of that with pip inside a running session lands in a durable, per-Environment user site on your workspace, so it is still importable the next time you start a runtime on the same Environment. Nothing to configure: a plain

!pip install rich

in a cell, or pip install rich in the terminal or over SSH, is enough. Stop the runtime, start a new one on the same Environment, and import rich works.

What persists, and what does not

Where it livesSurvives a stop?Notes
Files under ~/workspaceYesYour workspace filesystem.
pip install <pkg> (kernel, terminal, SSH)Yes, on the same EnvironmentInstalled into ~/workspace/.ns-cache/pyuser/<environment>/py<X.Y>-<cpu or cuX.Y>.
pip's download cache, XDG_CACHE_HOMEYesUnder ~/workspace/.ns-cache/, shared by every runtime of the workspace.
Hugging Face hub cache (HF_HOME)NoOn the runtime's local NVMe at $NS_SCRATCH/hf, wiped when the runtime stops. Models re-download on the next run. See Large datasets and models.
sudo pip install …NoRuns as root and installs into the image itself, which is discarded with the runtime. Drop the sudo.
apt-get install …, edits under /opt, /etc, /usrNoThe image is ephemeral. Put system packages in your Environment (System packages) so they are baked in.
~/.local, ~/.cache, /tmp, /scratchNoThe home directory and the local NVMe scratch disk are wiped with the runtime.

A runtime started with Workspace files switched off (Runtime settings) mounts no workspace, so nothing in the Yes rows survives it: files, installs and caches all stay on the ephemeral disk.

Persisted packages are keyed by the Environment, its Python X.Y and its CUDA line (cpu for a CPU image), so a runtime on a different Environment, or on the same Environment rebuilt with a different Python or CUDA release, starts clean. That is deliberate: wheels are bound to the interpreter ABI and the CUDA libraries they were built against.

A runtime on the Provider default environment (the cloud's stock image, no Environment) keeps its own user site, keyed provider-default-<cloud> plus the host's Python and CUDA line, so installs there persist for the next provider-default runtime on the same cloud and never leak into an Environment's.

The .ns-cache directory is managed by the platform. It is hidden from the Files page and cannot be addressed through the Files or notebook APIs; you will see it on the runtime, and you can delete a stale user site from the terminal (rm -rf ~/workspace/.ns-cache/pyuser/<environment>) if you want a clean slate for that Environment.

Save them as an Environment

Persisted installs are a convenience, not a recipe. When you stop a runtime that has packages installed outside its image, Neural Studio offers to save them as an Environment (the same Save as Environment action as the notebook menu). Accepting runs pip freeze on the runtime and creates a new Environment whose build bakes those packages into the image, so every future runtime on any machine starts with them and your collaborators can launch the same thing. Do this once you know which packages you want to keep.

How it works (and the sharp edges)

The runtime exports PYTHONUSERBASE pointing at the per-Environment directory above and PIP_USER=1, so pip install behaves as pip install --user and Python's user site is on sys.path from the first import.

  • The user site shadows the image. If you pip install a package the image already ships, the new version wins for this Environment until you remove it from the user site. That is what makes upgrading a package stick; it also means a bad upgrade follows you to the next runtime. pip uninstall <pkg> removes the user-site copy and the image's copy is visible again.
  • Virtual environments. pip refuses --user inside a virtualenv. If you create one, either create it with python -m venv --system-site-packages .venv, or set PIP_USER=0 for that pip (PIP_USER=0 .venv/bin/pip install …). uv is unaffected; it ignores PIP_USER.
  • Escape hatch. PIP_USER=0 pip install <pkg> installs into the image for this session only, exactly as before. --target and --prefix installs also need PIP_USER=0.
  • Console scripts. Executables from user-site installs land in $PYTHONUSERBASE/bin. Terminal and SSH shells have it on PATH; from a notebook cell, run them as !python -m <module>.
  • First import can be slower. The user site lives on the workspace filesystem, so the first import of a large package after a fresh start reads it over the network once; later imports are served from the runtime's local cache.
  • Storage pool. Everything under .ns-cache counts toward your account's storage pool like any other file.

Large datasets and models

Your workspace at ~/workspace is durable and counts against your account's storage pool (shared by all your workspaces). The runtime also has a local NVMe scratch disk at $NS_SCRATCH (/scratch) that is fast, does not count against your pool, and is wiped when the runtime stops. It is sized by the machine you picked: from ~118 GB on the smallest CPU shape to several terabytes on the largest GPU shapes.

Put big, re-creatable data on scratch and keep the workspace for what you want to keep:

DataWhere it should go
Notebooks, code, results, checkpoints you want to keep~/workspace
Training datasets, model downloads, intermediate artifacts$NS_SCRATCH

The Hugging Face cache already points at scratch, so from_pretrained(...) and load_dataset(...) download there and do not consume your pool. Downloads repeat on the next runtime; if you would rather cache a model durably and have the pool space for it, opt in:

import os
os.environ["HF_HOME"] = os.path.expanduser("~/workspace/.hf")  # before the import

Two things to watch:

  • Relative paths write to the workspace. A notebook runs with its own directory inside ~/workspace, so torch.save(model, "ckpt.pt") or a wget into . lands in your pool. Write large intermediates to $NS_SCRATCH explicitly.
  • Importing a dataset copies it into your workspace. A transfer from a storage connection lands in ~/workspace and is checked against your pool's free space before it starts, so a dataset larger than the space left is refused up front. To work with data larger than your pool, read it from your own bucket directly: put the credentials in workspace secrets and use boto3, s3fs or load_dataset(..., streaming=True) from a cell.