Volume Mounts

Share files between the host and machine using volume mounts.

Basic Mount

const machine = await Machine.create({
  name: 'with-mounts',
  mounts: [
    { source: '/host/code', target: '/workspace' }
  ]
});

// Access mounted files
const result = await machine.exec(['cat', '/workspace/script.py']);
console.log(result.stdout);
from smolvm import Machine, MachineConfig, MountSpec

config = MachineConfig(
    name="with-mounts",
    mounts=[
        MountSpec(source="/host/code", target="/workspace")
    ]
)

async with Machine(config) as machine:
    await machine.start()
    result = await machine.exec(["cat", "/workspace/script.py"])
    print(result.stdout)

Read-Only Mounts

Mount directories as read-only for safety:

const machine = await Machine.create({
  name: 'readonly-mount',
  mounts: [
    { source: '/host/data', target: '/data', readonly: true }
  ]
});

// Reading works
const result = await machine.exec(['cat', '/data/config.json']);

// Writing fails
const writeResult = await machine.exec(['touch', '/data/newfile']);
// writeResult.exitCode !== 0
config = MachineConfig(
    name="readonly-mount",
    mounts=[
        MountSpec(source="/host/data", target="/data", readonly=True)
    ]
)

Multiple Mounts

const machine = await Machine.create({
  name: 'multi-mount',
  mounts: [
    { source: '/host/code', target: '/workspace' },
    { source: '/host/data', target: '/data', readonly: true },
    { source: '/host/output', target: '/output' }
  ]
});
config = MachineConfig(
    name="multi-mount",
    mounts=[
        MountSpec(source="/host/code", target="/workspace"),
        MountSpec(source="/host/data", target="/data", readonly=True),
        MountSpec(source="/host/output", target="/output"),
    ]
)

Writing Files

Write output from machine to host:

const machine = await Machine.create({
  name: 'writer',
  mounts: [
    { source: '/tmp/machine-output', target: '/output' }
  ]
});

// Run computation and write result
await machine.run(
  'python:3.12-alpine',
  ['python', '-c', `
import json
result = {"answer": 42}
with open("/output/result.json", "w") as f:
    json.dump(result, f)
  `]
);

// Result is now at /tmp/machine-output/result.json on the host
config = MachineConfig(
    name="writer",
    mounts=[
        MountSpec(source="/tmp/machine-output", target="/output")
    ]
)

async with Machine(config) as machine:
    await machine.start()

    await machine.run(
        "python:3.12-alpine",
        ["python", "-c", """
import json
result = {"answer": 42}
with open("/output/result.json", "w") as f:
    json.dump(result, f)
        """]
    )
# Result is now at /tmp/machine-output/result.json

Mount Path Guidelines

Use top-level paths. Avoid paths that overlap with system directories inside the container image.

Good paths:

  • /workspace — default persistent workspace; mounting here replaces it with your host directory
  • /code
  • /data
  • /output

Avoid:

  • /var/data — conflicts with system directories inside the container
  • /home/user/code — may conflict with the container image’s home directory

`/workspace` and host mounts

Every image-based machine exposes `/workspace` backed by the VM's storage disk (persists across exec sessions and stop/start). Mounting a host directory at `/workspace` takes priority — your host directory is used instead of the storage-disk workspace. Any other mount target leaves `/workspace` intact.

Mount Permissions

The machine runs as root by default. Files created in mounted directories will be owned by root on the host.

To change ownership after execution:

# On the host, after machine execution
sudo chown -R $USER:$USER /tmp/machine-output

Or create files with specific permissions in the machine:

await machine.exec([
    "sh", "-c",
    "echo 'content' > /output/file.txt && chmod 666 /output/file.txt"
])