Set up Agent Relay
Deploy Agent Relay against your Coder deployment with a service account, provisioners, and the Helm chart.
On this page
Note
Agent Relay is in beta and requires a Premium license.
This guide is for Coder administrators who deploy Agent Relay against an existing Coder deployment. It covers the steps that are the same for every provider: the service account, provisioners, template rules, installation, and verification. Provider-specific steps live on the Claude Code and Cursor pages.
Before you begin
Important
Only a user with the Coder owner role can create service accounts and mint their tokens.
You need:
- A Coder deployment with a Premium license, on version 2.39 or later.
- Agent Relay v0.14.0 or later.
- Somewhere to run the Agent Relay container: a Kubernetes cluster that you can reach with
kubectlandhelm, or a Docker host. - The Coder CLI, signed in as an
owner. - An account with the provider you want to serve. Refer to the requirements on the Claude Code or Cursor page.
Plan your deployment
Choose one of these layouts:
- One relay per deployment. One relay serves every organization and both providers with a single service account.
- One relay per organization. Each relay has its own service account and its own provider credentials, which isolates organizations from each other. For example, a deployment with three organizations runs three relays.
In either layout, never give two relays the same provider credentials. Both relays claim the same sessions and compete over the same workspaces.
Plan provisioner capacity for each pool, because every pool you add can start more builds at once. Refer to Provision capacity for Agent Relay.
Create a service account
Agent Relay needs no site-wide role. At startup, it checks that its service account has the permissions that its pools need.
In every organization that a pool names, the service account needs these permissions:
| Resource | Actions |
|---|---|
organization_member | read |
workspace | create, read, start, update, delete |
workspace_secret | create |
On every template that a pool names, the service account needs read and use.
By default, a new template grants use, which includes read, to its organization's Everyone group, so a service account in the organization already has both.
Grant them in the template's permissions only if the template doesn't give the Everyone group access.
The organization-admin role in each organization covers all of these permissions.
To grant less, create an organization-scoped custom role with only the permissions above.
-
Create the service account in the organization that Agent Relay serves:
coder users create --service-account --username agent-relay --org <org> -
Add the service account to every other organization that a pool names.
-
Assign the
organization-adminrole, or your custom role, in each of those organizations. -
Create a long-lived token for the service account:
RELAY_TOKEN=$(coder tokens create --user agent-relay --name agent-relay-token --lifetime 8760h) -
If you install with Helm, store the token in a Kubernetes secret:
kubectl create secret generic agent-relay-coderd \ --namespace coder \ --from-literal=session-token="$RELAY_TOKEN"The Helm chart doesn't accept the token as a plain value.
Agent Relay verifies organization permissions at startup.
A missing organization permission stops the daemon.
For example, a service account without the workspace_secret create permission stops it with missing coderd capabilities [set-workspace-secrets[<org id>]].
Agent Relay verifies template permissions per pool.
A missing template permission degrades only that pool, and the pool recovers when you fix the template.
Why Agent Relay needs some of these permissions:
workspaceupdatelets it extend the autostop deadline of workspaces with an active session. It checks for this permission in every organization at startup, even if no template uses keepalive.workspace_secretcreatelets it deliver each session's provider credential to the workspace as an ephemeral workspace build secret. Agent Relay checks the Coder version once, at startup, to decide how to deliver credentials, so restart Agent Relay after you upgrade Coder.
Provision capacity for Agent Relay
Agent Relay builds run on your Coder provisioners, and time spent waiting for a free provisioner counts against each session's build deadline. We recommend dedicated external provisioners for Agent Relay templates, so relay builds don't compete with human workspace builds.
Each provider page explains how to size capacity for its pools:
To run a dedicated set of provisioners on Kubernetes:
-
Create a provisioner key with a tag for Agent Relay:
coder provisioner keys create relay-key --org <org> --tag workload=agent-relayCopy the key from the output. Coder shows it once.
-
Store the key in a Kubernetes secret:
kubectl create secret generic agent-relay-provisioner-key \ --namespace coder \ --from-literal=key="<provisioner-key>" -
Create a
provisioner-values.yamlfile:coder: env: - name: CODER_URL value: "https://coder.example.com" # One provisioner runs one build at a time. replicaCount: 10 provisionerDaemon: pskSecretName: "" keySecretName: agent-relay-provisioner-key keySecretKey: keyThe provisioners pick up the
workload=agent-relaytag from the key. -
Install the
coder-provisionerHelm chart with a version that matches your Coder server version:helm install coder-provisioner coder-v2/coder-provisioner \ --namespace coder \ --version <coder-version> \ --values provisioner-values.yaml -
When you push an Agent Relay template, use the same tag:
coder templates push <template> --provisioner-tag workload=agent-relay
If no provisioner with a matching tag is online, relay builds stay pending.
These coder-provisioner chart values affect Agent Relay:
| Value | Effect |
|---|---|
coder.replicaCount | The number of provisioner pods, which sets concurrent build capacity. The default is 1. |
provisionerDaemon.keySecretName, keySecretKey | The secret that holds the provisioner key. The key carries the provisioner's tags. |
coder.resources, nodeSelector, tolerations, affinity | Where pods run and how large they are. Plan for 1 vCPU and 1 GB of memory per 2 concurrent builds. |
provisionerDaemon.terminationGracePeriodSeconds | How long a stopping pod has to finish its build. The default is 600. |
The chart can install more than once under different release names, so you can run one set of provisioners for people and a tagged set for Agent Relay.
Prepare a workspace template
Each pool builds from one template. Every Agent Relay template must:
- Include the provider's Agent Relay module from the Coder Registry:
coder/agent-relay-claude-codeorcoder/agent-relay-cursor. - Declare the module's
agent_relay_statusmetadata item on itscoder_agentresource. Agent Relay reads this value to decide when to delete the workspace. If you rename or omit the key, Agent Relay never deletes the workspace. - Set
allow_user_cancel_workspace_jobs = true. Agent Relay cancels in-flight builds when it rebuilds or tears down a workspace. Without this setting, the pool degrades. - Provide a default for every template parameter, or set a value in the pool's
template_parameters.
The provider pages show the module block for each provider.
We also recommend that you:
- Use a dedicated template. Relay workspaces are ephemeral and machine-driven. A separate template keeps quotas, access control, and activity policy for people from interfering.
- Use quotas to control spend. Relay builds count against the session owner's quota, the same as any other workspace. Coder enforces quota during the build, so a build that exceeds the owner's quota fails and Agent Relay deletes the workspace. Cursor shows the build error in the developer's chat. Claude Code doesn't show it, so the reason appears only in Agent Relay's logs.
- Keep builds fast.
Pre-pull images, avoid persistent volumes, and bake the provider CLI into the image with
install_cli = false. - Own your workspace image.
ghcr.io/coder/agent-relay-workspace:latestis a reference image:codercom/enterprise-base:ubuntuwith both provider CLIs, rebuilt weekly. Use it as the base of your own image and add your tooling. - Restrict workspace egress. Agent sessions run unattended. Allow only the hosts on the provider page and the internal services that sessions need.
- Configure Git credentials in the template. Coder can supply each session owner's Git credentials through external authentication, but the template must declare the provider. Refer to the source control section on each provider page.
Start the worker after workspace setup
Agent Relay hands a workspace to the provider as soon as its build succeeds, not when the workspace agent is ready. The provider module starts the worker right away, so the agent can run commands while your startup scripts are still cloning repositories or installing tools.
To make the worker wait, put the setup that sessions need in coder_script resources or modules.
Then add a coder_script_order rule that runs the Agent Relay module after them:
resource "coder_script" "setup" {
agent_id = coder_agent.main.id
display_name = "Workspace setup"
run_on_start = true
timeout = 300
script = file("${path.module}/setup.sh")
}
data "coder_script_order" "relay_worker" {
rule {
phase = "start"
# Use module.claude_code_runner for Claude Code.
run = ["module.cursor_worker"]
after = ["coder_script.setup"]
}
}- Set
phase = "start", because the module also has a shutdown script. - Script ordering requires the Coder Terraform provider v2.20.0 or later.
- Script ordering also requires Coder v2.39.0 or later for the control plane, provisioners, and workspace agents. On earlier versions, Coder ignores the rule without an error, and the worker starts right away.
- Coder can't order the
startup_scriptattribute ofcoder_agent. Move setup steps that the worker depends on intocoder_scriptresources. - If a prerequisite fails, Coder skips the module's scripts and the worker never starts.
To start the worker even when setup fails, set
requires = "completion"on the rule. - The session waits while prerequisites run.
Set a
timeouton each prerequisite, and keep setup well under 15 minutes, after which Agent Relay deletes a workspace whose worker hasn't started.
Configure provider pools
Follow the steps on the provider page to create the provider credential, add the module to your template, and write the pool configuration:
Then return to this page to install Agent Relay.
Install Agent Relay
| Artifact | Location | Versioning |
|---|---|---|
| Image | ghcr.io/coder/agent-relay | X.Y.Z, X.Y, latest per release |
| Helm chart | oci://ghcr.io/coder/chart/coder-agent-relay | Matches the image it deploys |
Both are public and need no pull credentials. For every option and Helm value, refer to the Agent Relay configuration reference.
-
Create a
values.yamlfile with your Coder URL, the token secret from step 1, and the pools and secrets from the provider page:coderd: url: https://coder.example.com sessionToken: secretName: agent-relay-coderd key: session-token pools: claudeCode: [] # Refer to the Claude Code page. cursor: [] # Refer to the Cursor page. secretEnv: [] # One entry per pool secret_env. -
Install the chart:
helm install agent-relay oci://ghcr.io/coder/chart/coder-agent-relay \ --namespace coder \ --version <version> \ --values values.yaml
The chart runs a single replica with strategy: Recreate and creates no Kubernetes RBAC.
Agent Relay talks to the Coder control plane over HTTPS and never calls the Kubernetes API.
Pin a chart version in production instead of tracking latest.
The image is a static binary on a distroless base. It needs no writable path and no baked-in configuration.
-
Create a
config.yamlfile with your Coder URL and the pools from the provider page:# The Coder deployment to serve. It must be licensed. coderdURL: https://coder.example.com logLevel: info logFormat: json claudeCodeRunnerPools: [] # Refer to the Claude Code page. cursorWorkerPools: [] # Refer to the Cursor page. -
Run the container, passing the service account token and each pool's secret as environment variables:
docker run --rm \ -e CODER_AGENT_RELAY_CODERD_SESSION_TOKEN="$RELAY_TOKEN" \ -e CLAUDE_ENVIRONMENT_SECRET \ -e CURSOR_SERVICE_ACCOUNT_KEY \ -e CODER_AGENT_RELAY_PROMETHEUS_ENABLE=true \ -e CODER_AGENT_RELAY_PROMETHEUS_ADDRESS=0.0.0.0:2112 \ -e CODER_AGENT_RELAY_CONFIG_PATH=/etc/agent-relay/config.yaml \ -v "$PWD/config.yaml:/etc/agent-relay/config.yaml:ro" \ -p 2112:2112 \ ghcr.io/coder/agent-relay:<version>Pass one
-eflag for each pool'ssecret_envvariable, and leave out the providers you don't serve.
Set the session token with CODER_AGENT_RELAY_CODERD_SESSION_TOKEN.
Do not put it in config.yaml.
Pass each pool's secret through the environment variable that its secret_env names, not inline in config.yaml.
In a container, the metrics listener binds to loopback by default.
Bind it to 0.0.0.0 to reach it from outside the container.
Agent Relay release binaries for running as a systemd service aren't published yet.
Verify the deployment
Agent Relay fails fast at startup. An invalid token, an unlicensed deployment, an unsupported Coder version, or a missing organization permission stops the daemon with an explicit message.
-
Read the daemon's logs:
# Kubernetes kubectl logs -n coder deploy/coder-agent-relay # Docker docker logs <container> -
Confirm that the
authenticated against coderdlog line showsbuild_secrets=true. -
Confirm that each pool logs that it's ready.
A Claude Code pool logs
template verified, polling, and a Cursor pool logspool verified, serving. -
Confirm that Agent Relay is ready. With Helm, the pod becomes
Readyonly after at least one pool passes verification. With Docker, runcurl -sS http://localhost:2112/readyz, which printsokwhen a pool is serving and lists any pool that is still verifying or has failed. -
Start a session from the provider's client and select the pool.
-
In Coder, confirm that a workspace is created for your user.
-
End the session and confirm that Agent Relay deletes the workspace.
For logs, metrics, and troubleshooting, refer to Monitor Agent Relay.
Workspace lifecycle
The provider module publishes the worker's state through the agent_relay_status metadata item.
Agent Relay's reaper reads it on each sweep:
- When the worker exits normally, Agent Relay deletes the workspace.
- When the worker fails or disappears, Agent Relay deletes the workspace and records the failure.
- When a build fails or is canceled, Agent Relay deletes the workspace on the next sweep.
- Agent Relay deletes a workspace that is still building or not started after 15 minutes.
Keep these Coder settings in mind for relay templates:
- Autostop: Agent Relay extends the autostop deadline of a workspace while a session is attached, if the template declares the module's
agent_relay_session_attachedmetadata item. Without that item, the template's autostop schedule stops relay workspaces at the deadline, even in the middle of a session. Refer to Keep attached workspaces running. - Stopped workspaces: Agent Relay doesn't delete stopped workspaces. They remain until someone deletes them or dormancy auto-deletion removes them.
- Quotas: Relay builds count against the session owner's quota. Refer to the template recommendations above.
- Deleted work: Agent Relay deletes the workspace when the session ends. Unpushed commits and files are lost.
Keep attached workspaces running
To keep autostop from stopping a workspace in the middle of a session, add the module's agent_relay_session_attached metadata item next to agent_relay_status:
resource "coder_agent" "main" {
# ...
metadata {
key = "agent_relay_session_attached"
display_name = "Session attached"
# Use module.claude_code_runner for Claude Code.
script = module.cursor_worker.session_attached_metadata_script
interval = 30
timeout = 5
}
}On each sweep, Agent Relay reads this item.
When the value is attached and the sample is less than two minutes old, Agent Relay extends a deadline that is less than 35 minutes away to 45 minutes from now.
To change these durations, set --reap-keepalive-refresh-before and --reap-keepalive-bump-by.
- Keep the metadata
intervalwell under two minutes, or Agent Relay can't rely on a fresh sample. - The image needs
curl, and the Cursor module also needsawk. - Agent Relay doesn't extend a workspace past its template's maximum deadline, and doesn't extend workspaces that have no autostop deadline.
- Keepalive requires module version 1.0.0 or later and Agent Relay v0.14.0 or later.
Workspace names start with a provider prefix: cc- for Claude Code and cur- for Cursor.
Upgrade from early access
Agent Relay v0.14.0 and module version 1.0.0 deliver provider credentials as workspace build secrets, which need Coder 2.39 or later.
Early access module versions, agent-relay-claude-code 0.4.0 or earlier and agent-relay-cursor 0.7.0 or earlier, don't support build secrets.
Upgrade Coder, Agent Relay, and every Agent Relay template in the same maintenance window.
If you need help planning the upgrade, contact Coder.
-
Upgrade Coder to 2.39 or later.
-
Grant the service account
workspaceupdateandworkspace_secretcreatein every organization that a pool names.Agent Relay v0.14.0 doesn't start without them. The
organization-adminrole already includes both. -
Convert each Cursor pool's
idle_release_timeoutfrom seconds to a duration, such as10mfor600.0still turns off idle release. -
Upgrade Agent Relay to v0.14.0 or later.
-
Update every Agent Relay template to module version 1.0.0.