Predefined image helpers

Template().from_ubuntu_image("22.04")
Template().from_debian_image()
Template().from_python_image("3.12")
Template().from_node_image("20")
Template().from_bun_image("1.2")
// Note: the TypeScript builder does not expose fromBunImage yet.
client.templates.builder().fromUbuntuImage("22.04");
client.templates.builder().fromDebianImage();
client.templates.builder().fromPythonImage("3.12");
client.templates.builder().fromNodeImage("20");
nullspace template build --from-ubuntu-image 22.04 --name demo
nullspace template build --from-debian-image --name demo
nullspace template build --from-python-image 3.12 --name demo
nullspace template build --from-node-image 20 --name demo
nullspace template build --from-bun-image 1.2 --name demo
# Predefined helpers resolve to a base_image OCI ref server-side. Builds stream
# over SSE — see the API reference for the full request schema.
curl -N -X POST "${NULLSPACE_API_URL}/v1/templates/build" \
  -H "Authorization: Bearer ${NULLSPACE_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"name": "demo", "base_image": "python:3.12"}'
from_bun_image() requires an explicit Bun version and maps to oven/bun:<version>.

Existing templates and custom images

Template().from_template("base")
Template().from_image("registry.example.com/team/app:1.0")
Template().from_base_image("ubuntu:22.04")
client.templates.builder().fromTemplateRef("base");
client.templates.builder().fromImage("registry.example.com/team/app:1.0");
client.templates.builder().fromImage("ubuntu:22.04");
nullspace template build --from-template base --name demo
nullspace template build --from-image registry.example.com/team/app:1.0 --name demo
nullspace template build --from-base-image ubuntu:22.04 --name demo
# Custom and base images both populate base_image. Builds stream over SSE —
# see the API reference for the full request schema.
curl -N -X POST "${NULLSPACE_API_URL}/v1/templates/build" \
  -H "Authorization: Bearer ${NULLSPACE_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"name": "demo", "base_image": "registry.example.com/team/app:1.0"}'

Dockerfile

Build from a Dockerfile on disk:
builder = Template().from_dockerfile("./Dockerfile")
build = Template.build(builder, name="demo-from-dockerfile")
nullspace template build --from-dockerfile ./Dockerfile --name demo-from-dockerfile
# The HTTP API takes inline Dockerfile content via the "dockerfile" field
# (the SDK/CLI read the file for you). Builds stream over SSE — see the
# API reference for the full request schema.
curl -N -X POST "${NULLSPACE_API_URL}/v1/templates/build" \
  -H "Authorization: Bearer ${NULLSPACE_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"name": "demo-from-dockerfile", "dockerfile": "FROM ubuntu:22.04\nRUN apt-get update && apt-get install -y jq"}'
The TypeScript SDK builds Dockerfiles from inline content with fromDockerfileContent(...); it does not read a Dockerfile path for you.
Build from inline Dockerfile content:
dockerfile = """
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y jq
"""

builder = Template().from_dockerfile_content(dockerfile)
const dockerfile = `FROM ubuntu:22.04
RUN apt-get update && apt-get install -y jq`;

const builder = client.templates.builder().fromDockerfileContent(dockerfile);
# Inline Dockerfile content goes in the "dockerfile" field. Builds stream over
# SSE — see the API reference for the full request schema.
curl -N -X POST "${NULLSPACE_API_URL}/v1/templates/build" \
  -H "Authorization: Bearer ${NULLSPACE_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"name": "demo", "dockerfile": "FROM ubuntu:22.04\nRUN apt-get update && apt-get install -y jq"}'
Dockerfile builds use BuildKit. BuildKit-backed builds evaluate the Dockerfile, then import the resulting OCI image into a Nullspace rootfs and snapshot. build_backend="native" remains valid for non-Dockerfile declarative/OCI requests and historical build filters, but Dockerfile input with the native backend is rejected.

Compatibility

BuildKit-backed Dockerfile builds use BuildKit for upstream Dockerfile evaluation and then retain the resulting OCI runtime metadata during import.
FeatureBuildKit behavior
Multi-stage builds and COPY --fromHandled by BuildKit.
HeredocsSupported when the configured BuildKit Dockerfile frontend supports them.
RUN --mountBuildKit cache, bind, tmpfs, secret, and SSH mounts require matching Nullspace request support and policy.
Named contextsRequire BuildKit build-context support.
.dockerignore parityUses the uploaded BuildKit context path.
LABELRetained as template/build OCI metadata.
EXPOSERetained as metadata and UI/API hints; it does not automatically publish a port.
VOLUMERetained as metadata only; it does not create persistent Nullspace volumes.
HEALTHCHECKRetained as metadata. Shell/exec healthchecks map to command readiness when safe; unsupported forms remain metadata only.
Docker’s BuildKit and Dockerfile references define the upstream behavior: BuildKit, Dockerfile reference, and build contexts.

OCI import

When a custom image or external Dockerfile FROM imports an OCI image, Nullspace imports these runtime defaults:
OCI dataImport behavior
EnvKEY=VALUE entries become runtime env defaults; malformed entries are skipped.
UserTrimmed non-empty values become the default user. Supported Linux forms include names, numeric ids, and user/group combinations.
WorkingDirTrimmed non-empty values become the default workdir.
Entrypoint and CmdArrays are concatenated and shell-escaped into one start command.
LabelsRetained as template/build OCI metadata.
Exposed portsRetained as metadata and hints; they do not publish traffic by themselves.
VolumesRetained as metadata only; no persistent Nullspace volume is created automatically.
HealthcheckRetained as metadata and mapped to command readiness when semantics are safe.
LayersRaw tar, gzip, Docker tar/gzip, nondistributable variants, and zstd OCI layers are supported.
Filesystem entriesRegular files, directories, symlinks, hard links, and OCI whiteouts are applied when paths stay inside the rootfs. Mode bits and uid/gid ownership are preserved in the generated ext4 rootfs.
PlatformLinux manifests are selected for the machine host architecture. Manifests with os.version or os.features are rejected.
Nullspace does not import stop signal, shell metadata, or arbitrary annotations into runtime config today. Special files such as sockets, FIFOs, block devices, character devices, sparse tar entries, and unknown tar entries are rejected.

Private registries

builder = (
    Template()
    .from_image("registry.example.com/team/app:1.0")
    .set_base_image_auth(username="robot", password="token")
)
nullspace template build \
  --from-image registry.example.com/team/app:1.0 \
  --registry-username robot \
  --registry-password token \
  --name demo
# base_image_auth carries request-time credentials (kind=basic for
# username/password). Builds stream over SSE — see the API reference.
curl -N -X POST "${NULLSPACE_API_URL}/v1/templates/build" \
  -H "Authorization: Bearer ${NULLSPACE_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "demo",
    "base_image": "registry.example.com/team/app:1.0",
    "base_image_auth": {"kind": "basic", "username": "robot", "password": "token"}
  }'
The TypeScript SDK does not expose private registry auth yet; use the Python SDK, CLI, or HTTP API for authenticated base-image pulls.
Helpers also exist for GCP Artifact Registry and AWS ECR.