| name | injector-dev |
| description | Build, deploy, and test Datadog Agent components (agent, cluster-agent, operator, CSI driver) on a local Kubernetes cluster using the injector-dev CLI. Use when the user wants to iterate on local Agent or Operator change, spin up a local k8s test environment. |
| model | sonnet |
injector-dev
injector-dev is a CLI that turns the manual loop of build image → push → manage a cluster → deploy workloads by hand
into a single declarative command. You describe a scenario in YAML — which
Agent components to deploy, how to configure them, and what test workloads to
run — and injector-dev apply brings the whole environment up on a local
Kubernetes cluster.
Scenarios are reproducible and shareable: tear an environment down and recreate
it identically at any time.
When to use this skill
- Iterating on local
datadog-agent, datadog-operator, or Helm
chart changes and needing them running on a real cluster.
- Reproducing an APM auto-instrumentation / injection bug locally.
- Writing, editing, or debugging a
scenario.yaml.
- Pinning a test environment against a specific released version, a CI pipeline
artifact, or the agent
main branch.
Prerequisites
If you already have colima and/or minikube running, shut them down before
using injector-dev to avoid conflicts.
Installation
git clone https://github.com/DataDog/injector-dev
cd injector-dev
make install # builds and installs the binary to /usr/local/bin (uses sudo)
Configuration (~/.injector-dev/config.yaml)
A practical starting config:
---
platform: kind
builder:
code_root: "/Users/<USER>/dd"
dev_container:
name: "injector-dev-builder"
enabled: true
persist: true
installer:
repo_root: "/Users/<USER>/dd/injector-dev"
Key points:
builder.code_root — parent directory holding your component repos. The
tool derives each repo path as code_root/<repo-name> (e.g.
code_root/datadog-agent), so all repos must be siblings under this dir.
builder.dev_container.persist: true — leaves the build container running
between applies. First build takes several minutes (installing deps); later
builds drop to ~30 seconds.
installer.repo_root — path to your local injector-dev checkout.
- Every config key can be overridden by env var: prefix with
INJECTOR_DEV_
and replace dots with underscores, e.g.
INJECTOR_DEV_INSTALLER_API_KEY, INJECTOR_DEV_BUILDER_CODE_ROOT.
- API/App key resolution order:
config.yaml (installer.api_key/app_key)
→ then DD_API_KEY / DD_APP_KEY env vars.
Profiles (multiple configs)
To manage several environments (staging, sandbox, org2, …), drop additional
config files at ~/.injector-dev/<profile>.yaml and select one per command:
injector-dev apply -f scenario.yaml --profile sandbox
Omitting --profile uses config.yaml.
Remote clusters: the workspace platform
Instead of a local cluster, injector-dev can run the kind cluster on a remote
Datadog Workspace
VM. Image builds still happen locally — only the cluster and workloads run on
the workspace. Useful when your laptop is resource-constrained or you want a
beefier, disposable environment.
How it works: docker build runs locally; the image is streamed to the workspace
(docker save | ssh … kind load); kind is installed on the workspace
automatically on first use (workspaces ship with docker but not kind); and the
remote cluster's API server is exposed to your machine through a persistent SSH
tunnel, with an injector-dev-ws-<name> context merged into ~/.kube/config and
made current — so local kubectl/helm/k9s work against it transparently.
Prerequisites
Selecting the workspace
The workspace name comes from the --workspace flag or a scenario's
platform.workspace (flag wins). It is intentionally not read from the global
config.yaml.
In a scenario (picked up by apply):
platform:
type: workspace
workspace: firstname-lastname
name: my-cluster
reset: false
helm:
Or by flag — required for start/stop/reset, which don't read a scenario:
injector-dev apply -f scenario.yaml --workspace firstname-lastname --build
injector-dev stop --platform=workspace --workspace firstname-lastname
Usage
# bring the remote cluster up + deploy (build local, load remote)
injector-dev apply -f scenario.yaml --workspace <name> --build
# local kubectl now targets the remote cluster through the tunnel
kubectl get pods -A
# tear down the remote cluster, tunnel, and kube-context
injector-dev stop --platform=workspace --workspace <name>
Notes
start/apply are non-destructive when the cluster already exists: they reuse
it and just re-establish the tunnel and switch your kube-context (so
apply --reset=false still re-points kubectl at the workspace).
- The SSH tunnel is persistent (survives after the command exits) so local
kubectl keeps working. injector-dev stop closes it.
- Inspect open tunnels:
ls ~/.injector-dev/*.sock, and check one with
ssh -O check -S ~/.injector-dev/workspace-<name>.sock workspace-<name>.
- You can also inspect the cluster on the workspace directly:
ssh workspace-<name>
then kubectl (kind writes a kubeconfig there too).
The core loop: apply
apply is the primary command. It (optionally) resets/starts the cluster,
installs the Datadog stack via Helm or the Operator, deploys any apps/manifests,
and waits for health.
injector-dev apply -f workloads/my-feature/scenario.yaml # deploy
injector-dev apply -f workloads/my-feature/scenario.yaml --build # build local source first
apply flags
| Flag | Default | Purpose |
|---|
-f, --file | — | Path to the scenario file (required). |
--build | false | Run build steps for any component with build: {}. |
--reset | true | Reset the cluster before applying. Set --reset=false for fast iteration. |
--hard | false | Hard reset (rebuilds the VM — colima only). |
--wait | true | Wait for the install to become healthy. |
--skip-agent-validation | false | Skip the "agent started successfully" check. |
-t, --app-image-tag | — | Global image tag applied to all test apps. |
--helm-skip-schema-validation | false | Pass --skip-schema-validation to Helm (useful with local chart changes). |
--profile | config.yaml | Select a config profile. |
--platform | from config | Override the driver. If you set it on start, you must set it on every apply. |
--workspace | — | Remote workspace name for --platform=workspace (see Remote clusters). Global flag — also valid on start/stop/reset. |
--debug | false | Verbose logging. |
Scenario files
A scenario is helm: or operator:, optionally preceded by a platform:
block. Keep each scenario in its own directory alongside its manifests:
workloads/
├── hello-world/
│ └── scenario.yaml
├── my-feature/
│ ├── scenario.yaml
│ └── redis.yaml
Generate a starter template with injector-dev new --type helm --output scenario.yaml
(add --edit to open it in $EDITOR).
platform block
platform:
type: kind
name: my-dev-cluster
reset: false
Precedence for both platform and reset: CLI flag > scenario platform: block > default.
Deploying a pre-built version (simplest case)
---
platform:
type: kind
name: hello-world
reset: false
helm:
versions:
agent:
version: "7.81.0"
cluster_agent:
version: "7.81.0"
injector: "0.60.0"
config:
datadog:
kubelet:
tlsVerify: false
clusterAgent:
enabled: true
Building from local source
Add build: {} to any component and pass --build:
---
platform:
type: kind
name: my-dev-cluster
reset: false
helm:
versions:
agent:
version: "7.81.0"
build: {}
cluster_agent:
version: "7.81.0"
build: {}
injector:
version: "0.60.0"
build: {}
config:
datadog:
kubelet:
tlsVerify: false
clusterAgent:
enabled: true
injector-dev apply -f scenario.yaml --build
You can pin a build tag with build: { tag: "dev.1" } (defaults to a
git-derived tag otherwise).
Version / image field reference
Each of agent, cluster_agent, injector, csi, (and operator in operator
scenarios) accepts either a string or a map:
injector: "0.60.0"
agent:
tag: "7.81.0"
repository: registry.ddbuild.io/ci/datadog-agent/agent
pullPolicy: IfNotPresent
build:
tag: "dev.1"
Pin to a CI pipeline / branch artifact — reproduce a coworker's PR build (or
any pipeline build) without compiling locally:
helm:
versions:
agent:
repository: registry.ddbuild.io/ci/datadog-agent/agent
tag: v<PIPELINE>-<COMMIT>-7-amd64
cluster_agent:
repository: registry.ddbuild.io/ci/datadog-agent/cluster-agent
tag: v<PIPELINE>-<COMMIT>-amd64
Full helm: schema
| Field | Description |
|---|
versions | agent, cluster_agent, injector, csi image specs (see above). |
config | YAML passed to Helm as the values file (the datadog / clusterAgent / agents tree). |
configFile | Path to an external Helm values file instead of inline config. |
localChartPath | Install from a local chart dir instead of the public repo (see below). |
apps | List of test apps deployed via the base app chart (see Apps). |
namespaces | Explicitly create namespaces with specific labels. |
manifests | Raw Kubernetes YAML files applied after the agent + apps. |
charts | Additional Helm charts to install alongside. |
Test apps
Apps are deployed through a shared base chart (schema in apps/base/values.yaml).
Sample apps live in apps/: c, dotnet, java, js, php, python, ruby.
helm:
apps:
- name: python
namespace: application
values:
image:
repository: registry.ddbuild.io/ci/injector-dev/python
tag: "2cd78ded"
service:
port: "8080"
podLabels:
language: python
tags.datadoghq.com/env: local
env:
- name: DD_TRACE_DEBUG
value: "true"
- name: DD_APM_INSTRUMENTATION_DEBUG
value: "true"
App fields: name, namespace, values (or valuesFile), build (build the
app image locally), injector (override injector image per-app), wait.
Kubernetes health checks hit each pod's endpoints, so a running sample app
automatically produces traces once instrumentation is enabled — a quick way to
confirm injection is working.
Raw manifests & namespaces
helm:
namespaces:
- name: cache
labels:
team: platform
manifests:
- path: "redis-with-password.yaml"
namespace: cache
Local Helm chart
If you're also changing the Datadog Helm chart, point at a local copy.
injector-dev then skips the repo add/update and installs from the path: