Run a local process as if it were inside your cluster: prefix any command with plug and it resolves the cluster DNS and reaches the cluster services like any workload of the stack - no code change, no proxy configuration in your app. One tiny agent container on the cluster side, one static CLI on yours. Docker, Swarm, Kubernetes and OpenShift.

From your build tool

plug runs in front of the command that starts your service: plug -s my-api:8080:3000 npm run start:dev. Most build tools already have a place to keep such a command next to the project, so nobody types it twice and the whole team runs the same thing. Nothing to install beyond plug itself: each recipe below uses what the tool provides.

Every recipe takes the place of my-api, listening locally on the port given last. Replace the name and the ports with yours; the CLI reference has every option a command can carry.

Node.js (npm, pnpm, Yarn, Bun)

A script in package.json:

{
  "scripts": {
    "start:dev": "nest start --watch",
    "dev:cluster": "plug -s my-api:8080:3000 npm run start:dev"
  }
}
npm run dev:cluster

Maven

An execution of exec-maven-plugin, under <build><plugins>. It runs only when you ask for it, never during a build:

<plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>exec-maven-plugin</artifactId>
  <version>3.5.1</version>
  <executions>
    <execution>
      <id>cluster</id>
      <goals><goal>exec</goal></goals>
      <configuration>
        <executable>plug</executable>
        <arguments>
          <argument>-s</argument>
          <argument>my-api:8080:8080</argument>
          <argument>./mvnw</argument>
          <argument>spring-boot:run</argument>
        </arguments>
      </configuration>
    </execution>
  </executions>
</plugin>
mvn exec:exec@cluster

On Windows, write mvnw.cmd in place of ./mvnw. Maven starts twice, once for the goal and once for the application: a second or two more at startup.

Gradle

A task of type Exec in build.gradle.kts:

tasks.register<Exec>("bootRunCluster") {
    group = "application"
    description = "Runs the app in place of my-api in the cluster"
    commandLine("plug", "-s", "my-api:8080:8080", "./gradlew", "bootRun")
}
./gradlew bootRunCluster

On Windows, write gradlew.bat in place of ./gradlew.

PHP (Composer)

A script in composer.json. The first line matters: Composer stops any script after 300 seconds, a server included, unless it is told not to.

{
  "scripts": {
    "dev:cluster": [
      "Composer\\Config::disableProcessTimeout",
      "plug -s my-api:8080:8000 php -S 0.0.0.0:8000 -t public"
    ]
  }
}
composer run dev:cluster

Deno

A task in deno.json:

{
  "tasks": {
    "dev:cluster": "plug -s my-api:8080:8000 deno run -A main.ts"
  }
}
deno task dev:cluster

Python (PDM, Hatch)

A script in pyproject.toml. It runs inside the project's environment, so the server it starts is the one installed there.

[tool.pdm.scripts]
dev-cluster = "plug -s my-api:8080:8000 uvicorn app:app --port 8000"
pdm run dev-cluster

With Hatch, the same line under the environment's scripts:

[tool.hatch.envs.default.scripts]
dev-cluster = "plug -s my-api:8080:8000 uvicorn app:app --port 8000"
hatch run dev-cluster

uv and Poetry have no script runner of this kind: use the command itself (plug -s my-api:8080:8000 uv run uvicorn app:app), or a Makefile.

Everything else: Make, just, Taskfile

Go, Rust, .NET and most other languages start from a plain command (go run ., cargo run, dotnet run). A Makefile keeps it with the project:

dev-cluster:
	plug -s my-api:8080:8080 go run .

Or a justfile:

dev-cluster:
    plug -s my-api:8080:8080 cargo run

Running it from the IDE, and debugging

plug has to start your service: that is how the service receives the deployed one's configuration and how its traffic finds its way. A Run or Debug button that starts the program on its own leaves plug out. Run the recipe instead: every IDE can run an npm script, a Maven goal or a Gradle task from its own run configurations.

To debug, start the service with its debugger listening, then attach the IDE to it:

  • Node.js: add --inspect to the command that starts node, and attach to port 9229.
  • Spring Boot with Maven: add the argument -Dspring-boot.run.jvmArguments=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 after spring-boot:run, and attach to port 5005.
  • Spring Boot with Gradle: add "--debug-jvm" after "bootRun"; the application waits for the debugger on port 5005.
  • Python: start the server through debugpy (python -m debugpy --listen 5678 -m uvicorn app:app) and attach to port 5678.