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

# Docker and Compose

> Build images and run Docker containers or Compose services inside a persistent VM

JavaScript examples use `@beamcloud/beam-js` 1.1.0 or later. [Configure the SDK](/v2/vm/sdk#installation-and-configuration) before running them.

Enable the Docker daemon when creating your VM. Prepared VM images already include Docker, Compose, and Buildx:

```bash theme={null}
beam vm new docker-dev --docker-enabled
beam vm exec docker-dev -- docker version
beam vm exec docker-dev -- docker compose version
beam vm exec docker-dev -- docker run --rm hello-world
```

Docker's images, build cache, container layers, and named volumes are stored in the VM root and survive stop/start. Use restart policies for containers that should start on a cold boot. Pause/resume preserves their running state with the rest of the guest.

## Build and run from the SDK

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    from beam import VM

    with VM.connect("docker-dev") as vm:
        with vm.keep_alive():
            vm.fs.create_directory("/workspace/app")
            vm.fs.write_text(
                "/workspace/app/Dockerfile",
                'FROM alpine:3.20\nCMD ["echo", "hello"]\n',
            )
            build = vm.docker.build(tag="example:latest", context="/workspace/app")
            for line in build.logs():
                print(line, end="")
            if not build.wait():
                raise RuntimeError(build.stderr)
            run = vm.docker.run("example:latest", remove=True)
            run.wait()
            print(run.stdout)
            print(run.stderr)
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    const vm = await VM.connect("docker-dev");
    await vm.keepAlive(async () => {
      await vm.fs.createDirectory("/workspace/app");
      await vm.fs.writeText(
        "/workspace/app/Dockerfile",
        'FROM alpine:3.20\nCMD ["echo", "hello"]\n',
      );
      const build = await vm.docker.exec([
        "build",
        "-t",
        "example:latest",
        "/workspace/app",
      ]);
      for await (const line of build.logs) console.log(line);
      if ((await build.wait()) !== 0) throw new Error(await build.stderr.read());
      const container = await vm.docker.run("example:latest", ["--rm"]);
      await container.wait();
      console.log(await container.stdout.read());
    });
    ```
  </Tab>
</Tabs>

Python Docker operations return `DockerResult` handles with `wait()`, `success`, `stdout`, `stderr`, and combined `logs()`. Unlike `process.logs`, Docker's `logs()` is a method. Build progress often appears on stderr.

The SDK Docker helpers use the VM's network namespace. Publish the service's actual listening port with `vm.expose()`; SDK host-port remapping is unsupported. Direct commands issued through SSH or `vm.process.exec("docker", ...)` use normal guest Docker behavior and need their own Docker networking configuration.

## Compose from the CLI

Upload your project, then run Compose inside the VM:

```bash theme={null}
beam vm sync docker-dev ./project
beam vm exec docker-dev -- docker compose -f compose.yaml up -d --build
beam vm exec docker-dev -- docker compose -f compose.yaml ps
beam vm exec docker-dev -- docker compose -f compose.yaml logs --tail 100
beam vm expose docker-dev 8000 --protected
```

For direct Compose commands, configure `ports` or host networking in your Compose file so the application is reachable on the VM's published port.

## Compose from the SDK

The Python SDK Compose helpers share the VM's network and PID namespaces and reuse that configuration for later stack operations:

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    from beam import VM

    with VM.connect("docker-dev") as vm:
        with vm.keep_alive():
            vm.fs.upload_file("compose.yaml", "/workspace/compose.yaml")
            stack = vm.docker.compose_up(file="compose.yaml", cwd="/workspace", build=True)
            print(stack.ps())
            print(stack.logs())
            print(vm.expose(8000, protected=True))
            # When finished with these services:
            stack.stop()
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    const vm = await VM.connect("docker-dev");
    await vm.keepAlive(async () => {
      await vm.fs.uploadFile("compose.yaml", "/workspace/compose.yaml");
      const start = await vm.docker.compose([
        "-f",
        "compose.yaml",
        "up",
        "-d",
        "--build",
      ]);
      if ((await start.wait()) !== 0) throw new Error(await start.stderr.read());
      console.log(await vm.exposePort(8000, { protected: true }));
      const status = await vm.docker.compose(["-f", "compose.yaml", "ps"]);
      await status.wait();
      console.log(await status.stdout.read());
      // When finished: vm.docker.compose(["-f", "compose.yaml", "stop"])
    });
    ```
  </Tab>
</Tabs>

JavaScript Docker commands return the same process handles as `vm.exec()`: use `await command.wait()`, `await command.stdout.read()`, and `for await (const line of command.logs)`. JavaScript Compose runs the provided argv; configure ports or host networking in your Compose file.

Upload any Dockerfiles and source referenced by Compose's build contexts before starting the stack. Services using the SDK helpers must listen on distinct guest ports. See [Docker APIs](/v2/sandbox/docker) for image/container operations, and use `vm.aio.docker` for async calls.

For long builds on an idle-limited VM, hold `vm.keep_alive()` or disable the TTL. For daemon diagnostics, use `beam vm logs docker-dev --unit docker.service`.

Nested Docker containers do not inherit the VM's brokered-secret CA. Mount `/etc/beam/ca-certificates.crt` read-only into containers that use brokered credentials and configure their clients to trust it; for example, set `SSL_CERT_FILE` to the mounted path. See [brokered secrets](/v2/environment/secrets#rotation-and-compatibility) for header substitution and trust limitations.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.