# Use Docker in CI

CI jobs on Codebahn's hosted runners execute inside a container. The Docker daemon runs on the host, and the socket is mounted into the job, so `docker` commands work. `docker build`, `docker push`, `docker run` all behave as expected.

One thing does not: **bind mounts**.

## Prerequisites

- A working CI workflow ([Set up CI](/docs/set-up-ci/))
- Solo or Crew plan with hosted runners, or a [BYO runner](/docs/run-your-own-ci/) with Docker

## Why `-v` doesn't work

When you run a sibling container from a CI job:

```yaml
- run: docker run -v $(pwd)/report.json:/input/report.json scanner:latest
```

The `-v` path resolves on the **host**, not inside your job container. The host has no `/input/report.json` at that path, so the mount is empty or fails silently.

This is not a Codebahn limitation. The same thing happens on any runner that executes jobs in containers with a host Docker socket (GitHub Actions hosted runners, GitLab runners in Docker mode, self-hosted Forgejo runners).

## Use `docker cp` instead

Copy files into the container explicitly:

```yaml
- name: Scan with Trivy
  run: |
    cid=$(docker create aquasec/trivy image \
            --input /scan/image.tar \
            --severity HIGH,CRITICAL --exit-code 1)
    trap 'docker rm -f "$cid" >/dev/null 2>&1 || true' EXIT
    docker cp report.json "$cid:/scan/report.json"
    docker start -a "$cid"
```

`docker create` makes the container without starting it. `docker cp` copies files in. `docker start -a` runs it and attaches stdout.

## When you don't need `docker cp`

Most Docker workflows never hit this. If you only:

- **Build images** (`docker build`): the build context is sent to the daemon over the socket, not mounted. Works fine.
- **Push images** (`docker push`): no file exchange needed. Works fine.
- **Run containers that don't need local files** (`docker run postgres`): no mount needed. Works fine.

The limitation only matters when you need to pass a file from your job into a sibling container. Image scanning, linting a generated artifact, or feeding a report into a processing container are the common cases.

## Pipe as an alternative

For tools that accept stdin:

```yaml
- run: docker save myimage:latest | docker run -i aquasec/trivy image --input -
```

Simpler than `docker cp`, but not every tool supports stdin input, and large images can be slow over a pipe.
