Steckling / Docs
Search docs… ⌘K GitHub ↗
esc
{{ r.title }} {{ r.tag }}
{{ r.snippet }}
No matches — try “ports”, “compose”, or a command name
↑↓ navigate↵ openesc close
START HERE
Concepts
Install
Quickstart
REFERENCE
Commands
steckling.yml
GUIDES
Drive it from Claude (MCP)
START HERE

Concepts

The mental model behind Steckling: a git worktree + an isolated Docker service stack per branch. Each branch gets its own folder, its own services, its own data, on its own ports. Nothing collides.

../myapp-trees/feature-a/ steck up → postgres :31140 (own data) ../myapp-trees/feature-b/ steck up → postgres :44870 (own data) main/ steck up → postgres :20030 ~/.steckling/registry.json tracks them all

Three moving parts

1

A folder per branch. Via git worktree, each branch lives in its own directory. Switching branches is switching folders.

2

Private services per branch. A separate docker compose project per branch — own containers, own volumes, a free host port Steckling picks for you.

3

Env injection. Steckling writes the right DATABASE_URL (etc.) into a gitignored file your app reads, then runs your normal dev command.

What Steckling doesn't do

It's stack-agnostic: your app runs natively the way it always has (npm run dev, rails s, go run, …). Steckling only ever deals with git, Docker, and environment variables. It never runs your app for you and knows nothing about your framework. It works for any language that reads its config from the environment.

Not a process manager. It runs your one app.run command in the foreground; it doesn't supervise multiple processes. Run workers/extra processes via a second terminal or steck exec. (steck cockpit doesn't change this — the TUI is delegated to mprocs, and its service panes are log viewports over Docker-managed containers.)

Next: Install →
START HERE

Install

Requires git and Docker. The CLI is a single self-contained binary — no runtime to install.

Install script

Downloads a binary to ~/.local/bin:

$ curl -fsSL https://raw.githubusercontent.com/timd/steckling/main/install.sh | sh

Homebrew

$ brew install timd/steckling/steckling

Homebrew treats a binary-only formula as a source build, so it runs its compiler-toolchain check even though nothing gets compiled — on a fresh macOS it can refuse with “Xcode too outdated”. If it does, update the Command Line Tools (xcode-select --install) or just use the install script above.

From source

The engine is TypeScript on Bun:

$ bun run engine/src/cli.ts <command>

To get a real steck command on your PATH from a source checkout, compile it with the project's build script (its flags matter — it disables Bun's automatic .env loading, which would otherwise leak worktree .env files into every app steckling runs):

$ bun run build # → ../dist/steck $ cp ../dist/steck ~/.local/bin/steck

Optional extras

mprocs — only needed for the steck cockpit TUI:

$ brew install mprocs

Railway CLI — only needed for steck deploy

Verify

$ steck --version $ steck doctor # git, Docker daemon, docker compose, plus soft checks for mprocs + railway
← Concepts Next: Quickstart →
START HERE

Quickstart

Run steck init and the wizard writes both files (plus the .gitignore entry) for you — presets for Postgres, MySQL, Redis, Mongo, and RabbitMQ, and your detected run command. Or add the two files by hand — a services compose file and a steckling.yml.

1 · steckling.yml

version: 1
services:
  compose: ./compose.steckling.yml
  expose:
    postgres:
      container: 5432
      env: DATABASE_URL
      url: "postgres://app:app@localhost:{port}/app"
app:
  run: "npm run dev"  # any language
hooks:
  provision: "npm run migrate && npm run seed"

2 · compose.steckling.yml

services:
  postgres:
    image: postgres:16
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: app
      POSTGRES_DB: app
    # Steckling injects the host port
    ports: ["${STECKLING_PORT_POSTGRES}:5432"]
    volumes: [pgdata:/var/lib/postgresql/data]
volumes: { pgdata: {} }

3 · Run it

$ steck up               # start this branch: services + provision + run the app
$ steck new feature/x    # a second branch, in its own folder…
$ cd ../myapp-trees/feature/x
$ steck up               # …with its own DB, running in parallel
$ steck list             # see every worktree, its status + ports

Prefer a full-terminal view? steck cockpit does the same as up but opens a TUI — your app in one pane, live service logs in the others — and quitting it parks the whole stack (needs mprocs; see commands).

Already have a project? See Adopting an existing project in the repo docs for adding Steckling to an app with an existing database setup.
← Install Next: Commands →
REFERENCE

Commands

Everything the steck CLI can do.

steck initSet up a repo interactively — service presets, detected run command, both config files written for you
steck new <branch> [base]Create a worktree + allocate its service ports; --ticket <id> records a ticket ID explicitly
steck up [--no-run]Bring up services, provision on first boot, run the app (--reprovision: run the provision hook again)
steck cockpit [--keep-up]Cockpit TUI: app pane + live service logs, per branch (via mprocs). Quitting stops the branch's services (data kept); --keep-up leaves them running
steck downStop the containers, keep the data
steck list / statusWhat's registered, running, and on which ports
steck exec -- <cmd>Run a command wired to this branch's env (e.g. sh -c 'psql "$DATABASE_URL"')
steck rm / pruneReclaim a branch's stack / bulk-reclaim merged branches — the teardown hook runs first (--purge: folder + branch too)
steck deploy [--dry-run]Ship this branch's agent to Railway
steck logs / destroyTail or tear down the deployed agent
steck mcpRun the MCP server so Claude can drive the fleet
steck doctor / configCheck the environment / validate steckling.yml
← Quickstart Next: steckling.yml →
REFERENCE

steckling.yml

The single config file Steckling reads from your repo root. Validate it any time with steck config.

version: 1
services:
  compose: ./compose.steckling.yml
  expose:
    postgres:
      container: 5432
      env: DATABASE_URL
      url: "postgres://app:app@localhost:{port}/app"
app:
  run: "npm run dev"
hooks:
  provision: "npm run migrate && npm run seed"
  postCreate: ""  # optional; runs after `steck new` creates the worktree
  teardown: ""    # optional; runs before `steck rm`/`steck prune` destroy the stack
ticket:           # optional — parse a ticket ID out of the branch name
  pattern: "eng-\\d+"
  url: "https://linear.app/acme/issue/{ticket}"
  env: STECKLING_TICKET
versionConfig schema version. Currently 1.
services.composePath to the compose file describing this repo's services. Run per-branch as an isolated compose project.
services.exposePer service: the container port to map, the env variable to write, and a url template — {port} is replaced with the branch's allocated host port.
app.runYour normal dev command. Steckling runs it with the branch's env injected — any language.
hooks.provisionRuns once on a branch's first steck up — migrations, seeds, anything that prepares fresh services. Run it again with steck up --reprovision.
hooks.postCreateOptional. Runs in the new worktree after steck new (identity env only — no services yet). A failure warns; the worktree is kept.
hooks.teardownOptional. Runs before steck rm / steck prune destroy a stack. A failure aborts rm (unless --force) and skips that branch in prune.
ticketOptional, opt-in ticket identity. Steckling parses the ticket ID out of the branch name (pattern, a regex matched case-insensitively), remembers it, shows it in steck list, and injects it into hooks and the app as $STECKLING_TICKET (name configurable via env). url is a link template — must contain {ticket} — rendered into STECKLING_TICKET_URL and steck status. It never calls the tracker's API.
← Commands Next: MCP →
GUIDES

Drive it from Claude (MCP)

steck mcp runs an MCP server that exposes the fleet to AI agents. Because branches are fully isolated, you can point a separate Claude session at each one and let them work in parallel.

Register with Claude Code

$ claude mcp add steckling -- steck mcp

Tools

steckling_newCreate a worktree + stack for a branch
steckling_upBring a branch's services up and provision
steckling_listInspect every registered worktree, status + ports
steckling://registryA live resource — the registry of all branches, as MCP context. Each worktree's ticket ID rides along when a ticket block is configured

Why this works

Each agent session gets a branch whose database, ports, and data belong to it alone. Agents can migrate, seed, and break things freely — nothing they do leaks into your branch or another agent's. When a branch is merged, steck prune reclaims the whole stack; add --purge to remove the worktree folder and branch too.

← steckling.yml