Headless Mode
Run Autohand Code on Cloudflare Containers
Use Cloudflare Containers when you need Worker-routed HTTP control plus a container image that can run a Linux process.
Provider docs used
Cloudflare Containers are configured from Wrangler with a container image, Durable Object binding, and migration. The container image must be able to run on linux/amd64, and Worker code routes requests to container instances. Keep the current provider guide open: Cloudflare Containers getting started.
When to use this
Use Cloudflare Containers when a Worker trigger is not enough and you want Cloudflare to run the containerized runner. Start with a VPS or Docker host if you need persistent workspaces, large dependency caches, private VPC access, or simple shell debugging.
Step 1: Create the runner container
The container should expose a small HTTP API. The API receives an allowlisted task and starts Autohand Code inside the container.
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 /runner
COPY server.mjs /runner/server.mjs
EXPOSE 8787
CMD ["node", "/runner/server.mjs"]
Step 2: Add a minimal runner API
// server.mjs
import { createServer } from "node:http";
import { spawn } from "node:child_process";
const prompts = {
review: "Clone or update the configured repository, review recent changes, and summarize action items"
};
createServer((req, res) => {
if (req.method !== "POST") {
res.writeHead(405);
res.end("method not allowed");
return;
}
const job = spawn("autohand", [
"-p",
prompts.review,
"--restricted",
"--output-format",
"stream-json"
], { env: process.env });
job.stdout.on("data", chunk => process.stdout.write(chunk));
job.stderr.on("data", chunk => process.stderr.write(chunk));
res.writeHead(202);
res.end("queued\n");
}).listen(8787, "0.0.0.0");
Step 3: Configure Wrangler
Cloudflare Containers use Worker configuration plus a Durable Object class. The image can point at a local Dockerfile or a qualified image reference.
name = "autohand-container-runner"
main = "src/index.js"
compatibility_date = "2026-06-18"
[[containers]]
class_name = "AutohandCodeContainer"
image = "./Dockerfile"
max_instances = 5
[[durable_objects.bindings]]
name = "AUTOHAND_CONTAINER"
class_name = "AutohandCodeContainer"
[[migrations]]
tag = "v1"
new_sqlite_classes = [ "AutohandCodeContainer" ]
Step 4: Route requests to containers
import { Container, getContainer } from "@cloudflare/containers";
export class AutohandCodeContainer extends Container {
defaultPort = 8787;
sleepAfter = "10m";
}
export default {
async fetch(request, env) {
if (request.method !== "POST") {
return new Response("method not allowed", { status: 405 });
}
const repo = request.headers.get("x-repo") || "default";
const container = getContainer(env.AUTOHAND_CONTAINER, repo);
return container.fetch(request);
}
};
Step 5: Add secrets and deploy
Use Worker secrets for tokens. Pass only the values the container needs.
wrangler secret put OPENROUTER_API_KEY
wrangler secret put GIT_DEPLOY_KEY
wrangler deploy
Operations checklist
- Design jobs to be resumable because containers may sleep after idle time.
- Keep repository clones and dependency caches small unless you intentionally add durable storage elsewhere.
- Use allowlisted tasks instead of arbitrary prompts from public requests.
- Use
max_instancesto control concurrency and cost. - Start with the plain Worker trigger tutorial if you already have a VPS or Docker runner.