---
title: "Run Autohand Code on Cloudflare Containers"
source: https://docs.autohand.ai/tutorials/run-autohand-on-cloudflare-containers
---

# 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.

Advanced 45 min

## 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](https://developers.cloudflare.com/containers/get-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.

``` 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 /runner
COPY server.mjs /runner/server.mjs

EXPOSE 8787
CMD ["node", "/runner/server.mjs"]
```

## Step 2: Add a minimal runner API

``` js
// 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.

``` toml
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

``` js
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.

``` bash
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_instances` to control concurrency and cost.
-   Start with the plain Worker trigger tutorial if you already have a VPS or Docker runner.