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}"
      }
    }
  ]
}
FieldRequiredNotes
atyesAbsolute path the bucket appears at inside the VM.
bucketyesThe bucket name.
credentials.access_key_idyesUse a ${secrets.NAME} reference.
credentials.secret_access_keyyesUse a ${secrets.NAME} reference.
credentials.session_tokennoFor temporary STS credentials.
regionunless endpoint is setFor example us-east-1.
endpointnoMust start with https://. Defaults to https://s3.<region>.amazonaws.com, so set it for S3-compatible storage.
namenoLowercase letters, digits and hyphens, up to 32 characters, unique within the environment. Defaults to the last path segment of at.
prefixnoMount one prefix instead of the whole bucket.
read_onlynoDefaults 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 type mount and status healthy or failed. 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 failed in 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