Local API and smolvm serve

smolvm serve exposes local machine operations over HTTP. Use it for integrations that need a long-running local REST endpoint or already depend on the older smolvm-sdk client.

For new Node.js and Python applications, use the embedded smolmachines SDK instead. It runs the local engine in-process and can use the same Machine API with smol cloud. It does not require smolvm serve.

Start the local server

Listen on loopback:

smolvm serve start --listen 127.0.0.1:8080

Or listen on a Unix socket:

smolvm serve start --listen "$XDG_RUNTIME_DIR/smolvm.sock"

Generate the OpenAPI specification for the installed version:

smolvm serve openapi

One server to a host, unless you move a second port

Every smolvm serve also binds a guest ingress port, 127.0.0.1:10081 by default, which --listen does not move. A second server on the same host therefore fails at start whatever address it was given:

Error: config operation failed: bind guest rollout ingress: 127.0.0.1:10081: Address already in use

Give the second server its own port with --guest-rollout-port PORT, or SMOLVM_GUEST_ROLLOUT_HOST_PORT, and the two run side by side. A running machine keeps the port it started with.

Operator flags

FlagWhat it does
--cache-disk-dir DIRDirectory of cache disk bases the API may attach, named by file name in cacheDisk on create and never by path. POST /api/v1/machines/:name/cache-disk/publish writes a stopped machine’s cache, base and writes together, into it as a new base under a new name. Unset, the API refuses cache disks
--cache-disk-max-gb GiBMost the bases in --cache-disk-dir, which it requires, may hold: past it the least recently used unattached bases are removed and fetched again when needed. Half that directory’s filesystem by default, and a tenth of the filesystem is always kept free
--allow-nested-virtPermit machines created with nestedVirt. Off by default, because it exposes the host kernel’s nested virtualization to the guest; turning it off again also stops existing nested machines from starting
--egress-watchlist PATHRecord guest traffic to destinations named in a file of <label> dns-sha256:<hex> or <label> ip-sha256:<hex> lines, and block it on a line that ends in block: a matching lookup is answered as nonexistent and a matching connection is dropped. Matches come back as egressSignals on the machine, the file is re-read when it changes, and it applies to virtio-net machines. A TLS server name is matched like a DNS name, and a stratum login is recorded as the signal stratum
--shutdown-grace SECSSeconds in-flight requests get once the server stops accepting connections, five by default and capped at 3600. Connections are refused for the whole grace, so to restart without cutting off a long exec, wait for GET /inflight on the loopback door to report nothing in flight
--mtls-client-cn CNRequire this subject CN on the mTLS client certificate. Only applies when serve TLS is configured; unset, every certificate the client CA signed is accepted for every route
--mtls-allow-peer-blobsWith --mtls-client-cn, also accept other certificates the client CA signed, but only on the peer blob routes under /p2p/, for sibling hosts that fetch cached layers from this one
--local-image-dir DIRDirectory of local images the API may create machines from: docker save or OCI archives and unpacked rootfs directories. A request’s image path must resolve inside it; unset, the API accepts registry images only
--restore-cache-entries NRestored checkpoints kept extracted after their machines are gone, so restoring one again reuses it; 32 by default, 0 keeps none
--restore-cache-gib GiBSpace those kept checkpoints may hold together, 256 GiB by default
--prepared-checkpoint-cache-gib GiBSpace for captured checkpoints kept on this host, so restoring one here skips the download; 64 GiB by default, and SMOLVM_PREPARED_CHECKPOINT_CACHE_MAX_BYTES takes precedence when set
--cors-origin ORIGINAllowed CORS origin, repeatable; localhost:8080 and localhost:3000 by default
--json-logsWrite logs as structured JSON for a log aggregator

Selected HTTP endpoints

MethodPathOperation
POST/api/v1/machinesCreate a machine
GET/api/v1/machinesList machines
GET/api/v1/machines/:nameGet one machine
POST/api/v1/machines/:name/startStart a machine
POST/api/v1/machines/:name/stopStop a machine
DELETE/api/v1/machines/:nameDelete a machine
PUT/api/v1/machines/:name/portsReplace a machine’s published ports, live while it runs; a taken host port answers 409 PORT_IN_USE
GET, POST, DELETE/api/v1/machines/:name/mountsList, add or remove host directories mounted into a running machine; POST takes source, target and readonly, DELETE takes ?target=, and a stopped machine answers 409
POST/api/v1/machines/:name/execExecute a command
POST/api/v1/machines/:name/exec/streamStream execution over SSE
PUT/api/v1/machines/:name/files/*pathUpload a file: up to 1 GiB with a Content-Length, 100 MiB without, 413 beyond. ?uid=, ?gid= and ?mode= (octal) set its owner and permissions; with no owner it belongs to the workload’s user
GET/api/v1/machines/:name/files/*pathDownload a file
GET/api/v1/machines/:name/logsStream logs over SSE
POST/api/v1/machines/:name/images/pullPull an OCI image
POST/api/v1/poolsCreate a warm pool of branched workers from a branchable source
GET, DELETE/api/v1/pools/:nameRead or delete a pool
PUT/api/v1/pools/:name/sizeChange how many clean workers a pool keeps ready
POST/api/v1/pools/:name/leasesLease one clean worker
POST/api/v1/pools/:name/leases/:lease/heartbeatExtend a lease
POST/api/v1/pools/:name/leases/:lease/completeFinish a lease; the pool replaces its worker

A warm pool keeps desiredReady workers branched from a source machine and ready to hand out. The source must be started branchable and its workload must reach its branch point by running under smolvm-branch-ready; until it does, the pool stays provisioning. A lease takes an idempotencyKey: repeating a request with the same key and the same body returns the same lease, and the same key with a different body is refused with 409. A lease expires unless heartbeated, 300 seconds by default, and completing it retires the worker while the pool branches a fresh one.

The HTTP API covers common lifecycle, execution, file, image, volume, export, and branch operations. It does not have complete parity with every CLI command or interactive CLI behavior. Use the generated OpenAPI document as the wire-level reference for your installed release.

A branch request waits for the source up to 60 seconds plus 20 seconds per GiB of its memory, at most 540 seconds, then answers a retryable 503.

A start takes branchable, branchPoolSize and cudaVramLimitMib in its JSON body or its query string; when both carry one, the query string wins.

A create body is validated strictly: an unknown field is refused with 422 and a message naming the field and listing the ones it accepts, and so is a known field of the wrong type. A field this release does not have is refused rather than dropped in silence.

Choose an integration

Use npm or PyPI package smolmachines for new Node.js and Python applications.

  • The local engine is embedded through native bindings
  • No separate server process is required
  • The API can target local machines or smol cloud
  • This is the current SDK path in the smol repository

See SDK Quick Start and Use SDK in Local for installation and code examples.

Legacy REST client: smolvm-sdk

The older smolvm-sdk repository holds npm, Python and Go REST clients for a running smolvm serve process. Its npm and Python packages are named smolvm in source, but the smolvm on npm is an empty placeholder and the smolvm on PyPI is a different project, so neither registry installs these clients.

Use this path when:

  • An existing integration already uses the local REST API
  • A separate local runtime process is part of the deployment design
  • The application language cannot use the embedded Node.js or Python packages

Do not confuse the package names:

PackageConnection modelRecommended use
smolmachinesEmbedded local engine; optional cloud transportNew Node.js and Python applications
The smolvm-sdk clients, named smolvm in sourceHTTP client to smolvm serveExisting or server-oriented REST integrations

Security and deployment boundary

The local server does not provide authentication. Bind it to loopback or a protected Unix socket. Do not expose port 8080 to an untrusted network.

If another host or user must reach the API, place it behind an authenticated, encrypted proxy and enforce host-level account isolation. Anyone who can call the API can ask the runtime to create machines and execute workloads with the capabilities granted to the server process.

smolvm serve is a per-host runtime API. It is not the standalone smol cloud fleet control plane.

A machine serve starts cannot reach a private, carrier-NAT or loopback address even when its allowedCidrs names one: serve raises the egress floor for every machine it starts, and no allow-list entry lowers it. To lift it, set SMOLVM_EGRESS_FLOOR before starting serve: metadata keeps only the link-local range that holds cloud metadata blocked, and off blocks nothing. Neither is the CLI’s default, which also blocks the host’s loopback.