local dev for a distributed stack

Run the slice of your stack you're working on. Locally, wired to the rest.

$curl -fsSL https://raw.githubusercontent.com/pinkynrg/crew/main/install.sh | sh
★ Star on GitHub

One static binary · macOS & Linux · MIT on GitHub

crew start
crew start: pick a connected slice of services in the dependency graph, run it locally, watch the labelled logs

the config file

You don't hand-write it.

crew config is a visual editor: pick a service's folder and crew fills in the mechanical parts it can read from your package.json, lockfiles and .envs — type, start command and env-file path. The URLs are yours to add: the service's local URL/port, and its deployed host for each environment. Out comes one readable config.json, committable, no secrets.

crew config
crew config: add a frontend and a backend by picking their folders (crew auto-fills type, env and start command), add each one's local URL and deployed host per env, and create a guard
add a frontend & a backend from their folders (scaffolding auto-filled), fill their URLs, then a guard

what it holds

  • path + tasks.start: where each repo lives and how to run it. Drop {envfile} in the command and crew injects the wired env file it materializes for the run.
  • match: the host(s) it's deployed under, per env. crew matches these against the URLs in your env files to auto-discover who depends on whom.
  • local: its local URL, so crew can rewrite a peer's env to point at your local copy instead of the deployed one.

Committable as-is: no secrets, no machine paths. Those live in a gitignored local.json beside it.

crew reads these hosts → derives this graph
web api auth
config.json
{
  "services": {
    "web": {
      "path": "web",
      "type": "frontend",
      "tasks": {
        "start": "dotenv -e  -- npm run dev"
      },
      "env": ".envs/{env}.env",
      "match": {
        "your-env": "web.example.com"
      },
      "local": "http://localhost:3000"
    },
    "api": {
      "path": "api",
      "tasks": {
        "start": "uvicorn app:main --reload --env-file "
      },
      "env": ".envs/{env}.env",
      "match": {
        "your-env": "api.example.com"
      },
      "local": "http://localhost:4000"
    },
    "auth": {
      "path": "auth",
      "tasks": {
        "start": "godotenv -f  go run ."
      },
      "env": ".envs/{env}.env",
      "match": {
        "your-env": "auth.example.com"
      },
      "local": "http://localhost:4500"
    }
  }
}

why not docker

Every service is a switch.

On is local, off is remote. Flip a service on and crew runs it locally and rewrites every peer that talks to it to point at your localhost. Flip it off and it stays on its deployed environment, callers untouched. The slice you run is just which switches are on — one service, or the whole stack.

Tap a service to flip it — the URLs and wiring update live.

Docker Compose has no equivalent to that switch: a service is in the compose file or it isn't, and pointing at the deployed one is a manual env edit — so you run the whole graph locally or maintain a second file full of stubs, and either drifts from production. crew reads the wiring from the .env files you already ship, so there's nothing to keep in sync.

crewComposeTiltmirrordovermind
Runs natively, no containers✓✕✕✓✓
Uses your real remote stack✓✕✕✓✕
No daemon or cluster✓✕✕✕✓
No parallel config to keep✓✕✕✕✕

Columns are the representative tool of each family. crew sits between process-runners (overmind, foreman, mprocs) and remote-wiring tools (mirrord, Telepresence): the native-slice-plus-real-remote of the latter, with none of the cluster, daemon, or containers — just URL swaps in the .env files you already have.

run one service or all of them native processes, no daemon no images, no rebuilds wiring from your env files

Assumes each service ships at least one environment file with its peers' URLs, plus a deployed host those point at. That's what crew borrows for the services you leave off. Nothing deployed to point at? A full-stack tool like Compose fits your case better.

questions

Is it safe to point at real environments?

Short version: crew reads your env files and rewrites a throwaway copy — it never edits your repo, and there are no secrets in the committable config.

Does crew modify my repos or my .env files?

No. It reads your env file and writes a wired copy to ~/.config/crew/tmp/<service>.env (URLs swapped to localhost for the peers you're running), passes that path to your start command via {envfile}, and deletes it on teardown. Your checkout and its committed env files are never touched.

Where do my secrets live?

In your own env files, same as today — crew only reads them. The committable config.json holds no secrets; machine-local bits (services dir, your editor, remembered selection) live in a gitignored local.json. The temporary wired copy under ~/.config/crew/tmp/ is the only materialized secret, and it's regenerated per run and removed on exit.

What if a remote environment is down?

Then the services you left off are unreachable, exactly as if you called that deployed environment directly. crew doesn't proxy or cache; turn those services on to run them locally, or wait for the environment to recover.

Do I have to run everything?

No — that's the whole point. Run one service or all of them; whatever you don't run stays on its deployed env, wired in automatically.

Which platforms are supported?

macOS and Linux (arm64 and amd64). It's POSIX-only — teardown relies on process groups (setsid / kill(-pgid)). One static binary, no runtime to install.

get started

From zero to a running set.

  1. Install
    $ curl -fsSL https://raw.githubusercontent.com/pinkynrg/crew/main/install.sh | sh

    One static binary, so it lands instantly. Or grab it from GitHub Releases.

  2. Register your services, or write the config file manually
    $ crew config

    A two-pane visual editor: pick a folder and crew prefills each service (path, start command, type), or write the config.json yourself, as shown above.

  3. Run the set
    $ crew start env=your-env

    Pick a connected slice in the dependency graph (click or arrow keys); crew runs those locally, wires them together, and leaves the rest on their resolved remote env.