Headless Mode
Run Autohand Code in Docker
Package Autohand Code into a repeatable runner image and mount repositories into the container for local or remote automation.
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, Bind mounts, and Compose 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.
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"]
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.
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.
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.
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.
ssh [email protected]
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.