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

# Run Docker Compose integration tests

> Run a Compose stack without exposing the host Docker socket

Run Docker Compose inside a microVM when a test needs several containers. The inner daemon gets its own disk and never receives the host's `/var/run/docker.sock`.

This example assumes the current directory contains `compose.yaml` and a test service named `test`.

## Run the integration tests

<Steps>
  <Step title="Start Docker">
    <Tooltip tip="On microsandbox cloud, create a directory-backed Docker volume first and omit disk-kind, size, and replace-on-create options from this command."><span className="msb-badge-limited">Limited on cloud <Icon icon="circle-info" size={11} /></span></Tooltip>

    <CodeGroup>
      ```sh macOS & Linux theme={null}
      msb run -d --name compose-ci --replace \
        --memory 2G --root-disk 4G --max-duration 10m \
        --mount-named compose-ci-cache:/var/lib/docker:kind=disk,size=6G \
        --copy-dir .:/workspace --workdir /workspace \
        docker:27.5.1-dind
      ```

      ```powershell Windows theme={null}
      msb run -d --name compose-ci --replace `
        --memory 2G --root-disk 4G --max-duration 10m `
        --mount-named compose-ci-cache:/var/lib/docker:kind=disk,size=6G `
        --copy-dir .:/workspace --workdir /workspace `
        docker:27.5.1-dind
      ```
    </CodeGroup>

    The official `dind` image starts `dockerd`. Wait for it before sending Compose commands:

    <CodeGroup>
      ```sh macOS & Linux theme={null}
      for attempt in $(seq 1 60); do
        msb exec compose-ci -- docker info >/dev/null 2>&1 && break
        sleep 1
      done
      msb exec compose-ci -- docker info >/dev/null
      ```

      ```powershell Windows theme={null}
      for ($attempt = 1; $attempt -le 60; $attempt++) {
        msb exec compose-ci -- docker info *> $null
        if ($LASTEXITCODE -eq 0) { break }
        Start-Sleep -Seconds 1
      }
      msb exec compose-ci -- docker info *> $null
      if ($LASTEXITCODE -ne 0) { throw 'Docker did not become ready' }
      ```
    </CodeGroup>
  </Step>

  <Step title="Run the tests">
    <CodeGroup>
      ```sh macOS & Linux theme={null}
      msb exec --timeout 5m --workdir /workspace compose-ci -- \
        docker compose up --build \
          --abort-on-container-exit \
          --exit-code-from test
      ```

      ```powershell Windows theme={null}
      msb exec --timeout 5m --workdir /workspace compose-ci -- `
        docker compose up --build `
          --abort-on-container-exit `
          --exit-code-from test
      ```
    </CodeGroup>

    Replace `test` with the service whose exit code should decide the CI result. `msb exec` returns that code to the host.

    <Note>
      `--copy-dir` makes a guest copy. Run the command from a clean project directory, or replace it with narrower `--copy-file` and `--copy-dir` flags when the checkout contains credentials or unrelated files.
    </Note>
  </Step>

  <Step title="Clean up">
    Stop the Compose stack inside the sandbox:

    ```sh theme={null}
    msb exec --workdir /workspace compose-ci -- docker compose down --volumes
    ```

    Remove the sandbox:

    ```sh theme={null}
    msb rm -f compose-ci
    ```

    Delete the cache only when you no longer need it:

    ```sh theme={null}
    msb volume remove compose-ci-cache
    ```

    Keep the named volume if you want a repository-specific image cache between trusted runs. Do not share a writable cache across repositories or trust boundaries.
  </Step>
</Steps>

<Warning>
  Never mount the host Docker socket into the sandbox. Control of that socket is normally control of the host Docker daemon and defeats the microVM boundary.
</Warning>
