set_start_cmd() is a template-build setting. The command runs during template build before snapshotting. Machines created from the template restore that snapshotted running state instead of rerunning the command at machine create time. Create-time environment variables passed to Machine.create(envs=...) are not visible to the template start command. Use set_runtime_envs() for defaults that should be present in machines launched from the template.

Start command

from nullspace import Template, wait_for_port

builder = (
    Template()
    .from_python_image("3.12")
    .pip_install(["fastapi", "uvicorn"])
    .copy("./app.py", "/workspace/app.py")
    .set_workdir("/workspace")
    .set_start_cmd(
        "uvicorn app:app --host 0.0.0.0 --port 8080",
        readiness=wait_for_port(8080),
    )
)
// Note: the TypeScript setStartCmd takes only the command. Configure the
// readiness probe via the Python SDK, CLI, or HTTP API.
const builder = client.templates
  .builder()
  .fromPythonImage("3.12")
  .pipInstall(["fastapi", "uvicorn"])
  .copy("./app.py", "/workspace/app.py")
  .setWorkdir("/workspace")
  .setStartCmd("uvicorn app:app --host 0.0.0.0 --port 8080");
nullspace template build \
  --from-python-image 3.12 \
  --pip-install fastapi,uvicorn \
  --copy-src ./app.py --copy-dst /workspace/app.py \
  --set-workdir /workspace \
  --start-cmd "uvicorn app:app --host 0.0.0.0 --port 8080" \
  --ready-port 8080 \
  --name api-template
# start_cmd is top-level; the readiness probe lives under runtime_config.
# COPY needs an uploaded build_context, so the full body is large — see the
# API reference for the complete CreateTemplateRequest schema.
curl -N -X POST "${NULLSPACE_API_URL}/v1/templates/build" \
  -H "Authorization: Bearer ${NULLSPACE_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "api-template",
    "base_image": "python:3.12",
    "steps": [
      {"action": "pip_install", "packages": ["fastapi", "uvicorn"]},
      {"action": "workdir", "path": "/workspace"}
    ],
    "start_cmd": "uvicorn app:app --host 0.0.0.0 --port 8080",
    "runtime_config": {"readiness": {"kind": "tcp", "port": 8080}}
  }'
See the API reference for the full template build request schema, including build_context uploads for COPY steps.

Readiness helpers

from nullspace import (
    wait_for_file,
    wait_for_port,
    wait_for_process,
    wait_for_timeout,
    wait_for_url,
)

wait_for_port(8080)
wait_for_url("http://127.0.0.1:8080/health")
wait_for_file("/tmp/ready")
wait_for_process("server")
wait_for_timeout(5_000)
wait_for_url() is an in-guest local HTTP probe. It only accepts http:// URLs on localhost, 127.0.0.1, or 0.0.0.0; the URL must include an explicit port and cannot include query strings, fragments, or embedded credentials.

Warm Pools

Template warm pools use this same start-ready snapshot as their startup contract. Put service boot inputs that must exist before pooling in template runtime defaults with set_runtime_envs(). Create-time envs, volume mounts, cwd overrides, desktop settings, custom networking, non-destroy timeout policy, and auto-resume are not visible to the prewarmed service boot path. Use Template Warm Pools for the checkout modes and fallback behavior.

Ready command

builder = builder.set_ready_cmd("curl -fsS http://127.0.0.1:8080/health")
nullspace template build \
  --from-python-image 3.12 \
  --ready-cmd "curl -fsS http://127.0.0.1:8080/health" \
  --name api-template
# A command readiness probe is a runtime_config.readiness entry of kind=command.
curl -N -X POST "${NULLSPACE_API_URL}/v1/templates/build" \
  -H "Authorization: Bearer ${NULLSPACE_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "api-template",
    "base_image": "python:3.12",
    "runtime_config": {
      "readiness": {"kind": "command", "command": "curl -fsS http://127.0.0.1:8080/health"}
    }
  }'