> ## Documentation Index
> Fetch the complete documentation index at: https://docs.archil.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Serverless Execution

> Run bash commands directly on an Archil file system without needing a separate sandbox.

Archil file systems can run your code. `disk.exec(command)` spins up a container with the file system already mounted, runs an arbitrary shell command to completion, and returns stdout, stderr, exit code, and timing information. You pay only for the time the command is actively running.

<Note>
  Serverless execution is available today only for disks in our AWS regions (`aws-us-east-1`, `aws-us-west-2`, `aws-eu-west-1`). Disks created in other regions (such as the GCP region `gcp-us-central1`) work for storage but cannot run `exec` — calling it returns **`Exec is not enabled`**. If you plan to use `exec`, create the disk in one of the supported AWS regions. See [Region Availability](/reference/regions).
</Note>

<CodeGroup>
  ```typescript TypeScript theme={null}
  import * as archil from "disk";

  const { disk } = await archil.createDisk({
    name: "agent-workspace",
    mounts: [{ type: "s3", bucketName: "my-bucket" }],
  });

  const { stdout } = await disk.exec("du -sh logs");
  ```

  ```python Python theme={null}
  import archil
  from archil import S3Mount

  result = archil.create_disk(
      name="agent-workspace",
      mounts=[S3Mount(bucket_name="my-bucket")],
  )

  res = result.disk.exec("du -sh logs")
  ```
</CodeGroup>

Or from a shell, with the [`disk` CLI](/reference/disk-cli):

```bash theme={null}
npx disk dsk-abc123 exec "du -sh logs"
```

<Warning>Serverless execution always uses disks mounted in shared mode. This means that, by default, you will not be able to perform some kinds of mutating actions (such as editing or removing existing files). For more details on how to use shared disks, see [Shared Disks](/concepts/shared-disks).</Warning>

## When to use `disk.exec`

Reach for serverless execution when:

* **You only want a result, not the raw data.** Aggregating, summarizing, or transforming a bucket returns a few kilobytes; downloading it to your machine could be gigabytes.
* **You need to fan out work across the same data.** Every `exec` gets its own container, so running N execs concurrently is a map-reduce.
* **You're giving an agent a bash tool.** Persistent file system + ephemeral compute is the model agents want, and `disk.exec` gives you both in one SDK call.
* **You don't want to manage compute lifecycle.** No sandbox to warm up, no instance to size, no cold start to pay for when you're idle.

If you instead need **local latency** for an interactive session, or you're running a long-lived process that streams work, [mount the disk on a Linux server](/mounting/linux) and talk to it as a POSIX file system.

<Note>For the most common fan-out case — searching files for a pattern — use the purpose-built [Search Files](/compute/search-files) primitive (`disk.grep`) instead of hand-rolling parallel `exec` calls. It lists and matches across many containers in parallel, scaling the search across many machines instead of one.</Note>

## How it works

When you call `disk.exec`:

1. The request is sent to the Archil control plane and queued for a runtime container.
2. A container is started (or an existing warm one is claimed) with the file system mounted at `/mnt/data`.
3. Your command runs inside the container with `bash -c "<command>"`.
4. When the command exits, stdout, stderr, and the exit code are returned. The container is released.

The returned `ExecResult` includes a `timing` object with three fields:

| Field       | Meaning                                                                                     |
| ----------- | ------------------------------------------------------------------------------------------- |
| `totalMs`   | End-to-end wall clock, measured on the server.                                              |
| `queueMs`   | Time spent queueing, scheduling, booting/claiming a container, and mounting the filesystem. |
| `executeMs` | Time your command itself ran.                                                               |

Billing is based on `executeMs` — the wall-clock time your command runs — in 1ms increments, with a 100ms minimum per call. Queue time is not billed. See the [pricing page](https://archil.com/pricing) for current rates.

## Working directory and environment

Every `exec` runs with the disk as the working directory — commands can reference files on the disk using relative paths. The runtime image includes common Unix tools (`coreutils`, `grep`, `sed`, `awk`, `find`, `curl`, `jq`, `python3`, `node`) — if you need a specific interpreter, write the script to the disk first (through a mounted client or a prior `exec`) and invoke it by path.

<CodeGroup>
  ```typescript TypeScript theme={null}
  // Write a script via a mounted client, then run it through exec
  await disk.exec("python etl.py --input raw --output clean");
  ```

  ```python Python theme={null}
  # Write a script via a mounted client, then run it through exec
  disk.exec("python etl.py --input raw --output clean")
  ```
</CodeGroup>

## Mounting multiple disks in one command

Pass `archil.exec` a `disks` map and a `command` to run with several disks mounted at once. Each key in the map is a subdirectory name you pick, and each value is the disk to mount there. Inside the command, the disks show up at `/mnt/archil/<key>` — so in the example below, the `data` disk is at `/mnt/archil/data` and the `logs` disk is at `/mnt/archil/logs`.

<CodeGroup>
  ```typescript TypeScript theme={null}
  import * as archil from "disk";

  const { stdout } = await archil.exec({
    disks: {
      data: "dsk-abc123",
      logs: "dsk-def456",
    },
    command: "wc -l /mnt/archil/data/*.csv > /mnt/archil/logs/lines.txt",
  });
  ```

  ```python Python theme={null}
  import archil

  res = archil.exec(
      disks={
          "data": "dsk-abc123",
          "logs": "dsk-def456",
      },
      command="wc -l /mnt/archil/data/*.csv > /mnt/archil/logs/lines.txt",
  )
  ```
</CodeGroup>

Each value in the `disks` map is either a disk identifier (a disk id string or a `Disk` instance — mounts the disk's root as read-write) or an `ExecMountSpec` object that additionally pins the mount to a subdirectory of the disk and/or marks it read-only:

<CodeGroup>
  ```typescript TypeScript theme={null}
  import * as archil from "disk";

  const { stdout } = await archil.exec({
    disks: {
      data: "dsk-abc123",
      logs: {
        disk: "dsk-def456",
        subdirectory: "app/logs",
        readOnly: true,
      },
    },
    command: "wc -l /mnt/archil/logs/*.log",
  });
  ```

  ```python Python theme={null}
  import archil
  from archil import ExecMountSpec

  res = archil.exec(
      disks={
          "data": "dsk-abc123",
          "logs": ExecMountSpec("dsk-def456", subdirectory="app/logs", read_only=True),
      },
      command="wc -l /mnt/archil/logs/*.log",
  )
  ```
</CodeGroup>

`subdirectory` must be a relative path with no `.` or `..` segments; when omitted, the disk's root is exposed. `readOnly: true` causes writes against the mount to fail with `EROFS`. The same forms are accepted by the underlying `POST /api/exec` endpoint — see the [API reference](/api-reference/serverless-execution).

## Consistency with other clients

Serverless execs are regular Archil clients — they use the same read-after-write consistency model as any mounted client. A file written by a FUSE mount is visible to the next `exec` that reads it, and vice versa. For concurrent writes, the usual [delegation rules](/concepts/shared-disks) apply; most short-lived execs run fine as non-exclusive writers in their own subdirectory.

## Limits

* **Command timeout:** the HTTP response returns after 5 minutes; longer-running work should be broken up into multiple `exec` calls that communicate through files on the disk.
* **Output size:** stdout and stderr are each capped at 128 KiB per invocation. If you hit the cap, the output is truncated and a trailer is appended noting how many bytes were dropped. Pipe large outputs to a file on the disk instead.
* **Concurrency:** Per-disk concurrency limits are generous but not unbounded — contact us before running large fan-outs.

## Next steps

* [Search files in parallel with `disk.grep`](/compute/search-files)
* [Build a bash tool for an agent](/guides/ai/bash-tool)
* [TypeScript SDK reference for `disk.exec`](/sdks/typescript#executing-commands)
* [Python SDK reference for `Disk.exec`](/sdks/python#executing-commands)
* [`disk` CLI reference](/reference/disk-cli)
