Self-hosted Runners
Run InfraDots in your own environment: the agent runner reviews pull requests and implements infrastructure changes with your own model account, and the executor runs your Terraform, OpenTofu and Terragrunt plans and applies. Both register with a worker pool and connect out to InfraDots over HTTPS. Nothing needs to reach into your network.
How it works
-
You create a pool in InfraDots and start runners with its registration token.
-
Each runner registers, then polls InfraDots for work routed to its pool.
-
For each run:
- The agent runner gets a token scoped to that one run and a short-lived git credential for that run's repository. It clones the repository, calls your model provider, and opens or comments on the pull request through InfraDots.
- The executor downloads the run's configuration from InfraDots and runs Terraform, OpenTofu or Terragrunt with your workspace's variables.
Then the runner reports the result.
-
Results, logs and history appear in InfraDots as for any other run.
Each runner handles one run or job at a time. Run more replicas for more throughput.
Requirements
- Kubernetes 1.25+ (for the Helm chart), or any Docker host. The images are
linux/amd64andlinux/arm64, so they also run under Docker Desktop, Colima or OrbStack on Intel and Apple Silicon Macs. - Outbound HTTPS to the destinations under Network.
- For the agent runner: a model account (see Your model account).
1. Create the pools
In Organization settings → Worker Pools, create a pool of kind agent, kind executor, or both, and copy each registration token. See Worker Pools.
2a. Install on Kubernetes (Helm)
helm repo add infradots https://infra-dots.github.io/helm-charts
helm repo update
kubectl create namespace infradots
kubectl -n infradots create secret generic infradots-agent-pool --from-literal=REGISTRATION_TOKEN=<agent pool token>
kubectl -n infradots create secret generic anthropic --from-literal=ANTHROPIC_API_KEY=<your key>
helm -n infradots install runners infradots/infradots-runner \
--set agent.registration.existingSecret=infradots-agent-pool \
--set agent.model.existingSecret=anthropic
To run the executor too, create its pool's Secret and add:
--set executor.enabled=true \
--set executor.registration.existingSecret=infradots-executor-pool
The chart is hardened by default:
- non-root, with a read-only root filesystem and every Linux capability dropped
- no Kubernetes API token mounted
- size-capped scratch space
It also supports workload identity, an egress proxy and an optional NetworkPolicy. Ready-made values for Bedrock, Vertex, an egress proxy and more are in the chart's examples, and every value is in the chart README.
Check the agent runner's setup:
kubectl -n infradots exec deploy/runners-infradots-runner-agent -- idp-agent doctor
2b. Run with Docker (VMs and on-prem)
Agent runner:
docker run -d --name infradots-agent --restart unless-stopped \
-e IDP_URL=https://app.infradots.com \
-e REGISTRATION_TOKEN=<agent pool token> \
-e ANTHROPIC_API_KEY=<your key> \
public.ecr.aws/e5i7i1j1/idp-agent:latest
Executor:
docker run -d --name infradots-executor --restart unless-stopped \
-e REGISTRATION_TOKEN=<executor pool token> \
public.ecr.aws/e5i7i1j1/idp-executor:latest
Check the agent runner's setup with docker exec infradots-agent idp-agent doctor.
With Docker Compose: keep the tokens in an .env file next to compose.yaml.
services:
agent:
image: public.ecr.aws/e5i7i1j1/idp-agent:latest
restart: unless-stopped
environment:
IDP_URL: https://app.infradots.com
REGISTRATION_TOKEN: ${AGENT_POOL_TOKEN}
ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY}
stop_grace_period: 60s
executor:
image: public.ecr.aws/e5i7i1j1/idp-executor:latest
restart: unless-stopped
environment:
REGISTRATION_TOKEN: ${EXECUTOR_POOL_TOKEN}
# On shutdown the executor finishes its current job first: allow for your longest apply.
stop_grace_period: 1h
As a systemd service: to run the agent runner on boot, put its variables in /etc/infradots/agent.env
(IDP_URL=…, REGISTRATION_TOKEN=…, ANTHROPIC_API_KEY=…, mode 0600) and add
/etc/systemd/system/infradots-agent.service:
[Unit]
Description=InfraDots agent runner
After=docker.service
Requires=docker.service
[Service]
ExecStartPre=-/usr/bin/docker rm -f infradots-agent
ExecStart=/usr/bin/docker run --rm --name infradots-agent --env-file /etc/infradots/agent.env public.ecr.aws/e5i7i1j1/idp-agent:latest
ExecStop=/usr/bin/docker stop -t 60 infradots-agent
Restart=always
[Install]
WantedBy=multi-user.target
Then systemctl daemon-reload && systemctl enable --now infradots-agent. For the executor, use its image and token,
and raise the stop timeout (docker stop -t 3600, and TimeoutStopSec=3700 in [Service]) so a running apply can
finish.
💡 Tip
[!tip]
Pin a version (idp-agent:0.5.0, idp-executor:1.9.0) instead of latest in production, and upgrade deliberately.
Your model account
InfraDots chooses which Claude model each agent uses, and your runner sends the call through your own account. Prompts, tools and results are the same whichever provider you use.
| Provider | MODEL_PROVIDER | Credentials | MODEL_MAP |
|---|---|---|---|
| Anthropic API | anthropic (default) | ANTHROPIC_API_KEY | not needed |
| AWS Bedrock | bedrock | the AWS credential chain (IRSA, EKS Pod Identity, an instance profile, or AWS_* variables), plus AWS_REGION | required |
| Google Vertex AI | vertex | Application Default Credentials or GKE Workload Identity, plus CLOUD_ML_REGION and ANTHROPIC_VERTEX_PROJECT_ID | required |
| Azure AI Foundry | foundry | ANTHROPIC_FOUNDRY_API_KEY and ANTHROPIC_FOUNDRY_RESOURCE | required |
MODEL_MAP is JSON that maps InfraDots' model names to your provider's model IDs. For example, for Bedrock:
{
"claude-opus-5": "eu.anthropic.claude-opus-5-v1:0",
"claude-sonnet-5": "eu.anthropic.claude-sonnet-5-v1:0"
}
With the Helm chart, set agent.model.provider and agent.model.map instead.
Map every model InfraDots uses. idp-agent doctor sends a one-token request to each mapped model. A run that
needs a model the map lacks stops before cloning anything, and its message names the model to add.
Permissions. On Bedrock, the runner's role needs bedrock:InvokeModel and
bedrock:InvokeModelWithResponseStream on the mapped models or inference profiles. On Vertex AI, its service account
needs roles/aiplatform.user in the project. Enable the Claude models in your account's model catalog first.
ℹ️ Info
[!info] Runs on your own model account don't count against your plan's AI interactions. InfraDots still records their token usage, so you can see it.
Configuration
Both runners are configured through environment variables. The Helm chart sets them for you from its values.
Agent runner (idp-agent)
| Variable | Default | |
|---|---|---|
IDP_URL | (required) | Your InfraDots URL, e.g. https://app.infradots.com |
REGISTRATION_TOKEN | (required) | The agent pool's token |
RUNNER_NAME | hostname | How the runner shows in its pool |
MODEL_PROVIDER / MODEL_MAP | anthropic / {} | See Your model account |
WORK_DIR | $TMPDIR/idp-agent | Where runs clone. Must be writable and allow running programs (not noexec) |
TERRAFORM_RELEASES_URL / OPENTOFU_RELEASES_URL | HashiCorp / GitHub | Mirrors for the fmt check's downloads |
HTTPS_PROXY / NO_PROXY | Egress proxy | |
LOG_LEVEL | INFO |
Executor (idp-executor)
| Variable | Default | |
|---|---|---|
REGISTRATION_TOKEN | (required) | The executor pool's token |
REGISTRATION_HOST | app.infradots.com | Your InfraDots host |
EXECUTABLE_CACHE_ENABLED | false | Keep downloaded Terraform/OpenTofu binaries between jobs |
TERRAFORM_RELEASES_URL / TERRAGRUNT_RELEASES_URL | HashiCorp / GitHub | Mirrors for binary downloads |
HTTPS_PROXY / NO_PROXY | Egress proxy | |
LOG_LEVEL | INFO |
Network
The runners make outbound connections only:
| Destination | Runner | Why |
|---|---|---|
app.infradots.com (your InfraDots URL), port 443 | both | Registration, receiving work, reporting results |
| Your git host: github.com, gitlab.com, bitbucket.org or your own | agent | Cloning, and pushing its branches |
| Git hosts your Terraform modules come from, if any | executor | terraform init of git:: module sources |
| Your model provider's API | agent | Model calls |
releases.hashicorp.com, api.github.com and GitHub release downloads | both | Terraform/OpenTofu/Terragrunt binaries (can be mirrored) |
| Terraform provider and module registries | executor | terraform init |
Kubernetes NetworkPolicy can't filter by hostname. To allow only these destinations, route the runners through an
egress proxy (HTTPS_PROXY), or use your CNI's FQDN policies.
Security
- Per-run credentials. For each agent run, InfraDots issues the runner a token scoped to that run, and a git credential for that run's repository only, valid for the run. The runner holds no long-lived VCS secret. The executor doesn't need git access to your repositories: it downloads each run's configuration from InfraDots.
- The registration token is good only for its own pool, and only for that pool's kind of work. Rotate it on the pool if it may have leaked; see Rotating a token.
- Clean workspace per run. Each run works in a fresh directory, which is removed or wiped when the run ends. The agent runner passes git credentials to git at run time, never writing them into the clone or its config.
- Your model calls stay in your account. The agent runner calls your provider directly; prompts and code don't pass through an InfraDots model account.
Upgrades
Runners and InfraDots are versioned separately. InfraDots supports the previous runner version, and a runner too old for the work InfraDots sends is marked needs upgrade on its pool, so you see it before runs wait on it.
- Helm:
helm repo update && helm -n infradots upgrade runners infradots/infradots-runner --reuse-values - Docker: pull the new image tag and recreate the container.
On shutdown the agent runner reports the run it was carrying out as interrupted, and the executor finishes its current job before exiting.
Troubleshooting
idp-agent doctor checks everything the agent runner needs from where it runs, and says what to fix:
ok configuration: runner runners-infradots-runner-agent-5d8f7-x2k4p, models via bedrock
ok work directory: /work is writable and can run programs
ok git: git version 2.47.3
ok platform: https://app.infradots.com accepted the registration token
ok model anthropic:claude-opus-5 via bedrock: answered
ok model anthropic:claude-sonnet-5 via bedrock: answered
All checks passed.
| Symptom | Check |
|---|---|
| The runner doesn't appear in its pool | Its log: it retries registration while InfraDots is unreachable. Check IDP_URL, egress to it, and that the token is the pool's current one |
doctor fails on the work directory | The volume is mounted noexec, or read-only. Use a writable, exec-allowed path for WORK_DIR |
doctor fails on a model | The provider's own error is shown: credentials, region, model access, or a wrong ID in MODEL_MAP |
| Runs wait, then fail | The message says whether no runner was online, the runners need an upgrade, or all were busy. See Worker Pools |
