An ecosystem file defines your entire application topology in a single configuration. pboss supports JSON, JavaScript, and TypeScript ecosystem files.
Relative script paths resolve against the ecosystem file’s own directory — the same place cwd defaults to when left unset — so pboss start /srv/app/ecosystem.config.json works from any working directory.
pboss start ecosystem.config.json
pboss start ecosystem.config.ts
Auto-detection#
Run pboss start with no target and pboss looks for a config file in the current directory, loading the first one that exists — in this priority order:
ecosystem.config.json·ecosystem.config.js·ecosystem.config.tspboss.config.json·pboss.config.js·pboss.config.tsbm2.config.json·bm2.config.js·bm2.config.tspm2.config.json·pm2.config.js·pm2.config.ts
pboss start # loads the first config file above
Within a prefix, .json beats .js, which beats .ts. The pboss, bm2, and pm2 prefixes mean a project migrating from either naming scheme keeps booting from its existing file.
An explicit target always wins over detection — pboss start ./server.ts starts the script, and pboss start pm2.config.js loads that file even when an ecosystem.config.json sits next to it. When no config file is found, pboss start continues to its normal script and name resolution, and only errors when nothing at all can be resolved:
No PBoss configuration file or application was found.
Please provide a config file, executable script, or application to start.
JSON example#
{
"apps": [
{
"name": "api",
"script": "./src/api/server.ts",
"instances": 4,
"execMode": "cluster",
"port": 3000,
"env": {
"NODE_ENV": "production",
"DATABASE_URL": "postgres://localhost/mydb"
},
"maxMemoryRestart": "512M",
"healthCheckUrl": "http://localhost:3000/health",
"healthCheckInterval": 15000,
"logMaxSize": "50M",
"logRetain": 10,
"logCompress": true
},
{
"name": "worker",
"script": "./src/worker/index.ts",
"instances": 2,
"env": {
"NODE_ENV": "production",
"REDIS_URL": "redis://localhost:6379"
},
"cron": "0 */6 * * *",
"maxRestarts": 50
},
{
"name": "scheduler",
"script": "./src/scheduler/cron.ts",
"instances": 1,
"autorestart": true,
"watch": ["./src/scheduler"]
}
],
"deploy": {
"production": {
"user": "deploy",
"host": ["web1.example.com", "web2.example.com"],
"ref": "origin/main",
"repo": "git@github.com:your-org/your-app.git",
"path": "/var/www/app",
"preDeploy": "bun test",
"postDeploy": "bun install && pboss reload ecosystem.config.json --env production"
}
}
}
TypeScript example#
// ecosystem.config.ts
import type { EcosystemConfig } from "pboss/types";
const config: EcosystemConfig = {
apps: [
{
name: "api",
script: "./src/server.ts",
instances: "max",
execMode: "cluster",
port: 3000,
env: {
NODE_ENV: "production",
},
maxMemoryRestart: "1G",
healthCheckUrl: "http://localhost:3000/health",
},
],
};
export default config;
The TypeScript variant gets you type checking, comments, and shared constants — ideal once the file grows past a handful of apps.
Multiple environments#
Run the same topology under different env vars by declaring one entry per environment:
{
"apps": [
{
"name": "api-staging",
"script": "./server.ts",
"env": { "NODE_ENV": "staging", "PORT": "3000" }
},
{
"name": "api-production",
"script": "./server.ts",
"env": { "NODE_ENV": "production", "PORT": "8080" }
}
]
}
Then target a single group by name: pboss restart api-staging.
Namespace boundaries (#31)#
pboss start ecosystem.config.* uses the namespace field to divide the topology into lifecycle boundaries:
- Apps without a namespace are standalone and independent — a failure is reported, but the start sweep continues: no rollback, no blocking of the other apps.
- Apps sharing a namespace form one lifecycle group, started atomically at the namespace’s first-declaration position (members may be declared non-contiguously — they still form one group). If any member fails, the members that invocation started are rolled back; already-running members are never touched.
A namespace failure never affects other namespaces or standalone apps, and the overall command reports every failure after starting everything startable:
api online (standalone — unaffected)
shop
├── shop-api rolled back
├── shop-worker rolled back
└── shop-scheduler failed
admin
├── admin-api online (other namespace — unaffected)
└── admin-worker online
Members can also react to a sibling’s terminal exit with the onNsMemberExit policy — "ignore" (default) or "exit", which stops the other running members so the namespace runs complete or not at all.
Dependency ordering (#33)#
The dependsOn field makes the sweep graph-ordered: dependencies start before their dependents regardless of declaration order, with independent branches starting concurrently:
module.exports = {
apps: [
{ name: "worker", script: "./worker.ts", dependsOn: ["api"] }, // declared first…
{ name: "api", script: "./api.ts", dependsOn: ["postgres"] }, // …starts second…
{ name: "postgres", script: "./postgres-wrapper.ts" }, // …starts first
],
};
pboss start ecosystem.config.json brings the chain up postgres → api → worker. Dependencies that are not ecosystem apps resolve against live pboss processes first, then systemd units — an app may depend on a database the machine already runs without pboss owning it (see Dependencies for the full rules).
Cross-namespace dependencies order the groups: when web in namespace frontend depends on api in namespace backend, the backend group starts first. Cycles — inside a namespace or across them — are refused upfront, before a single process starts.
Dependency failures keep the same boundaries as everything else: a standalone app whose required dependency is unavailable fails (reported, sweep continues), and a blocked namespace member never starts — nothing of its group is left half-up. Dependencies started for a group join that group’s rollback scope; other namespaces are untouched.
What’s available per app#
Every field in the apps array mirrors a pboss start flag — the full mapping lives in the configuration reference. The deploy block is consumed by pboss deploy.
Recommended boot setup#
pboss start ecosystem.config.json
pboss save
pboss startup install
On reboot, systemd / launchd / Task Scheduler starts the pboss daemon, and the daemon automatically resurrects your processes. See Startup scripts.