Mounted S3 Storage
A VM is disposable. Anything an agent writes to its disk goes away when the machine is destroyed, and nothing outside a source workspace is committed anywhere. Mount an S3 bucket you own into the environment and the agent writes to a normal directory, while the bytes land in your bucket: a build artifact, a dataset it produced, a report somebody needs after the session ends.
Mounts are declared in the environment's manifest, under mounts. Every VM booted from that environment gets them.
Declare a Mount
{
"version": 1,
"platform": { "os": "linux/amd64" },
"mounts": [
{
"name": "artifacts",
"at": "/mnt/artifacts",
"bucket": "acme-agent-artifacts",
"prefix": "nightly/",
"region": "us-east-1",
"credentials": {
"access_key_id": "${secrets.S3_ACCESS_KEY_ID}",
"secret_access_key": "${secrets.S3_SECRET_ACCESS_KEY}"
}
}
]
}| Field | Required | Notes |
|---|---|---|
at | yes | Absolute path the bucket appears at inside the VM. |
bucket | yes | The bucket name. |
credentials.access_key_id | yes | Use a ${secrets.NAME} reference. |
credentials.secret_access_key | yes | Use a ${secrets.NAME} reference. |
credentials.session_token | no | For temporary STS credentials. |
region | unless endpoint is set | For example us-east-1. |
endpoint | no | Must start with https://. Defaults to https://s3.<region>.amazonaws.com, so set it for S3-compatible storage. |
name | no | Lowercase letters, digits and hyphens, up to 32 characters, unique within the environment. Defaults to the last path segment of at. |
prefix | no | Mount one prefix instead of the whole bucket. |
read_only | no | Defaults to false. |
At most four mounts per environment.
Credentials belong in environment secrets, not in the manifest text. Wallfacer checks the references resolve before it accepts the manifest, refuses to delete a secret a mount still references, and stores the manifest with the credential fields redacted.
Where a Mount Can Go
at must be an absolute path with no .. segment, it must be unique among the environment's mounts, and it must sit under a directory the agent user can write: /mnt, /workspace, /app, or /home/appuser. It cannot be one of those roots itself, because mounting replaces whatever is at the path, and replacing /workspace would take the source checkouts with it. /mnt/<name> is the easy choice.
A manifest that breaks one of these rules is rejected when you save the environment, with a manifest_not_accepted message naming the mount.
Linux Only
Mounts need a FUSE runtime, which the macOS base image does not ship. A manifest that declares mounts on darwin/arm64 is rejected at save time. An empty mounts: [] is fine on any platform.
When the Mount Attaches
Mounts attach after the VM's services are up and the environment reports ready, not before, and attaching happens in the background. A bucket that fails to attach never blocks the boot: the VM comes up and the mount reports its failure instead.
Two consequences worth knowing:
- Watch the mount rather than assuming it. Each mount writes its own log source,
mount-<name>, with typemountand statushealthyorfailed. Read it through the logs API when something the agent wrote is not in the bucket. - Snapshot generation skips mounts entirely. The VM that builds an environment's base snapshot does not attach them, so nothing from a bucket, and no credential, is ever baked into a snapshot.
A mount is a place to put finished files, not a working directory. Every read crosses the network, so an agent should copy what it needs onto local disk, work there, and copy the result back.
Uploads Finish Before the VM Goes Away
Destroying a VM is the risky moment for a mount: an unmount returns as soon as the filesystem detaches, while the upload queue behind it may still be draining. Wallfacer holds the teardown open for it. On every destroy of a VM whose environment declares mounts, the platform flushes the filesystem, unmounts cleanly, and waits for the upload process to exit before the machine goes away. That wait is bounded at roughly 16 minutes.
DELETE /v1/accounts/{account}/vms/{vm} answers 202 and runs this teardown in the background, so a VM you delete through the API keeps the same barrier. Deactivating an account does too.
What the barrier does not cover:
- Files written anywhere outside a mountpoint. They live on the VM's disk and go with it.
- A mount that never attached, or one whose upload process died. Those report
failedin the mount's log rather than passing the barrier. - A flush that runs past its time budget. The incident is logged and the VM is destroyed anyway, so keep individual objects small enough to drain in minutes.
Related
- Manifest Reference for the rest of the environment manifest.
- Environments and Secrets for creating the secrets a mount's credentials reference.
- VM Lifecycle for when VMs boot, idle, and get destroyed.