Machine
One
Machineclass, two backends. The sameMachineAPI from thesmolmachinesSDK (Node) /smolmodule (Python) runs against either an embedded in-process engine (the default — no server to start) or the smolfleet cloud. The backend is selected byConnectOptions:{}/{ target: 'local' }= local,{ target: 'cloud', apiKey }(or theSMOL_CLOUD_TOKENenv var) = cloud. TypeScript methods are async (return Promises); the Python API is synchronous. For the standalone hosted REST API atapi.smolmachines.com, see the Cloud API reference.
A machine is a microVM instance that provides an isolated execution environment.
Creating a Machine
Machine.create(config?, conn?) creates and starts a machine in one call. A name is generated if you omit one.
import { Machine } from 'smolmachines';
// Basic creation (local embedded engine — the default)
const machine = await Machine.create();
// With configuration
const machine = await Machine.create({
name: 'configured-machine',
mounts: [
{ source: '/host/code', target: '/workspace' }
],
resources: {
cpus: 2,
memoryMb: 1024
}
}); Python from smol import Machine, MachineConfig, MountSpec, ResourceSpec
# Basic creation (local embedded engine — the default)
machine = Machine.create()
# With configuration
config = MachineConfig(
name="configured-machine",
mounts=[
MountSpec(source="/host/code", target="/workspace")
],
resources=ResourceSpec(
cpus=2,
memory_mb=1024
)
)
machine = Machine.create(config) Choosing a backend
Pass a ConnectOptions as the second argument. Local is the default; cloud is selected by target: 'cloud' or by supplying an API key (directly or via SMOL_CLOUD_TOKEN).
// Cloud — image is required on the cloud target
const machine = await Machine.create(
{ image: 'python:3.12', resources: { cpus: 2, memoryMb: 1024 } },
{ target: 'cloud', apiKey: 'smk_...' }
);
console.log(await machine.url()); // public ingress URL (cloud) from smol import Machine, MachineConfig, ConnectOptions
# Cloud — image is required on the cloud target
machine = Machine.create(
MachineConfig(image="python:3.12"),
ConnectOptions(target="cloud", api_key="smk_..."),
)
print(machine.url()) # public ingress URL (cloud) Configuration Options
MachineConfig fields:
| Option | Type | Default | Description |
|---|---|---|---|
name | string | auto-generated | Machine name / identifier |
image | string | — | Base image. Required for cloud; optional for local (you typically run(image, …) per command instead) |
mounts | MountSpec[] | none | Host directories to mount (local) |
ports | PortSpec[] | none | Host→guest port mappings (local) |
resources | ResourceSpec | engine default | CPU / memory / disk / network — see Resources |
persistent | boolean | false | Keep the machine record after the process exits (local) |
autoStopSeconds / auto_stop_seconds | number | none | Auto-stop after N idle seconds (cloud) |
ttlSeconds / ttl_seconds | number | none | Delete the machine after N seconds (cloud) |
forkable | boolean | false | Start as a live-RAM fork base so the machine can be cloned with fork() (cloud) |
Lifecycle Methods
state()
Returns the current state as a string: "created", "running", or "stopped".
const s = await machine.state(); // "running" Python s = machine.state() # "running" url()
Public ingress URL for the machine’s first published port (cloud). Returns null/None on the local target, before a host port is allocated, or when no port is published.
const url = await machine.url(); // "https://…" or null Python url = machine.url() # "https://…" or None stop()
Stops the machine. The machine can be restarted.
TypeScriptawait machine.stop(); Python machine.stop() delete()
Stops the machine and deletes its storage. This is permanent.
TypeScriptawait machine.delete(); Python machine.delete() connect()
Attach to an existing machine instead of creating one — to drive a machine made elsewhere (another process, the console, the REST API). Locally this re-opens a persisted machine by name (starting it if stopped); on cloud it looks up the machine by id (mach-…).
const machine = await Machine.connect('my-machine'); Python machine = Machine.connect("my-machine") Execution Methods
exec and run run against the local embedded engine by default — there is no metering or billing on the local path.
exec()
Execute a command directly in the microVM. The command is an array of argv (not a shell string).
TypeScriptconst result = await machine.exec(['ls', '-la', '/']);
// With options
const result = await machine.exec(['long-running-task'], {
timeout: 60, // seconds
env: { MY_VAR: 'value' },
workdir: '/tmp'
}); Python from smol import ExecOptions
result = machine.exec(["ls", "-la", "/"])
# With options
result = machine.exec(
["long-running-task"],
ExecOptions(
timeout=60, # seconds
env={"MY_VAR": "value"},
workdir="/tmp",
),
) run()
Pull an image (if needed) and run a command in a container of it (local).
TypeScriptconst result = await machine.run(
'python:3.12-alpine',
['python', '-c', 'print("Hello")']
);
// With options
const result = await machine.run(
'node:22-alpine',
['node', 'script.js'],
{
timeout: 30,
env: { NODE_ENV: 'production' }
}
); Python from smol import ExecOptions
result = machine.run(
"python:3.12-alpine",
["python", "-c", "print('Hello')"]
)
# With options
result = machine.run(
"node:22-alpine",
["node", "script.js"],
ExecOptions(timeout=30, env={"NODE_ENV": "production"}),
) execStream() / exec_stream()
Execute a command and stream its output live as it is produced (local). Yields events: { kind: "stdout" | "stderr", data }, { kind: "exit", exitCode }, { kind: "error", message }.
for await (const ev of machine.execStream(['pip', 'install', 'numpy'])) {
if (ev.kind === 'stdout' || ev.kind === 'stderr') process.stdout.write(ev.data);
else if (ev.kind === 'exit') console.log('exit', ev.exitCode);
} Python for ev in machine.exec_stream(["pip", "install", "numpy"]):
if ev["kind"] in ("stdout", "stderr"):
print(ev["data"], end="")
elif ev["kind"] == "exit":
print("exit", ev["exit_code"]) Files
readFile() / read_file()
Read a file from the machine. Returns the raw bytes.
TypeScriptconst buf = await machine.readFile('/tmp/out.bin'); // Buffer Python data = machine.read_file("/tmp/out.bin") # bytes writeFile() / write_file()
Write a file into the machine. data may be a string or bytes; an optional file mode may be supplied.
await machine.writeFile('/workspace/script.py', 'print("hi")');
await machine.writeFile('/workspace/run.sh', '#!/bin/sh\necho hi\n', 0o755); Python machine.write_file("/workspace/script.py", 'print("hi")')
machine.write_file("/workspace/run.sh", "#!/bin/sh\necho hi\n", 0o755) Images
pullImage() / pull_image()
Pull an OCI image into the machine’s storage (local). Returns an ImageInfo.
const info = await machine.pullImage('python:3.12-alpine'); Python info = machine.pull_image("python:3.12-alpine") listImages() / list_images()
List cached OCI images (local).
TypeScriptconst images = await machine.listImages(); Python images = machine.list_images() fork()
Fork a running, forkable machine into a new clone via copy-on-write live RAM + disks (cloud). The clone inherits the golden’s warm in-memory state; forks are fast (~tens of ms). The golden must have been created with forkable: true.
const golden = await Machine.create(
{ image: 'python:3.12', forkable: true },
{ target: 'cloud', apiKey: 'smk_...' }
);
const clone = await golden.fork('clone-1'); Python golden = Machine.create(
MachineConfig(image="python:3.12", forkable=True),
ConnectOptions(target="cloud", api_key="smk_..."),
)
clone = golden.fork("clone-1") Best Practices
Use Context Managers (Python)
Python’s Machine is a context manager — it deletes the machine on exit:
with Machine.create(MachineConfig(name="worker")) as machine:
machine.exec(["whoami"])
# Automatically deleted, even on error Use try/finally (TypeScript)
There is no helper wrapper — clean up with try/finally:
const machine = await Machine.create({ name: 'worker' });
try {
await machine.exec(['whoami']);
} finally {
await machine.delete();
} Reuse Machines
For multiple commands, reuse the same machine rather than creating one per command:
// Good — one machine, many commands
const machine = await Machine.create({ name: 'worker' });
try {
for (const task of tasks) {
await machine.exec(task.command);
}
} finally {
await machine.delete();
}