Skip to content

dev()

The dev() function declares a dev target — a Kubernetes workload you want to develop against. Each call registers a target that CT will patch, port-forward, sync files to, and optionally attach a terminal.

function dev(name: string, options: DevTargetOptions): void
ParameterTypeRequiredDescription
namestringyesWorkload name. If the name matches a resource from main.ct, the selector is resolved automatically.
optionsDevTargetOptionsyesTarget configuration object.

Returns void — the call registers the target internally; there is no return value.

interface DevTargetOptions {
selector?: Record<string, string>;
container?: string;
sync?: SyncRule[];
ports?: (number | [number, number])[];
terminal?: string;
probes?: boolean;
replicas?: number;
env?: EnvVar[];
workingDir?: string;
image?: string;
command?: string[];
}
selector?: Record<string, string>

Manual label selector used to find pods. When omitted, CT automatically extracts spec.selector.matchLabels from the Deployment/StatefulSet/DaemonSet named name in your rendered main.ct output.

Use this for external resources not defined in main.ct (operators, shared databases, etc.):

dev("postgres", {
selector: { "cnpg.io/cluster": "postgres" },
ports: [[5432, 5432]],
});
container?: string

Name of the container to target inside the pod. Defaults to the first container in the pod spec. Use this when a pod has multiple containers (e.g. sidecars) and you want to patch a specific one.

dev("api", {
container: "app",
command: ["npm", "run", "dev"],
});
sync?: SyncRule[]

File sync rules — stream file changes from your local machine into the running container. See SyncRule below.

dev("api", {
sync: [{
from: "./src",
to: "/app/src",
exclude: ["/node_modules", "/.git", "*.log"],
}],
});
ports?: (number | [number, number])[]

Port forwarding rules. Each entry is either:

  • A single number — same port for both local and remote (e.g. 9229localhost:9229 → container:9229).
  • A tuple [local, remote] — different local and remote ports.

Ports are forwarded with automatic reconnect when pods restart.

dev("api", {
ports: [
[3000, 8080], // localhost:3000 → container:8080
9229, // localhost:9229 → container:9229 (same port)
],
});
terminal?: string

Shell command to execute in an attached terminal session. Only one target can have a terminal — the first target with .terminal set gets attached. When a terminal is active, log streaming is disabled (terminal takes over stdout).

dev("api", {
terminal: "npm install && bash",
});

If workingDir is also set, the terminal command is prefixed with cd <workingDir> &&.

probes?: boolean // default: false

Whether to keep liveness/readiness probes on the workload. Default is false — probes are removed during dev mode to prevent Kubernetes from restarting your dev container while you’re working.

dev("api", {
probes: true, // keep probes active
});
replicas?: number

Override the replica count for the workload. Useful for scaling down to a single replica during development.

dev("api", {
replicas: 1,
});
env?: EnvVar[]

Add or override environment variables on the target container. See EnvVar below.

dev("api", {
env: [
{ name: "NODE_OPTIONS", value: "" },
{ name: "DEBUG", value: "app:*" },
],
});
workingDir?: string

Override the container’s working directory. Also affects the terminal command — if both terminal and workingDir are set, the terminal session starts with cd <workingDir>.

dev("api", {
workingDir: "/app",
terminal: "bash",
// effective terminal command: cd "/app" && bash
});
image?: string

Override the container image. Useful when the production image is too minimal for development (e.g. missing shell, node, etc.).

dev("api", {
image: "node:20-bookworm",
command: ["npm", "run", "dev"],
});
command?: string[]

Override the container entrypoint command. Replaces spec.containers[].command on the patched workload.

dev("api", {
command: ["npm", "run", "dev"],
});

interface SyncRule {
from: string; // local directory path (relative to project root)
to: string; // absolute path inside the container
exclude?: string[]; // glob patterns to exclude
polling?: boolean; // use polling instead of fsnotify (default: false)
}
FieldTypeRequiredDescription
fromstringyesLocal directory to sync from (relative to project root).
tostringyesAbsolute path inside the container to sync to.
excludestring[]noGlob patterns to exclude from sync (e.g. /node_modules, /.git).
pollingbooleannoUse polling mode instead of fsnotify. Useful for network filesystems or Docker Desktop volumes. Default: false.

Sync implementation uses tar + kubectl exec:

  • Initial sync — full directory tar streamed on startup.
  • Incremental sync — fsnotify watches for changes, 300ms debounce, only changed files are synced.
// As passed to dev():
type PortInput = number | [local: number, remote: number];
// Parsed internally:
interface PortRule {
local: number;
remote: number;
}

When a single number is passed, both local and remote are set to that value.

interface EnvVar {
name: string; // environment variable name
value: string; // environment variable value
}
FieldTypeRequiredDescription
namestringyesEnvironment variable name. Must be non-empty.
valuestringyesEnvironment variable value. Can be empty string.

When you write dev("api", { ... }) without a selector, CT automatically:

  1. Finds the workload named "api" in the rendered main.ct output.
  2. Extracts spec.selector.matchLabels from the Deployment / StatefulSet / DaemonSet.
  3. Uses those labels to find running pods.

Supported workload kinds: Deployment, StatefulSet, DaemonSet.

For external resources, pass selector explicitly.

For each dev target, CT patches the rendered workload before applying to the cluster:

WhatBehavior
ProbesRemoved unless probes: true
ReplicasOverridden if replicas is set
CommandReplaced if command is set
EnvMerged/overridden if env is set
ImageReplaced if image is set
Working directoryReplaced if workingDir is set

All patches target the container specified by container (or the first container by default).

dev("api", {
replicas: 1,
image: "node:20-bookworm",
command: ["npm", "run", "dev"],
workingDir: "/app",
env: [
{ name: "NODE_OPTIONS", value: "--inspect=0.0.0.0:9229" },
{ name: "DEBUG", value: "app:*" },
],
sync: [
{ from: "./", to: "/app", exclude: ["/node_modules", "/.git", "/dist"] },
],
ports: [
[3000, 8080],
9229,
],
terminal: "npm install && bash",
probes: false,
});