Self-Hosted Orbs

You can self-host orbs on your own infrastructure. You provide the compute and Amp turns it into orbs.

If you have compute that can

  • quickly start new machines
  • put them to sleep and resume them equally fast
  • keep them around for a long time
  • scale them to zero, so no one has to worry about having a lot of them sleeping

then you can replicate the orb experience within your own infrastructure.

Self-hosted orbs are available to every workspace, and they are free for individuals and teams (see Pricing). If you work alone, you can create a workspace for free.

How It Works

You run amp orb provider on a machine in your network. It connects out to Amp and waits for requests to spawn, pause, or delete orbs. Only outbound HTTPS is needed; Amp never connects in.

1. amp orb provider opens a connection to ampcode.com. 2. Amp sends events to the provider. 3. The provider spawns an orb per thread. 4. Each orb runs in your infrastructure and connects back to its thread on ampcode.com. No inbound ports are needed. 1. amp orb provider opens a connection to ampcode.com. 2. Amp sends events to the provider. 3. The provider spawns an orb per thread. 4. Each orb runs in your infrastructure and connects back to its thread on ampcode.com. No inbound ports are needed.

How you satisfy those requests is up to you. The orb provider runs a dispatch program that you provide, once per job:

amp orb provider ./dispatch.sh

Your dispatch program receives spawn, pause, and delete jobs and creates, resumes, stops, or removes a machine or container on your platform. On spawn, it starts amp orb bootstrap inside the orb. That process connects out to Amp so Amp can set up the environment and run the agent’s tools.

When a member creates a thread on your provider, Amp sends a spawn job. When the thread goes idle or is archived, Amp sends pause, and the next message sends spawn again to resume the same orb. Amp sends delete when the orb is no longer needed, e.g., when the thread is deleted.

See What Leaves Your Network for the data sent to Amp and the outbound connections your network must allow.

Caution

Not all compute can be turned into orbs. Not all infrastructure meets the requirements to provide a great orb experience.

That’s why Amp-hosted orbs still give the best experience. When they pause, they save disk and memory, so processes keep running when the orb wakes, and new threads start from a project snapshot instead of running setup.

If you run orbs on Docker, for example, the experience is different. Docker cannot save memory. So when a Docker-backed orb wakes, the services behind your portals start again when someone opens a portal, and Terminal sessions and Desktop apps are gone.

The dispatch examples below differ in what an orb keeps when it pauses:

InfrastructureMemoryDiskProject snapshots
Amp-hosted orbsYesYesYes
DockerNoYesOn one host
Apple containersNoYesOn one Mac, with container 1.6.0 or later
Kubernetes (Helm chart)NoYesYes
E2BYesYesNot in the example
ModalNoYesNot in the example
VercelNoYesNot in the example
DaytonaNoYesNot in the example
exe.devYesYesNot in the example
BoatNo guaranteePartialNot in the example
  • Memory: processes keep running when the orb wakes, including the services behind portals, Terminal sessions, and Desktop apps.
  • Disk: files and installed software survive a pause, so a waking orb runs .agents/resume, not .agents/setup.
  • Project snapshots: new threads start from a prepared copy of the orb and skip .agents/setup while the snapshot is current. See Project Snapshots.

We know not everyone can use Amp-hosted orbs, and some infrastructure does meet every requirement. That’s why we offer self-hosted orbs.

Getting Started

Create a dispatch program for your platform, register a provider in your workspace, and start your first thread. For GKE, EKS, or AKS, you can use Amp’s Helm chart instead of writing a dispatch program.

Create a Dispatch Program

Save one of the examples below as dispatch.sh, dispatch.py, or dispatch.mjs and adapt it to your platform. The provider passes each job to your program through environment variables. Each example passes ORB_TOKEN into the orb to connect it to its thread. Most boot ORB_IMAGE; the Boat example uses Boat’s own base machine instead.

Amp’s default ORB_IMAGE includes the Amp CLI and other dependencies. For ARM64 hosts, use the ARM64 image. You can also bring your own image. See the dispatch contract for the full list of variables and what each job must do.

#!/usr/bin/env bash
# dispatch.sh
set -euo pipefail

# "$ORB_IMAGE" is the Amp orb image.

case "$ORB_OPERATION" in
	spawn)
		image="$ORB_IMAGE"
		docker image inspect "${ORB_SNAPSHOT_ID-}" >/dev/null 2>&1 && image="$ORB_SNAPSHOT_ID"
		docker start "$ORB_ID" 2>/dev/null ||
			docker run --init --platform linux/amd64 -d --name "$ORB_ID" \
				"$image" sleep infinity
		docker exec -d --env ORB_TOKEN "$ORB_ID" amp orb bootstrap
		;;
	pause)
		docker stop "$ORB_ID"
		[ -z "${ORB_SNAPSHOT_ID-}" ] || docker commit "$ORB_ID" "$ORB_SNAPSHOT_ID"
		;;
	delete)
		docker rm -f "$ORB_ID"
		;;
esac

Docker does not save memory, so processes in the orb stop when it pauses. The container keeps its files, so a waking orb runs .agents/resume, not .agents/setup. Project snapshots are local images, so they work only on this host.

# Needs the E2B CLI, `npm install e2b`, and E2B_API_KEY. Build the template once:
echo 'FROM us-central1-docker.pkg.dev/amp-prod-0/amp-orb/amp-orb:latest' \
    > e2b.Dockerfile
e2b template create amp-orbs --dockerfile e2b.Dockerfile \
    --cpu-count 4 --memory-mb 8192 --ready-cmd true
export E2B_TEMPLATE=amp-orbs
#!/usr/bin/env node
// dispatch.mjs
import { Sandbox } from 'e2b'

const {
	ORB_OPERATION, ORB_ID, ORB_TOKEN, AMP_URL, E2B_TEMPLATE,
} = process.env
const metadata = { orbId: ORB_ID }
const [existing] = await Sandbox.list({
	query: { metadata, state: ['running', 'paused'] },
}).nextItems()

if (ORB_OPERATION === 'spawn') {
	const timeoutMs = 60 * 60 * 1000
	const sandbox = existing
		? await Sandbox.connect(existing.sandboxId, { timeoutMs })
		: await Sandbox.create(E2B_TEMPLATE, {
			metadata, timeoutMs, lifecycle: { onTimeout: 'pause' },
		})
	const envs = { ORB_TOKEN }
	if (AMP_URL) envs.AMP_URL = AMP_URL
	const bootstrap = await sandbox.commands.run('amp orb bootstrap', {
		user: 'user', envs, background: true, timeoutMs: 0,
	})
	await bootstrap.disconnect()
} else if (ORB_OPERATION === 'pause' && existing) {
	await Sandbox.pause(existing.sandboxId)
} else if (ORB_OPERATION === 'delete' && existing) {
	await Sandbox.kill(existing.sandboxId)
}

E2B preserves the disk and memory on pause. This example also pauses on timeout after an hour rather than deleting the orb. It finds running and paused orbs by metadata, so the provider does not need to save sandbox IDs locally. It ignores ORB_SNAPSHOT_ID, so every new thread runs .agents/setup.

Amp supplies a Helm chart for GKE, EKS, and AKS.

It runs the orb provider and a dispatch program that creates orbs as pods and saves paused orbs to your storage bucket, so you do not write a dispatch program yourself. The bucket holds the orb’s files, not its memory, so processes in the orb stop when it pauses.

See Kubernetes to set it up.

#!/usr/bin/env bash
# dispatch.sh
set -euo pipefail
export PATH="/usr/local/bin:/opt/homebrew/bin:$PATH"

# "$ORB_IMAGE" is the Amp orb image.

ids=$(container list --all --quiet)
exists() { grep -Fxq "$ORB_ID" <<<"$ids"; }

case "$ORB_OPERATION" in
	spawn)
		image="$ORB_IMAGE"
		container image inspect "${ORB_SNAPSHOT_ID-}" >/dev/null 2>&1 && image="$ORB_SNAPSHOT_ID"
		if exists; then
			container start "$ORB_ID"
		else
			container run --detach --init --rosetta \
				--platform linux/amd64 --cpus 4 --memory 8G \
				--name "$ORB_ID" "$image" sleep infinity
		fi
		# A bare --env NAME copies the value from the host.
		container exec --detach --env ORB_TOKEN --env AMP_URL \
			"$ORB_ID" amp orb bootstrap
		;;
	pause)
		if exists; then
			container stop "$ORB_ID"
			[ -z "${ORB_SNAPSHOT_ID-}" ] || container commit "$ORB_ID" "$ORB_SNAPSHOT_ID"
		fi
		;;
	delete)
		if exists; then
			container delete --force "$ORB_ID"
		fi
		;;
	*)
		exit 1
		;;
esac

Apple containers do not save memory, so processes in the orb stop when it pauses. Saving project snapshots with container commit needs container 1.6.0 or later. Snapshots are local images, so they work only on this Mac.

#!/usr/bin/env python3
# dispatch.py
import os

import modal

# Needs `pip install modal` plus MODAL_TOKEN_ID and MODAL_TOKEN_SECRET.
APP = "amp-orbs"
app = modal.App.lookup(APP, create_if_missing=True)
snapshots = modal.Dict.from_name("amp-orb-snapshots", create_if_missing=True)
orb_id = os.environ["ORB_ID"]

try:
    sandbox = modal.Sandbox.from_name(APP, orb_id)
except modal.exception.NotFoundError:
    sandbox = None

match os.environ["ORB_OPERATION"]:
    case "spawn":
        if sandbox is None:
            # Modal has no pause, so resume from the last snapshot.
            snapshot = snapshots.get(orb_id)
            if snapshot:
                image = modal.Image.from_id(snapshot)
            else:
                image = modal.Image.from_registry(os.environ["ORB_IMAGE"])
            sandbox = modal.Sandbox.create(
                "sleep", "infinity", app=app, name=orb_id, image=image,
                cpu=4, memory=(16384, 16384), timeout=24 * 60 * 60,
            )
        # Modal execs run as root; point bootstrap at the image user's home.
        bootstrap = "setsid amp orb bootstrap </dev/null >/dev/null 2>&1 &"
        sandbox.exec(
            "bash", "-c", bootstrap,
            env={
                "ORB_TOKEN": os.environ["ORB_TOKEN"],
                "AMP_URL": os.environ.get("AMP_URL"),
                "HOME": "/home/user", "USER": "user",
            },
        ).wait()
    case "pause":
        if sandbox:
            image_id = sandbox.snapshot_filesystem(timeout=120).object_id
            snapshots[orb_id] = image_id
            sandbox.terminate()
    case "delete":
        if sandbox:
            sandbox.terminate()
        snapshots.pop(orb_id, None)

Modal’s filesystem snapshot keeps the orb’s files but not its memory, so processes in the orb stop when it pauses. This example ignores ORB_SNAPSHOT_ID, so every new thread runs .agents/setup.

#!/usr/bin/env node
// dispatch.mjs
import { APIError, Sandbox, Snapshot } from '@vercel/sandbox'

const { ORB_OPERATION, ORB_ID, ORB_IMAGE, ORB_TOKEN, AMP_URL } = process.env
const credentials = {
	token: process.env.VERCEL_TOKEN,
	teamId: process.env.VERCEL_TEAM_ID,
	projectId: process.env.VERCEL_PROJECT_ID,
}

if (ORB_OPERATION === 'spawn') {
	const sandbox = await Sandbox.getOrCreate({
		...credentials,
		name: ORB_ID,
		// Copy Amp's image to VCR, then set ORB_IMAGE to its VCR URL.
		image: ORB_IMAGE,
		resources: { vcpus: 4 },
		timeout: 60 * 60 * 1000,
		persistent: true,
		snapshotExpiration: 0,
		keepLastSnapshots: { count: 1, expiration: 0, deleteEvicted: true },
	})
	const setup = await sandbox.runCommand({
		cmd: '/bin/bash',
		args: ['-c', 'hostname localhost && mkdir -p /vercel/sandbox'],
		sudo: true,
	})
	if (setup.exitCode !== 0) throw new Error(await setup.stderr())
	await sandbox.runCommand({
		cmd: '/usr/bin/setpriv',
		args: [
			'--reuid=user', '--regid=user', '--init-groups', '--',
			'/usr/bin/env', `ORB_TOKEN=${ORB_TOKEN}`,
			...(AMP_URL ? [`AMP_URL=${AMP_URL}`] : []),
			'HOME=/home/user', 'USER=user', '/bin/bash', '-lc',
			'exec amp orb bootstrap',
		],
		sudo: true,
		detached: true,
	})
} else if (ORB_OPERATION === 'pause' || ORB_OPERATION === 'delete') {
	try {
		const sandbox = await Sandbox.get({
			...credentials, name: ORB_ID, resume: false,
		})
		if (ORB_OPERATION === 'delete') {
			if (sandbox.status === 'running') await sandbox.stop()
			for await (const saved of await sandbox.listSnapshots()) {
				if (saved.status !== 'created') continue
				const snapshot = await Snapshot.get({
					...credentials, snapshotId: saved.id,
				})
				await snapshot.delete()
			}
			await sandbox.delete()
		} else if (sandbox.status === 'running') await sandbox.stop()
	} catch (error) {
		if (!(error instanceof APIError && error.response.status === 404)) {
			throw error
		}
	}
}

Vercel saves the orb’s files when it stops, but not its memory, so processes in the orb stop when it pauses. This example ignores ORB_SNAPSHOT_ID, so every new thread runs .agents/setup.

#!/usr/bin/env python3
# dispatch.py
import os
from uuid import uuid4
from daytona import (
    Daytona, DaytonaNotFoundError, CreateSandboxFromImageParams,
    Resources, SessionExecuteRequest, SandboxState,
)

daytona = Daytona()
operation = os.environ['ORB_OPERATION']
try:
    sandbox = daytona.get(os.environ['ORB_ID'])
except DaytonaNotFoundError:
    sandbox = None

if operation == 'spawn':
    if sandbox is None:
        sandbox = daytona.create(CreateSandboxFromImageParams(
            name=os.environ['ORB_ID'], image=os.environ['ORB_IMAGE'],
            os_user='user', resources=Resources(cpu=4, memory=8, disk=10),
            auto_stop_interval=0, auto_delete_interval=-1,
        ), timeout=240)
    elif sandbox.state != SandboxState.STARTED:
        sandbox.start()
    env = {key: os.environ[key] for key in ('ORB_TOKEN', 'AMP_URL')
           if key in os.environ}
    sandbox.update_env(env, unset=['AMP_URL'])
    session = str(uuid4())
    sandbox.process.create_session(session)
    sandbox.process.execute_session_command(session, SessionExecuteRequest(
        command="bash -lc 'exec amp orb bootstrap'", run_async=True,
    ))
elif operation == 'pause' and sandbox:
    if sandbox.state == SandboxState.STARTED:
        sandbox.stop()
elif operation == 'delete' and sandbox:
    sandbox.delete()

Daytona keeps the orb’s files when it stops, but not its memory, so processes in the orb stop when it pauses. This example ignores ORB_SNAPSHOT_ID, so every new thread runs .agents/setup.

#!/usr/bin/env bash
# dispatch.sh
set -euo pipefail

# Pause/resume currently requires access enabled by exe.dev support.
status=$(ssh exe.dev ls "$ORB_ID" --json |
    jq -r --arg id "$ORB_ID" '.vms[] | select(.vm_name == $id) | .status')

case "$ORB_OPERATION" in
    spawn)
        if [[ -z "$status" ]]; then
            ssh exe.dev new --name="$ORB_ID" --image="$ORB_IMAGE" \
                --cpu=2 --memory=8G --no-email
        elif [[ "$status" == paused ]]; then
            ssh exe.dev resume "$ORB_ID"
        fi
        {
            printf 'export ORB_TOKEN=%q\n' "$ORB_TOKEN"
            if [[ -n "${AMP_URL:-}" ]]; then
                printf 'export AMP_URL=%q\n' "$AMP_URL"
            fi
            cat <<'BOOTSTRAP'
sudo -u user --preserve-env=ORB_TOKEN,AMP_URL bash -lc \
    'cd "$HOME"; nohup amp orb bootstrap </dev/null >.amp-bootstrap.log 2>&1 &'
BOOTSTRAP
        } | ssh "$ORB_ID.exe.xyz" bash -s
        ;;
    pause)
        if [[ "$status" == running ]]; then
            ssh exe.dev pause "$ORB_ID"
        fi
        ;;
    delete)
        if [[ -n "$status" ]]; then
            ssh exe.dev rm "$ORB_ID"
        fi
        ;;
esac

exe.dev saves memory on pause, so processes keep running when the orb wakes. This example ignores ORB_SNAPSHOT_ID, so every new thread runs .agents/setup.

Install the Boat CLI and sign in with boat login. The controller needs Bash, jq, and OpenSSH. Enable snapshots in Boat’s settings and keep zero data retention off.

#!/usr/bin/env bash
# dispatch.sh
set -euo pipefail
export BOAT_NO_UPDATE=1
# Keep this directory on persistent storage.
STATE="${BOAT_ORB_STATE:-$HOME/.amp-boat-orbs}"
mkdir -p "$STATE"
trap 'echo "Boat $ORB_OPERATION $ORB_ID failed at line $LINENO" >&2' ERR
MAP="$STATE/$ORB_ID"
id=""
state=""
if [[ -s "$MAP" ]]; then
    id=$(cat "$MAP")
    state=$(boat info "$id" --json | jq -er '.sandbox.state')
fi
case "$ORB_OPERATION" in
    spawn)
        if [[ -z "$id" ]]; then
            # Read all JSON Lines before parsing. Increase the TTL for longer turns.
            out=$(boat new --json --ttl 3600 --no-env)
            printf '%s\n' "$out" | jq -ser 'map(select(.event == "ready"))[0].id // empty' > "$MAP"
            id=$(cat "$MAP")
        else
            while [[ "$state" == stopping ]]; do
                sleep 2
                state=$(boat info "$id" --json | jq -er '.sandbox.state')
            done
            if [[ "$state" == stopped || "$state" == error ]]; then
                boat resume "$id" >/dev/null
            fi
        fi
        {
            printf 'export ORB_TOKEN=%q AMP_URL=%q\n' "$ORB_TOKEN" "${AMP_URL:-https://ampcode.com}"
            printf '%s\n' \
                'export PATH="$HOME/.local/bin:$HOME/.amp/bin:$PATH"' \
                'command -v amp >/dev/null || curl -fsSL https://ampcode.com/install.sh | bash' \
                'nohup amp orb bootstrap </dev/null >~/.amp-bootstrap.log 2>&1 &'
        } | boat ssh "$id" bash -se -o pipefail
        ;;
    pause)
        if [[ -n "$id" && "$state" != stopped ]]; then
            [[ "$state" == stopping ]] || boat stop "$id" >/dev/null
            until [[ "$(boat info "$id" --json | jq -er '.sandbox.state')" == stopped ]]; do
                sleep 2
            done
        fi
        ;;
    delete)
        if [[ -n "$id" ]]; then
            boat delete "$id" --yes
            rm -f "$MAP"
        fi
        ;;
esac

Boat keeps home files for 14 days after a pause, but not /tmp or ~/.cache, and memory may not survive. This example uses Boat’s base machine instead of ORB_IMAGE and ignores ORB_SNAPSHOT_ID, so every new thread runs .agents/setup.

Set Up a Provider

  1. Open Settings → Orbs for your workspace, click Add Orb Provider, and give the provider a name such as our-k8s-orbs. Members pick the provider by this name when they start a thread.

    The Add Orb Provider dialog with the name our-k8s-orbsThe Add Orb Provider dialog with the name our-k8s-orbs
  2. Copy the aop_live_… token. It is shown once.

  3. Make your dispatch program executable and run the orb provider with the token in its environment and the absolute path of your program, under a service manager that restarts it after a reboot:

    chmod +x dispatch.sh
    export AMP_ORB_PROVIDER_TOKEN="<copied token>"
    amp orb provider /opt/orbs/dispatch.sh

Run exactly one amp orb provider per token. Two of them, for example on two machines or a forgotten terminal next to the service, each claim a share of the jobs and each start their own orbs, so a thread’s orb ends up on one while its next job goes to the other. A provider tells Amp when it starts and stops, and Amp notices one that was killed. If another provider is still connected when one starts, it asks for confirmation at a terminal; without a terminal it logs the other provider and waits for it to stop, so a rollout that stops the old one first needs nothing special. Pass --allow-multiple only if your dispatch program shares its state across all of them.

Select the provider in workspace settings to make it the default for new threads.

Self-hosted orbs are on by default. A workspace admin can turn them off with the Self-Hosted Orbs setting in Settings → Orbs. While they are off, members cannot start orbs on the workspace’s providers and the providers stop receiving jobs; the providers and their tokens are kept.

Start Your First Thread

In the new thread composer, click Orb, open the type dropdown, and choose your provider under Self-Hosted.

The new thread composer with the orb picker open and our-k8s-orbs selected under Self-HostedThe new thread composer with the orb picker open and our-k8s-orbs selected under Self-Hosted

From the CLI, run:

amp -ox "your first message" --orb-provider our-k8s-orbs

Submit your prompt. Amp sends a request to your provider to spawn an orb, clones your repository, runs .agents/setup, and starts the agent.

Dispatch Contract

The orb provider runs your program once per job, with no arguments. The job arrives as environment variables:

VariableValue
ORB_OPERATIONspawn, pause, or delete.
ORB_IDThe orb’s ID, such as amp-<thread id>. Use it as the machine name so repeated jobs find the same machine.
ORB_IMAGEThe image to boot: your ORB_IMAGE override or Amp’s default.
ORB_SNAPSHOT_IDThe project snapshot to boot from on spawn, or to save on pause. Unset when the job has none.
ORB_TOKENspawn only. A short-lived credential that authorizes this orb’s connection to its thread. Pass it into the orb.
AMP_URLspawn only. The Amp server to connect to when it is not ampcode.com. Pass it into the orb with the token.

Each operation does the following:

OperationWhat your program does
spawnMake the orb run. Start it again if it exists. Otherwise create it from ORB_SNAPSHOT_ID if you have that snapshot, or from ORB_IMAGE. Then start amp orb bootstrap inside it as the image’s default user, detached, with ORB_TOKEN and AMP_URL set.
pauseStop the orb and keep its disk. When ORB_SNAPSHOT_ID is set, also save a copy of the disk under that ID for later spawn jobs. If your platform cannot keep the disk, delete the orb instead; the next spawn boots a fresh one and Amp sets the repository up again. If your platform also saves memory, processes keep running after the next spawn. Otherwise they stop with the orb.
deleteRemove the orb and its disk.

Make every operation safe to repeat and finish within five minutes; your program’s output goes to the provider log, and spawn output also shows in the thread’s setup log.

Who Should Self-Host

We built self-hosted orbs for enterprises that can’t use the orbs we host, because regulations might restrict where their code runs, because they need access to internal networks, or because they need custom hardware attached.

That’s why the interface is so open: we want to support all the infrastructure our enterprise customers might have, as long as it meets the qualitative requirements.

The infrastructure needs to provide fast-to-start, resumable machines that scale to zero.

We only support our end of the dispatch contract, not every possible piece of infrastructure you can plug in.

We recommend you self-host orbs when:

  • Your repository checkout and saved orb disks must stay in your cloud account.
  • Your builds need private services that are reachable only from your network.
  • Your rules require compute in a specific cloud, region, or account.
  • You need custom hardware attached to orbs.

If you’re on an Enterprise plan, let us know and we’ll help you set it up.

What Leaves Your Network

Amp runs the agent loop and model inference on its own servers. Your orb runs the agent’s tools, such as reading and editing files and running commands. The repository checkout, build output, and saved orb disks stay on your infrastructure, and orbs can reach the private services your network allows.

During a thread, the orb sends Amp what those tools produce: the file contents the agent reads, command output, the diff shown in the thread’s Changes tab, and screenshots and other files attached to the thread. Amp stores this with the thread.

Amp never connects into your network and needs no cloud credentials or access to your cluster or storage, so you do not need to allow Amp’s server IP addresses. Allow this outbound access. Proxies must allow long-lived connections.

SourceDestinationPurpose
Orb providerampcode.com, HTTPS 443Receiving jobs and reporting results
Orb provider and orbsstatic.ampcode.com, HTTPS 443Amp CLI updates
Orbsampcode.com, HTTPS and WSS 443Bootstrap and tool connections
Orbstunnel.ampcode.com, WSS 443Amp Tunnel for portals, Terminal, and Desktop
OrbsYour Git host, HTTPS 443Cloning and pushing the repository
Orbsstorage.googleapis.com, HTTPS 443ripgrep download when the image has no rg

Portals, Terminal, and Desktop use Amp Tunnel. A portal on a self-hosted orb can be opened only by a signed-in Amp user with access to the thread.

Docker Image

Amp publishes two orb images. Both include the Amp CLI and the tools for portals, Terminal, and Desktop. When the provider’s environment does not set ORB_IMAGE, Amp passes the x64 image to every spawn job.

ImagePlatform
us-central1-docker.pkg.dev/amp-prod-0/amp-orb/amp-orb:latestLinux x64, the default
us-central1-docker.pkg.dev/amp-prod-0/amp-orb/amp-orb-arm64:latestLinux ARM64

For ARM64 hosts, such as Apple silicon Macs or ARM64 servers, set ORB_IMAGE to the ARM64 image in the provider’s environment:

export ORB_IMAGE=us-central1-docker.pkg.dev/amp-prod-0/amp-orb/amp-orb-arm64:latest
amp orb provider ./dispatch.sh

In the Docker example, replace --platform linux/amd64 with --platform linux/arm64. Also add --shm-size=1g, because the browser in the orb needs more than Docker’s default 64 MB of shared memory.

Changing ORB_IMAGE affects only new orbs. Existing orbs and project snapshots keep the image they started from.

Bring Your Own Image

Set ORB_IMAGE in the provider’s environment to start orbs from your own image instead of the default Amp orb image. Amp passes the value to every spawn job.

The easiest way is to build on the Amp orb image and add what every orb needs, such as language toolchains or internal CA certificates. Keep project-specific setup in .agents/setup.

An image built from another base must run Linux x64 or ARM64 with glibc, so Alpine does not work. It needs:

RequirementNeeded for
Bash, coreutils, util-linux, and CA certificatesEvery orb
Git at /usr/bin/git or /opt/git/bin/git, 2.34 or later to sign commitsEvery orb
The Linux amp binary for the image’s platform at /usr/local/bin/ampEvery orb
A user user and group as the image’s default user, with a writable /home/userEvery orb
Passwordless sudo for user (user ALL=(ALL) NOPASSWD: ALL)Every orb
Node.js 18 or laterPortals and external agents
tmux 2.9 or laterTerminal and external agents
curlExternal agents
/opt/amp-wayland from the Amp orb image, xwayland, dbus, gsettings-desktop-schemas, xsettingsd, wlr-randr, pulseaudio, ffmpeg, and fonts-dejavu-coreDesktop
ripgrep, gh, and openssh-clientRecommended

Amp uses passwordless sudo inside the orb to configure system Git settings and prepare runtime files, as well as manage Amp services on images that use systemd.

Project Snapshots

A project snapshot is a saved copy of a prepared orb, with the repository cloned and .agents/setup already run, so later threads skip setup. Amp uses snapshots on self-hosted providers the same way it does on Amp-hosted orbs, as long as your dispatch program supports them. Of the examples above, Docker, Apple containers, and the Kubernetes chart do. See The Orb Lifecycle for when Amp saves and reuses them.

To save a snapshot, Amp sends a pause job with ORB_SNAPSHOT_ID set and then a spawn job for the same orb. Store a copy of the orb under that ID on pause, and boot from the copy when a later spawn names it.

If your dispatch program ignores ORB_SNAPSHOT_ID, every thread starts from the image and runs .agents/setup.

To list or delete a project’s snapshots, run:

amp projects snapshots list <project>
amp projects snapshots delete <project>

Bring Your Own Repository Checkout

By default, Amp clones the project’s repository into each new orb and sets up Git and gh with short-lived GitHub tokens for the member who created the thread. If your orb image already contains the repository and its own Git credentials, turn off Repository Setup in the provider’s Edit dialog in workspace settings.

The Edit Orb Provider dialog with Repository Setup turned offThe Edit Orb Provider dialog with Repository Setup turned off

Amp then does not clone or fetch the repository, change the orb’s Git credential helpers or gh login, or get a GitHub token for the orb. It still sets the commit author, runs the pre-setup script and .agents/setup, and starts the agent. The setting applies to orbs Amp sets up after you change it and does not apply to repositories hosted on Amp.

Your image must have a Git checkout of the project’s repository at /home/user/workspace/repo with origin and origin/HEAD set, so the Changes view and Ship can find the base branch. Keeping the checkout up to date is your job. Pushes and pull requests use the credentials in the orb, so use a credential helper that gets a fresh token each time Git asks; a token that lasts an hour can expire during a long thread.

Pricing

Self-hosted orbs are free for every workspace until October 29. After that, they stay free on the Hobby, Individual, and Teams plans, and Enterprise workspaces pay 100% of the standard Amp-hosted orb price for the orb’s CPUs and memory. Volume discounts are available; contact the Amp team.

You also pay for the infrastructure that runs your orbs. Your thread usage shows each orb’s runtime and size, even when its cost is $0.

Prices are at the standard credit rate; an Enterprise workspace pays in its credits at its rate. An orb is billed by the minute while it is connected to a thread, with the CPUs and memory it reports. Only the thread’s orb is billed; its agent tools sandbox is not.

Troubleshoot

If an orb does not start, read the orb provider’s log and the thread’s setup log. If the cause is not on your side, send a bug report from the thread and include the log lines.