Base image
The ssh and jupyter launch modes need an image that ships an SSH server, JupyterLab and the on-start contract. A plain upstream image such as pytorch/pytorch ships none of that, which is why those two modes are restricted to images derived from the Superheat base.
superheat/base
That is the canonical name, on Docker Hub. The image is also mirrored to
ghcr.io/superheat-software/base with an identical set of tags, pushed by the same build —
so either reference works, and hosts fall back to the mirror if Docker Hub is unreachable.
Prefer the short name.
Both are public, so hosts pull without credentials and you do not need registry credentials on a template that uses the base.
Published tags
| Tag | What it is |
|---|---|
cuda-12.1 | CUDA 12.1 |
cuda-12.4 | CUDA 12.4 |
cuda-12.6 | CUDA 12.6 |
cuda-12.8 | CUDA 12.8 |
latest | the newest CUDA minor |
sha-<12 chars>-<minor> | one immutable tag per build and CUDA minor |
Each cuda-<minor> tag is built from the matching NVIDIA CUDA cuDNN runtime image on Ubuntu 22.04 — cuda-12.4 from nvidia/cuda:12.4.1-cudnn-runtime-ubuntu22.04. Images are amd64 only, which matches the fleet: Superheat hosts are x86_64.
The cuda-<minor> naming is also what the [Automatic] template tag resolves against. Leaving a template's tag as [Automatic] picks the newest tag whose CUDA the chosen machine can run, so one template works across a fleet with mixed drivers. Pin a specific tag when you need a build to be reproducible rather than current.
What it adds
On top of the CUDA runtime: openssh-server, python3 with pip and jupyterlab, tini, gosu, openssl, ca-certificates, curl, git and rsync. The working directory is /workspace and the entrypoint is tini -- /opt/superheat/entrypoint.sh.
The image carries the label com.superheat.base="true". That label is how Superheat recognizes an image as usable for ssh and jupyter modes, and it is inherited by anything you build FROM the base.
What the entrypoint does
Every step is idempotent, so stopping and starting an instance re-runs it safely against the same container.
| Step | Detail |
|---|---|
| 1. Export environment | The container environment is written to /etc/environment and /etc/profile.d/superheat-env.sh, because sshd and Jupyter spawn login shells that do not inherit it. Provisioning secrets are excluded. |
| 2. Install keys | PUBLIC_KEY and SUPERHEAT_SSH_PUBLIC_KEYS are written to /root/.ssh/authorized_keys. |
| 3. Start sshd | Host keys are generated if missing, then sshd listens on container port 22. Key-only; passwords are not accepted. |
| 4. Start Jupyter | Only when JUPYTER_TOKEN is set. A self-signed certificate is generated once and JupyterLab is bound to 0.0.0.0:${JUPYTER_PORT} over HTTPS. |
| 5. Run onstart | SUPERHEAT_ONSTART runs once, guarded by a sentinel. A failure is logged and the container stays up. |
| 6. Hand off | The image's CMD is executed if there is one; otherwise the container idles so it stays running for the port map. |
The environment contract
These variable names are the wire between Superheat and the image. Build on the base and the entrypoint honors them for you; build your own entrypoint and these are what you have to implement.
| Variable | Purpose |
|---|---|
PUBLIC_KEY | A single OpenSSH public key, appended to authorized_keys |
SUPERHEAT_SSH_PUBLIC_KEYS | Newline-separated OpenSSH public keys, appended to authorized_keys |
SUPERHEAT_ONSTART | Bash run once at first start. Sensitive; not persisted to the env files. The bare alias ONSTART is also accepted |
JUPYTER_TOKEN | When set, JupyterLab starts with this as its access token |
JUPYTER_PORT | Jupyter bind port, default 8080 |
JUPYTER_DIR | Jupyter root directory, default /workspace |
JUPYTER_LAB | true for JupyterLab (default), false for the classic notebook |
OPEN_BUTTON_PORT | Informational — the console Open button target port |
CONTAINER_ID | Informational — the workload identifier |
GPU_COUNT | Informational — how many GPUs are assigned |
SUPERHEAT_TCP_PORT_<n> | Informational — the published host port for container port <n> |
Superheat sets these last, after your template environment and any renter overrides, so none of them can be shadowed. See Environment variables.
Building your own image
Inherit from the tag whose CUDA matches what your code needs, install your dependencies, and leave the entrypoint alone:
FROM superheat/base:cuda-12.4
RUN pip install --no-cache-dir torch torchvision transformers
COPY train.py /workspace/train.py
WORKDIR /workspace
Three rules keep the result launchable in ssh and jupyter modes:
- Inherit, do not rebuild. Building from
nvidia/cudaand copying in your own sshd loses thecom.superheat.baselabel and the entrypoint contract along with it. - Do not override
ENTRYPOINT. The base entrypoint is what starts sshd, Jupyter and your on-start script. Overriding it turns ansshtemplate into a container with no way in. - Put your process in
CMD. The entrypoint executesCMDafter provisioning, so a long-running server started that way gets sshd and Jupyter alongside it.
If your image cannot follow those rules — an upstream image you do not control, or one with an entrypoint of its own — use the args launch mode instead. It runs any image's native entrypoint and needs nothing from the base. See Launch modes.
Which tag to inherit from
| Situation | Tag |
|---|---|
| Your framework pins a CUDA minor | the matching cuda-<minor> |
| You want the newest available CUDA and can rebuild | latest |
| You need a build to resolve identically months from now | the sha-… tag |
Pinning a cuda-<minor> tag is the usual answer. latest moves when a new CUDA minor is published, which is fine for a dev image and a poor foundation for a reproducible training run.
Filesystem layout
| Path | Contents |
|---|---|
/workspace | The working directory, and Jupyter's root by default |
/opt/superheat/entrypoint.sh | The provisioning entrypoint |
/var/lib/superheat/onstart.sh | The on-start body as it was executed |
/var/lib/superheat/.onstart-done | The sentinel that stops the on-start script re-running |
/var/log/superheat/onstart.log | On-start output |
/var/log/superheat/jupyter.log | Jupyter output |
/etc/profile.d/superheat-env.sh | The environment as login shells see it |
sshd and Jupyter also stream to the container log, which is what the console shows. See Logs.