What is Portler?
Portler is a process runner for multi-service projects — like a small docker compose, but with automatic port assignment. The same portler.yml runs your stack on a developer machine and in production.
It reads a portler.yml file, assigns free localhost ports to your services, resolves service references like backend.url in environment values, and starts each service with the right environment variables.
The problem
Local multi-service development usually means hardcoded ports:
- Two projects both want port
3000, so you can only run one at a time. - A colleague's
.envassumes port5432is free, but you already have a Postgres running. - You open a second git worktree of the same repository and everything collides with the first one.
- Every service needs to know every other service's address, so URLs are copy-pasted across
.envfiles and break the moment a port changes.
The idea
Declare your services once, without committing to specific ports:
services:
backend:
command: npm run dev
port: 4000
port_env: PORT
frontend:
command: npm run dev
port: 3000
port_env: PORT
env:
VITE_API_URL: backend.urlWhen you run portler up, Portler:
- Assigns a free port to every service (from a configurable range,
51000–59999by default). The declaredportis treated as a preference, not a requirement. - Resolves references —
backend.urlbecomeshttp://localhost:51235,${postgres.port}becomes the assigned Postgres port. - Injects environment variables — each service receives its own port via
port_env(e.g.PORT=51236) plus generatedPORTLER_*variables describing every service in the stack. - Starts services in dependency order, waiting for each dependency to become ready (TCP, HTTP, or command healthchecks) before starting its dependents.
Because nothing is hardcoded, any number of projects — or git worktrees of the same project — run side by side without stepping on each other.
One config, three run modes
The same portler.yml can run your stack three ways:
| Mode | Command | What runs where |
|---|---|---|
| Local | portler up | Services run as local processes; services with Docker config (e.g. image:) run in Docker |
| Docker | portler up docker | Everything with Docker config runs in containers on a shared project network |
| Kubernetes | portler up k8s | Everything runs in a local cluster (kind, minikube, Docker Desktop), port-forwarded back to localhost |
In every mode, the generated URLs and environment variables keep working the same way, so your app code never has to care.
Development and production
The same file also describes production. --prod applies the prod: overrides (production commands, setup steps and env), and the deployment's own environment wins over the file:
- On a platform with a build command and a start command, run
portler setup --prodas the build step andportler start <service> --prodas the start command. The platform'sPORTand secrets apply. - On a server with Docker,
portler up docker --prod -dstarts the stack, and--imagewith a digest reference (api=ghcr.io/org/api@sha256:…) runs the exact images CI built instead of building on the host. A tag is not enough: Docker reuses a cached copy of it without checking the registry.
See Setup & production and Docker for the details.
What Portler is not
- Not a cluster orchestrator. Portler runs a project on one machine: a developer machine, a CI runner or a server. It does not spread services across hosts, and it has no rolling or zero-downtime deploys:
restartstops a service and then starts its replacement. Kubernetes mode is meant for local clusters (kind, k3d, minikube, Docker Desktop and the like): by default it refuses a context or API endpoint it does not recognize as local, and runs against one only when you allow it by its exact name (PORTLER_ALLOW_K8S_CONTEXTfor the context,PORTLER_ALLOW_K8S_ENDPOINTfor the API server URL). It is not built to deploy to a production cluster. - Not a crash supervisor.
updoes not restart a service that crashes, and nothing comes back by itself after a reboot. In production, leave that to the platform or an init system:portler startruns one service in the foreground so the platform can supervise and restart it.
Requirements
- Node.js
>= 22.6 - Docker — only if you use Docker services
kubectland a local cluster (kind, minikube, or Docker Desktop Kubernetes) — only if you use Kubernetes mode
The CLI is written in TypeScript and runs directly on modern Node.js type stripping.
Next steps
- Getting started — install Portler and run your first stack.
- Services & environment — how services, env values, and references work.
- portler.yml reference — every configuration option.