---
title: "Run Autohand Code in Docker"
source: https://docs.autohand.ai/tutorials/run-autohand-in-docker
---

# Run Autohand Code in Docker

Package Autohand Code into a repeatable runner image and mount repositories into the container for local or remote automation.

Intermediate 25 min

## Provider docs used

This tutorial follows Docker's guidance for Dockerfiles, bind mounts, and Compose secrets. Dockerfiles define the runner image. Bind mounts share source code with the container. Compose secrets mount sensitive values into `/run/secrets` on a per-service basis. Keep the provider docs open: [Dockerfile overview](https://docs.docker.com/build/concepts/dockerfile/), [Bind mounts](https://docs.docker.com/engine/storage/bind-mounts/), and [Compose secrets](https://docs.docker.com/compose/how-tos/use-secrets/).

## Step 1: Create the runner image

Create a Dockerfile at the root of a runner project, not inside every repository you want Autohand Code to inspect.

``` dockerfile
FROM node:22-bookworm

RUN apt-get update \
  && apt-get install -y --no-install-recommends git openssh-client ca-certificates build-essential \
  && rm -rf /var/lib/apt/lists/*

RUN npm install -g autohand-cli

WORKDIR /workspace
ENTRYPOINT ["autohand"]
```

``` bash
docker build -t autohand-runner:local .
```

## Step 2: Run against a local repository

Mount the repository into `/workspace`. Docker bind mounts can write to the host by default, so use `readonly` for review-only jobs.

``` bash
cd /path/to/your-repo

docker run --rm -it \
  --mount type=bind,src="$PWD",dst=/workspace,readonly \
  -e OPENROUTER_API_KEY \
  -e AUTOHAND_MODEL="anthropic/claude-sonnet-4" \
  autohand-runner:local \
  -p "Review this repository and summarize the highest-risk issues" --restricted
```

Remove `readonly` only for trusted write jobs that need to update files in the mounted repository.

## Step 3: Keep secrets out of the image

Do not bake model provider keys into the Dockerfile. Pass them at runtime, or use Compose secrets for long-running runner services.

``` yaml
services:
  autohand:
    image: autohand-runner:local
    working_dir: /workspace
    volumes:
      - ./your-repo:/workspace
    secrets:
      - openrouter_api_key
    environment:
      AUTOHAND_MODEL: anthropic/claude-sonnet-4
    entrypoint: ["/bin/bash", "-lc"]
    command: >
      export OPENROUTER_API_KEY="$(cat /run/secrets/openrouter_api_key)" &&
      autohand -p "Review this repository and write a risk summary" --restricted --output-format stream-json

secrets:
  openrouter_api_key:
    file: ./secrets/openrouter_api_key.txt
```

## Step 4: Give the container Git access

For private repositories, mount a read-only deploy key and known hosts file. Avoid mounting your entire personal `~/.ssh` directory.

``` bash
docker run --rm -it \
  --mount type=bind,src="$PWD",dst=/workspace \
  --mount type=bind,src="$HOME/.ssh/github-autohand",dst=/home/node/.ssh/id_ed25519,readonly \
  --mount type=bind,src="$HOME/.ssh/known_hosts",dst=/home/node/.ssh/known_hosts,readonly \
  -e OPENROUTER_API_KEY \
  autohand-runner:local \
  -p "Update docs for the latest CLI changes, then summarize the diff" --output-format stream-json
```

## Step 5: Use Docker on a remote host

When the Docker daemon is remote, bind mounts refer to paths on the remote daemon host, not your local laptop. Clone the repository on the remote host first, then run the container there.

``` bash
ssh autohand@runner.example.com
cd ~/work/your-repo

docker run --rm \
  --mount type=bind,src="$PWD",dst=/workspace \
  --env-file ~/.autohand/env \
  autohand-runner:local \
  -p "Run tests and fix straightforward failures" --output-format stream-json
```

## Operations checklist

-   Keep runner images small and rebuild them when Node.js or Autohand Code changes.
-   Use read-only mounts for analysis jobs.
-   Mount only the Git keys needed for the job.
-   Use Compose secrets or runtime environment injection, not Dockerfile `ENV`, for provider keys.
-   Pin image tags in production runner scripts.