DEV Community

Cover image for crew: run just the services you're working on - wired to the rest
Francesco Meli
Francesco Meli

Posted on Originally published at pinkynrg.github.io

crew: run just the services you're working on - wired to the rest

You have fifteen services. Today you're touching two.

If your stack is more than a couple of services, you know this moment. You want to run web and api locally to work on a feature - but api talks to auth, billing, a queue, a search cluster, and three other internal services. So what are your options?

  • Run the whole thing in Docker Compose. Now you're maintaining a docker-compose.yml that mirrors production (and drifts from it), booting thirteen containers you don't care about, feeding a daemon and disk, and waiting on rebuilds - all to change two services.
  • Point your two local services at staging by hand. Edit env files, swap URLs to localhost for the one peer you're running, remember to change them back. Every time.

Both are chores. I wanted a third option, so I built crew.

Every service is a switch

The whole idea fits in one line: on = local, off = remote.

  • Flip a service on → crew runs it natively and rewrites every peer that talks to it to point at your localhost.
  • Leave it off → it stays on its real deployed environment (qa / staging / prod), and its callers keep hitting the real host.

The "slice" you run locally is just which switches are on - one service, or the whole stack, or anything in between. And flipping a switch flips the wiring for you, read straight from the .env files you already ship.

crew start env=staging     web   [x] on  → http://localhost:3000
                           api   [x] on  → http://localhost:4000   (web now calls this)
                           auth  [ ] off → https://auth.staging.acme.dev
                           …     [ ] off → deployed staging
Enter fullscreen mode Exit fullscreen mode

Here's crew start in practice - pick a connected slice from the dependency graph, run it, watch the labelled logs:

crew start

How the wiring works

crew doesn't ask you to declare a topology. It derives the dependency graph from your env files: each project gets a match (the host(s) it's deployed under, per env) and a local (its localhost URL). When a URL in one project's env matches another project's match host, that's an edge.

At crew start, for each service you switched on, crew reads its env file, rewrites the URLs of any other on-services to their local value, writes the result to a throwaway file, and hands that path to your start command via {envfile}. Services you left off keep their real URLs. Nothing in your repo is touched - the wired copy lives in ~/.config/crew/tmp/ and is deleted on teardown.

Because the env each service resolves to is derived from the graph (not a static setting), the same shared config serves multiple teams correctly - the frontend team and the SDK team each get the right remote env for the services they didn't run.

You don't hand-write the config

crew config is a visual editor: pick a project's folder and crew auto-fills the mechanical parts it can read from your package.json, lockfiles and .envs - type, runner, start command, env-file path. You fill in the URLs (local port + deployed host per env). Out comes one readable, committable config.json with no secrets in it.

crew config

{
  "projects": {
    "web": {
      "path": "web",
      "type": "frontend",
      "tasks": { "start": "dotenv -e {envfile} -- npm run dev" },
      "env": ".envs/{env}.env",
      "match": { "staging": "web.staging.acme.dev", "prod": "web.acme.dev" },
      "local": "http://localhost:3000"
    },
    "api": {
      "path": "api",
      "tasks": { "start": "uvicorn app:main --reload --env-file {envfile}" },
      "env": ".envs/{env}.env",
      "match": { "staging": "api.staging.acme.dev", "prod": "api.acme.dev" },
      "local": "http://localhost:4000"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

The same slice, three ways

The picked set is remembered, so the other two commands open exactly what you started:

Command What it opens
crew start env=staging the dev servers (streamed, per-project-colored logs; Ctrl-C tears the whole group down)
crew workspace one multi-root VS Code window with every picked repo side by side
crew claude one Claude Code session over the set, history kept per group of repos

Why not just Docker Compose?

crew runs your slice natively and borrows the rest from their deployed envs - a different category from the usual tools:

crew Compose Tilt mirrord overmind
Runs your slice natively
Rest of stack = real remote
Wiring from .env files compose file k8s manifests cluster intercept -
Infra required none daemon k8s k8s none

crew sits between plain process-runners (overmind, foreman, mprocs) and remote-wiring tools (mirrord, Telepresence): the native-slice-plus-real-remote of the latter, without the cluster, daemon, or containers - just URL swaps in the env files you already have.

When Docker is the right call: you need full-stack isolation, byte-for-byte prod/CI parity, or your services can't run natively. crew doesn't replace that. It replaces the daily loop of flipping a couple of services on and letting everything else stay remote.

The honest caveats

  • You need shared remote environments to borrow from. If you don't have a staging/qa the "off" services can run on, a full-stack tool fits better.
  • crew doesn't proxy. If staging is down, your off-services are unreachable - same as calling staging directly. Turn them on to run locally.
  • POSIX only (macOS + Linux) - teardown relies on process groups so reparented dev-server children actually die.
  • No task graph, no ordering, no caching. crew fans out one task at a time; that's make/turbo/nx territory, on purpose.

Zero dependencies, by design

crew is one file of Node built-ins - no runtime dependencies, no build step, no bundler. The source is what runs, and its own parallel process runner handles the fan-out and teardown. So it installs instantly:

npx @pinkynrg/crew          # or: npm i -g @pinkynrg/crew
Enter fullscreen mode Exit fullscreen mode

Try it

The site has a live version of that on/off switchboard: flip services on and off and watch the URLs and wiring rewire as you go. Source, docs, and the full config reference are on GitHub.

I built crew for my own stack. If you've solved the run-a-slice-locally problem a different way, I'm curious which trade-offs you landed on.

Top comments (0)