bm2 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -0
- package/README.md +1591 -0
- package/package.json +68 -0
- package/src/cluster-manager.ts +117 -0
- package/src/constants.ts +44 -0
- package/src/cron-manager.ts +74 -0
- package/src/daemon.ts +233 -0
- package/src/dashboard-ui.ts +285 -0
- package/src/dashboard.ts +203 -0
- package/src/deploy.ts +188 -0
- package/src/env-manager.ts +77 -0
- package/src/graceful-reload.ts +71 -0
- package/src/health-checker.ts +112 -0
- package/src/index.ts +15 -0
- package/src/log-manager.ts +201 -0
- package/src/module-manager.ts +119 -0
- package/src/monitor.ts +185 -0
- package/src/process-container.ts +508 -0
- package/src/process-manager.ts +403 -0
- package/src/startup.ts +158 -0
- package/src/types.ts +230 -0
- package/src/utils.ts +171 -0
package/README.md
ADDED
|
@@ -0,0 +1,1591 @@
|
|
|
1
|
+
# ⚡ BM2
|
|
2
|
+
|
|
3
|
+
**A blazing-fast, full-featured process manager built entirely on Bun native APIs.**
|
|
4
|
+
The modern PM2 replacement — zero Node.js dependencies, pure Bun performance.
|
|
5
|
+
|
|
6
|
+

|
|
7
|
+

|
|
8
|
+

|
|
9
|
+

|
|
10
|
+
[](https://github.com/bun-bm2/bm2/actions/workflows/test.yml)
|
|
11
|
+
|
|
12
|
+
**Created by the MaxxPainn Team**
|
|
13
|
+
🌐 [https://maxxpainn.com](https://maxxpainn.com)
|
|
14
|
+
📧 Support: [hello@maxxpainn.com](mailto:hello@maxxpainn.com)
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Table of Contents
|
|
19
|
+
|
|
20
|
+
- [Why BM2?](#why-bm2)
|
|
21
|
+
- [Features](#features)
|
|
22
|
+
- [Requirements](#requirements)
|
|
23
|
+
- [Installation](#installation)
|
|
24
|
+
- [Quick Start](#quick-start)
|
|
25
|
+
- [CLI Reference](#cli-reference)
|
|
26
|
+
- [Process Management](#process-management)
|
|
27
|
+
- [Cluster Mode](#cluster-mode)
|
|
28
|
+
- [Log Management](#log-management)
|
|
29
|
+
- [Monitoring and Metrics](#monitoring-and-metrics)
|
|
30
|
+
- [Dashboard](#dashboard)
|
|
31
|
+
- [Ecosystem Files](#ecosystem-files)
|
|
32
|
+
- [Environment Management](#environment-management)
|
|
33
|
+
- [Deployment](#deployment)
|
|
34
|
+
- [Startup Scripts](#startup-scripts)
|
|
35
|
+
- [Modules](#modules)
|
|
36
|
+
- [Daemon Control](#daemon-control)
|
|
37
|
+
- [Configuration Reference](#configuration-reference)
|
|
38
|
+
- [Ecosystem File Format](#ecosystem-file-format)
|
|
39
|
+
- [Process Options](#process-options)
|
|
40
|
+
- [Cluster Options](#cluster-options)
|
|
41
|
+
- [Log Options](#log-options)
|
|
42
|
+
- [Health Check Options](#health-check-options)
|
|
43
|
+
- [Watch Options](#watch-options)
|
|
44
|
+
- [Deploy Configuration](#deploy-configuration)
|
|
45
|
+
- [Web Dashboard](#web-dashboard)
|
|
46
|
+
- [Dashboard Features](#dashboard-features)
|
|
47
|
+
- [REST API](#rest-api)
|
|
48
|
+
- [WebSocket API](#websocket-api)
|
|
49
|
+
- [Prometheus and Grafana Integration](#prometheus-and-grafana-integration)
|
|
50
|
+
- [Programmatic API](#programmatic-api)
|
|
51
|
+
- [Architecture](#architecture)
|
|
52
|
+
- [Comparison with PM2](#comparison-with-pm2)
|
|
53
|
+
- [Recipes and Examples](#recipes-and-examples)
|
|
54
|
+
- [Troubleshooting](#troubleshooting)
|
|
55
|
+
- [File Structure](#file-structure)
|
|
56
|
+
- [Contributing](#contributing)
|
|
57
|
+
- [License](#license)
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Why BM2?
|
|
62
|
+
|
|
63
|
+
PM2 is the de facto process manager for Node.js, but it carries years of legacy baggage, a heavy dependency tree, and is fundamentally built for the Node.js runtime. BM2 is a ground-up reimagining of production process management designed exclusively for the Bun runtime.
|
|
64
|
+
|
|
65
|
+
BM2 replaces PM2's Node.js internals with Bun-native APIs. It uses `Bun.spawn` for process management, `Bun.serve` for the dashboard and IPC, native `WebSocket` for daemon communication, `Bun.file` for high-performance I/O, and `Bun.gzipSync` for log compression. The result is a process manager that starts faster, uses less memory, and leverages Bun's superior performance across the board.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Features
|
|
70
|
+
|
|
71
|
+
**Core Process Management** — Start, stop, restart, reload, delete, and scale processes with automatic restart on crash, configurable restart strategies, memory-limit restarts, and tree killing.
|
|
72
|
+
|
|
73
|
+
**Cluster Mode** — Run multiple instances of your application with per-worker environment injection, automatic port assignment, and round-robin-ready configuration using `NODE_APP_INSTANCE` conventions.
|
|
74
|
+
|
|
75
|
+
**Zero-Downtime Reload** — Graceful reload cycles through instances sequentially, starting the new process before stopping the old one, ensuring your application never drops a request.
|
|
76
|
+
|
|
77
|
+
**Real-Time Web Dashboard** — A built-in dark-themed web dashboard with live WebSocket updates, CPU/memory charts, process control buttons, and a log viewer. No external dependencies.
|
|
78
|
+
|
|
79
|
+
**Prometheus Metrics** — A dedicated metrics endpoint exports process and system telemetry in Prometheus exposition format, ready for scraping by Prometheus and visualization in Grafana.
|
|
80
|
+
|
|
81
|
+
**Log Management** — Automatic log capture with buffered writes, size-based rotation, configurable retention, optional gzip compression, log flushing, and real-time tailing.
|
|
82
|
+
|
|
83
|
+
**Health Checks** — HTTP health check probes with configurable intervals, timeouts, and failure thresholds that automatically restart unhealthy processes.
|
|
84
|
+
|
|
85
|
+
**Cron Restarts** — Schedule periodic restarts using standard cron expressions for applications that benefit from regular recycling.
|
|
86
|
+
|
|
87
|
+
**File Watching** — Automatic restart on file changes with configurable watch paths and ignore patterns. Ideal for development workflows.
|
|
88
|
+
|
|
89
|
+
**Ecosystem Files** — Declare your entire application topology in a single JSON or TypeScript configuration file and start everything with one command.
|
|
90
|
+
|
|
91
|
+
**Process Persistence** — Save the current process list and resurrect it after a daemon restart or system reboot. Combined with startup script generation, your applications survive server reboots.
|
|
92
|
+
|
|
93
|
+
**Startup Script Generation** — Automatically generate and install systemd (Linux) or launchd (macOS) service configurations so the BM2 daemon starts at boot.
|
|
94
|
+
|
|
95
|
+
**Remote Deployment** — A built-in deploy system that handles SSH-based deployment with git pull, release directory management, symlink rotation, and pre/post-deploy hooks.
|
|
96
|
+
|
|
97
|
+
**Module/Plugin System** — Extend BM2 with custom modules that hook into the process manager lifecycle.
|
|
98
|
+
|
|
99
|
+
**Environment Management** — Store, retrieve, and inject environment variables per process with `.env` file loading support.
|
|
100
|
+
|
|
101
|
+
**Full IPC Architecture** — A daemonized architecture where the CLI communicates with a long-running daemon process over a Unix domain socket using WebSocket protocol.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Requirements
|
|
106
|
+
|
|
107
|
+
Bun version 1.0 or higher is required. BM2 is built exclusively for the Bun runtime.
|
|
108
|
+
|
|
109
|
+
Install Bun if you haven't already:
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
curl -fsSL https://bun.sh/install | bash
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Installation
|
|
118
|
+
|
|
119
|
+
### From Source
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
git clone https://github.com/aspect-dev/bm2.git
|
|
123
|
+
cd bm2
|
|
124
|
+
bun install
|
|
125
|
+
bun link
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Global Install
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
bun add -g bm2
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Verify Installation
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
bm2 --version
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Quick Start
|
|
143
|
+
|
|
144
|
+
### Start a process
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
bm2 start app.ts
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Start with a name and options
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
bm2 start app.ts --name my-api --instances 4 --port 3000
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### List all processes
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
bm2 list
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Output:
|
|
163
|
+
|
|
164
|
+
```
|
|
165
|
+
┌────┬──────────┬──────────┬──────┬───────┬──────────┬──────────┬──────────┐
|
|
166
|
+
│ ID │ Name │ Status │ PID │ CPU │ Memory │ Restarts │ Uptime │
|
|
167
|
+
├────┼──────────┼──────────┼──────┼───────┼──────────┼──────────┼──────────┤
|
|
168
|
+
│ 0 │ my-api-0 │ online │ 4521 │ 0.3% │ 42.1 MB │ 0 │ 5m 23s │
|
|
169
|
+
│ 1 │ my-api-1 │ online │ 4522 │ 0.2% │ 39.8 MB │ 0 │ 5m 23s │
|
|
170
|
+
│ 2 │ my-api-2 │ online │ 4523 │ 0.4% │ 41.3 MB │ 0 │ 5m 23s │
|
|
171
|
+
│ 3 │ my-api-3 │ online │ 4524 │ 0.1% │ 40.5 MB │ 0 │ 5m 23s │
|
|
172
|
+
└────┴──────────┴──────────┴──────┴───────┴──────────┴──────────┴──────────┘
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Open the dashboard
|
|
176
|
+
|
|
177
|
+
```
|
|
178
|
+
bm2 dashboard
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Output:
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
⚡ Dashboard running at http://localhost:9615
|
|
185
|
+
📊 Prometheus metrics at http://localhost:9616/metrics
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Save and auto-resurrect on reboot
|
|
189
|
+
|
|
190
|
+
```
|
|
191
|
+
bm2 save
|
|
192
|
+
bm2 startup
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## CLI Reference
|
|
198
|
+
|
|
199
|
+
### Process Management
|
|
200
|
+
|
|
201
|
+
#### bm2 start
|
|
202
|
+
|
|
203
|
+
Start a new process or processes.
|
|
204
|
+
|
|
205
|
+
```
|
|
206
|
+
bm2 start server.ts
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
```
|
|
210
|
+
bm2 start server.ts --name api -- --port 8080 --host 0.0.0.0
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
```
|
|
214
|
+
bm2 start server.ts --name api --env NODE_ENV=production --env API_KEY=xxx
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
```
|
|
218
|
+
bm2 start server.ts --name api --max-memory-restart 512M
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
bm2 start script.py --interpreter python3
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
```
|
|
226
|
+
bm2 start server.ts --name api --wait-ready --listen-timeout 10000
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
**Options:**
|
|
230
|
+
|
|
231
|
+
| Flag | Description | Default |
|
|
232
|
+
|---|---|---|
|
|
233
|
+
| `--name <name>` | Process name | Script filename |
|
|
234
|
+
| `--instances <n>` | Number of instances. Use `max` for all CPUs | `1` |
|
|
235
|
+
| `--exec-mode <mode>` | `fork` or `cluster` | `fork` |
|
|
236
|
+
| `--cwd <path>` | Working directory | Current directory |
|
|
237
|
+
| `--env <KEY=VAL>` | Environment variable (repeatable) | — |
|
|
238
|
+
| `--interpreter <bin>` | Custom interpreter binary | Auto-detected |
|
|
239
|
+
| `--interpreter-args <args>` | Arguments for the interpreter | — |
|
|
240
|
+
| `--node-args <args>` | Additional runtime arguments | — |
|
|
241
|
+
| `--max-memory-restart <size>` | Restart when memory exceeds limit | — |
|
|
242
|
+
| `--max-restarts <n>` | Maximum consecutive restarts | `16` |
|
|
243
|
+
| `--min-uptime <ms>` | Minimum uptime before a restart is considered stable | `1000` |
|
|
244
|
+
| `--restart-delay <ms>` | Delay between restarts | `0` |
|
|
245
|
+
| `--kill-timeout <ms>` | Grace period before SIGKILL | `5000` |
|
|
246
|
+
| `--no-autorestart` | Disable automatic restart | `false` |
|
|
247
|
+
| `--cron <expression>` | Cron expression for scheduled restarts | — |
|
|
248
|
+
| `--watch` | Enable file watching | `false` |
|
|
249
|
+
| `--ignore-watch <dirs>` | Directories to ignore | `node_modules,.git` |
|
|
250
|
+
| `--port <n>` | Base port (auto-incremented in cluster mode) | — |
|
|
251
|
+
| `--namespace <ns>` | Process namespace for grouping | — |
|
|
252
|
+
| `--wait-ready` | Wait for process ready signal | `false` |
|
|
253
|
+
| `--listen-timeout <ms>` | Timeout waiting for ready signal | `3000` |
|
|
254
|
+
| `--source-map-support` | Enable source map support | `false` |
|
|
255
|
+
| `--merge-logs` | Merge all instance logs into one file | `false` |
|
|
256
|
+
| `--log-date-format <fmt>` | Date format prefix for log lines | — |
|
|
257
|
+
| `--output <file>` | Custom stdout log path | `~/.bm2/logs/<name>-<id>-out.log` |
|
|
258
|
+
| `--error <file>` | Custom stderr log path | `~/.bm2/logs/<name>-<id>-error.log` |
|
|
259
|
+
| `--log-max-size <size>` | Max log file size before rotation | `10M` |
|
|
260
|
+
| `--log-retain <n>` | Number of rotated log files to keep | `5` |
|
|
261
|
+
| `--log-compress` | Gzip rotated log files | `false` |
|
|
262
|
+
| `--health-check-url <url>` | HTTP endpoint for health probes | — |
|
|
263
|
+
| `--health-check-interval <ms>` | Probe interval | `30000` |
|
|
264
|
+
| `--health-check-timeout <ms>` | Probe timeout | `5000` |
|
|
265
|
+
| `--health-check-max-fails <n>` | Failures before restart | `3` |
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
#### bm2 stop
|
|
270
|
+
|
|
271
|
+
Stop a process, all processes with a name, or all processes.
|
|
272
|
+
|
|
273
|
+
```
|
|
274
|
+
bm2 stop 0
|
|
275
|
+
bm2 stop my-api
|
|
276
|
+
bm2 stop my-namespace
|
|
277
|
+
bm2 stop all
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
#### bm2 restart
|
|
283
|
+
|
|
284
|
+
Stop and restart a process. The process is fully stopped and then re-spawned.
|
|
285
|
+
|
|
286
|
+
```
|
|
287
|
+
bm2 restart my-api
|
|
288
|
+
bm2 restart all
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
#### bm2 reload
|
|
294
|
+
|
|
295
|
+
Graceful zero-downtime reload. New instances start before old ones are killed, ensuring your application always has live workers handling requests.
|
|
296
|
+
|
|
297
|
+
```
|
|
298
|
+
bm2 reload my-api
|
|
299
|
+
bm2 reload all
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
The reload process works as follows for each instance. First, a new process is spawned. Then BM2 waits for the new process to become stable or emit a ready signal if `--wait-ready` is enabled. Next, the old process receives SIGTERM and is given the kill timeout to shut down gracefully. Finally, the cycle moves to the next instance.
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
#### bm2 delete
|
|
307
|
+
|
|
308
|
+
Stop and remove a process from BM2's management.
|
|
309
|
+
|
|
310
|
+
```
|
|
311
|
+
bm2 delete 0
|
|
312
|
+
bm2 delete my-api
|
|
313
|
+
bm2 delete all
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
#### bm2 scale
|
|
319
|
+
|
|
320
|
+
Dynamically scale a process group up or down.
|
|
321
|
+
|
|
322
|
+
```
|
|
323
|
+
bm2 scale my-api 8
|
|
324
|
+
bm2 scale my-api 2
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
When scaling up, new instances inherit the configuration of the existing instances. When scaling down, the highest-numbered instances are stopped and removed first.
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
#### bm2 describe
|
|
332
|
+
|
|
333
|
+
Show detailed information about a process.
|
|
334
|
+
|
|
335
|
+
```
|
|
336
|
+
bm2 describe my-api
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
Output:
|
|
340
|
+
|
|
341
|
+
```
|
|
342
|
+
┌─────────────────────┬──────────────────────────────────────────┐
|
|
343
|
+
│ Name │ my-api-0 │
|
|
344
|
+
│ ID │ 0 │
|
|
345
|
+
│ Status │ online │
|
|
346
|
+
│ PID │ 4521 │
|
|
347
|
+
│ Exec Mode │ cluster │
|
|
348
|
+
│ Instances │ 4 │
|
|
349
|
+
│ Uptime │ 2h 15m │
|
|
350
|
+
│ Restarts │ 0 │
|
|
351
|
+
│ Unstable Restarts │ 0 │
|
|
352
|
+
│ CPU │ 0.3% │
|
|
353
|
+
│ Memory │ 42.1 MB │
|
|
354
|
+
│ File Handles │ 24 │
|
|
355
|
+
│ Script │ /home/user/app/server.ts │
|
|
356
|
+
│ CWD │ /home/user/app │
|
|
357
|
+
│ Interpreter │ bun │
|
|
358
|
+
│ Watch │ disabled │
|
|
359
|
+
│ Max Memory Restart │ 512 MB │
|
|
360
|
+
│ Health Check │ http://localhost:3000/health (healthy) │
|
|
361
|
+
│ Cron Restart │ disabled │
|
|
362
|
+
│ Namespace │ production │
|
|
363
|
+
│ Created │ 2025-02-11T10:30:00.000Z │
|
|
364
|
+
│ Out Log │ /home/user/.bm2/logs/my-api-0-out.log │
|
|
365
|
+
│ Error Log │ /home/user/.bm2/logs/my-api-0-error.log │
|
|
366
|
+
└─────────────────────┴──────────────────────────────────────────┘
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
#### bm2 list
|
|
372
|
+
|
|
373
|
+
List all managed processes with their status, resource usage, and uptime.
|
|
374
|
+
|
|
375
|
+
```
|
|
376
|
+
bm2 list
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
#### bm2 signal
|
|
382
|
+
|
|
383
|
+
Send an OS signal to a process.
|
|
384
|
+
|
|
385
|
+
```
|
|
386
|
+
bm2 signal my-api SIGUSR2
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
#### bm2 reset
|
|
392
|
+
|
|
393
|
+
Reset the restart counter for a process.
|
|
394
|
+
|
|
395
|
+
```
|
|
396
|
+
bm2 reset my-api
|
|
397
|
+
bm2 reset all
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
---
|
|
401
|
+
|
|
402
|
+
### Cluster Mode
|
|
403
|
+
|
|
404
|
+
Cluster mode spawns multiple instances of your application, each running in its own process. This is ideal for CPU-bound workloads and for taking full advantage of multi-core servers.
|
|
405
|
+
|
|
406
|
+
```
|
|
407
|
+
bm2 start server.ts --name api --instances max
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
```
|
|
411
|
+
bm2 start server.ts --name api --instances 4
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
```
|
|
415
|
+
bm2 start server.ts --name api --instances 4 --port 3000
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
Each cluster worker receives the following environment variables:
|
|
419
|
+
|
|
420
|
+
| Variable | Description |
|
|
421
|
+
|---|---|
|
|
422
|
+
| `BM2_CLUSTER` | Set to `"true"` in cluster mode |
|
|
423
|
+
| `BM2_WORKER_ID` | Zero-indexed worker ID |
|
|
424
|
+
| `BM2_INSTANCES` | Total number of instances |
|
|
425
|
+
| `NODE_APP_INSTANCE` | Same as `BM2_WORKER_ID` (PM2 compatibility) |
|
|
426
|
+
| `PORT` | `basePort + workerIndex` (if `--port` is specified) |
|
|
427
|
+
|
|
428
|
+
Example application using cluster-aware port binding:
|
|
429
|
+
|
|
430
|
+
```
|
|
431
|
+
// server.ts
|
|
432
|
+
const workerId = parseInt(process.env.BM2_WORKER_ID || "0");
|
|
433
|
+
const port = parseInt(process.env.PORT || "3000");
|
|
434
|
+
|
|
435
|
+
Bun.serve({
|
|
436
|
+
port,
|
|
437
|
+
fetch(req) {
|
|
438
|
+
return new Response(`Hello from worker ${workerId} on port ${port}`);
|
|
439
|
+
},
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
console.log(`Worker ${workerId} listening on :${port}`);
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
---
|
|
446
|
+
|
|
447
|
+
### Log Management
|
|
448
|
+
|
|
449
|
+
#### bm2 logs
|
|
450
|
+
|
|
451
|
+
Display recent logs for a process.
|
|
452
|
+
|
|
453
|
+
```
|
|
454
|
+
bm2 logs
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
```
|
|
458
|
+
bm2 logs my-api --lines 100
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
```
|
|
462
|
+
bm2 logs my-api --err
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
```
|
|
466
|
+
bm2 logs my-api --follow
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
---
|
|
470
|
+
|
|
471
|
+
#### bm2 flush
|
|
472
|
+
|
|
473
|
+
Clear log files.
|
|
474
|
+
|
|
475
|
+
```
|
|
476
|
+
bm2 flush my-api
|
|
477
|
+
bm2 flush
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
---
|
|
481
|
+
|
|
482
|
+
#### Log Rotation
|
|
483
|
+
|
|
484
|
+
Log rotation runs automatically in the background. It checks log file sizes once per minute and rotates when the configured threshold is exceeded.
|
|
485
|
+
|
|
486
|
+
```
|
|
487
|
+
bm2 start server.ts --log-max-size 50M --log-retain 10 --log-compress
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
Rotation behavior: When a log file exceeds `--log-max-size`, it is renamed with a numeric suffix. Existing rotated files are shifted up by one number. Files beyond the `--log-retain` count are deleted. If `--log-compress` is enabled, rotated files are gzip-compressed using Bun's native `Bun.gzipSync`.
|
|
491
|
+
|
|
492
|
+
Default values:
|
|
493
|
+
|
|
494
|
+
| Setting | Default |
|
|
495
|
+
|---|---|
|
|
496
|
+
| `log-max-size` | `10 MB` |
|
|
497
|
+
| `log-retain` | `5` |
|
|
498
|
+
| `log-compress` | `false` |
|
|
499
|
+
|
|
500
|
+
---
|
|
501
|
+
|
|
502
|
+
### Monitoring and Metrics
|
|
503
|
+
|
|
504
|
+
#### bm2 monit
|
|
505
|
+
|
|
506
|
+
Open an interactive terminal monitor showing real-time CPU, memory, and event loop data for all processes.
|
|
507
|
+
|
|
508
|
+
```
|
|
509
|
+
bm2 monit
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
---
|
|
513
|
+
|
|
514
|
+
#### bm2 metrics
|
|
515
|
+
|
|
516
|
+
Dump a current metrics snapshot as JSON.
|
|
517
|
+
|
|
518
|
+
```
|
|
519
|
+
bm2 metrics
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
Output:
|
|
523
|
+
|
|
524
|
+
```
|
|
525
|
+
{
|
|
526
|
+
"timestamp": 1707650400000,
|
|
527
|
+
"processes": [
|
|
528
|
+
{
|
|
529
|
+
"id": 0,
|
|
530
|
+
"name": "my-api-0",
|
|
531
|
+
"pid": 4521,
|
|
532
|
+
"cpu": 0.3,
|
|
533
|
+
"memory": 44150784,
|
|
534
|
+
"handles": 24,
|
|
535
|
+
"status": "online",
|
|
536
|
+
"restarts": 0,
|
|
537
|
+
"uptime": 8100000
|
|
538
|
+
}
|
|
539
|
+
],
|
|
540
|
+
"system": {
|
|
541
|
+
"totalMemory": 17179869184,
|
|
542
|
+
"freeMemory": 8589934592,
|
|
543
|
+
"cpuCount": 8,
|
|
544
|
+
"loadAvg": [1.23, 1.45, 1.67],
|
|
545
|
+
"platform": "linux"
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
---
|
|
551
|
+
|
|
552
|
+
#### bm2 metrics --history
|
|
553
|
+
|
|
554
|
+
Retrieve historical metrics. BM2 retains up to 1 hour of per-second snapshots in memory.
|
|
555
|
+
|
|
556
|
+
```
|
|
557
|
+
bm2 metrics --history 600
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
---
|
|
561
|
+
|
|
562
|
+
#### bm2 prometheus
|
|
563
|
+
|
|
564
|
+
Output current metrics in Prometheus exposition format.
|
|
565
|
+
|
|
566
|
+
```
|
|
567
|
+
bm2 prometheus
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
Output:
|
|
571
|
+
|
|
572
|
+
```
|
|
573
|
+
# HELP bm2_process_cpu CPU usage percentage
|
|
574
|
+
# TYPE bm2_process_cpu gauge
|
|
575
|
+
bm2_process_cpu{name="my-api-0",id="0"} 0.3
|
|
576
|
+
# HELP bm2_process_memory_bytes Memory usage in bytes
|
|
577
|
+
# TYPE bm2_process_memory_bytes gauge
|
|
578
|
+
bm2_process_memory_bytes{name="my-api-0",id="0"} 44150784
|
|
579
|
+
# HELP bm2_process_restarts_total Total restart count
|
|
580
|
+
# TYPE bm2_process_restarts_total counter
|
|
581
|
+
bm2_process_restarts_total{name="my-api-0",id="0"} 0
|
|
582
|
+
# HELP bm2_process_uptime_seconds Process uptime in seconds
|
|
583
|
+
# TYPE bm2_process_uptime_seconds gauge
|
|
584
|
+
bm2_process_uptime_seconds{name="my-api-0",id="0"} 8100
|
|
585
|
+
# HELP bm2_process_status Process status (1=online)
|
|
586
|
+
# TYPE bm2_process_status gauge
|
|
587
|
+
bm2_process_status{name="my-api-0",id="0",status="online"} 1
|
|
588
|
+
# HELP bm2_system_memory_total_bytes Total system memory
|
|
589
|
+
# TYPE bm2_system_memory_total_bytes gauge
|
|
590
|
+
bm2_system_memory_total_bytes 17179869184
|
|
591
|
+
# HELP bm2_system_memory_free_bytes Free system memory
|
|
592
|
+
# TYPE bm2_system_memory_free_bytes gauge
|
|
593
|
+
bm2_system_memory_free_bytes 8589934592
|
|
594
|
+
# HELP bm2_system_load_average System load average
|
|
595
|
+
# TYPE bm2_system_load_average gauge
|
|
596
|
+
bm2_system_load_average{period="1m"} 1.23
|
|
597
|
+
bm2_system_load_average{period="5m"} 1.45
|
|
598
|
+
bm2_system_load_average{period="15m"} 1.67
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
---
|
|
602
|
+
|
|
603
|
+
### Dashboard
|
|
604
|
+
|
|
605
|
+
#### bm2 dashboard
|
|
606
|
+
|
|
607
|
+
Launch the built-in web dashboard.
|
|
608
|
+
|
|
609
|
+
```
|
|
610
|
+
bm2 dashboard
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
```
|
|
614
|
+
bm2 dashboard --port 8080 --metrics-port 8081
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
#### bm2 dashboard stop
|
|
618
|
+
|
|
619
|
+
Stop the web dashboard.
|
|
620
|
+
|
|
621
|
+
```
|
|
622
|
+
bm2 dashboard stop
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
See the Web Dashboard section below for a detailed description of dashboard capabilities.
|
|
626
|
+
|
|
627
|
+
---
|
|
628
|
+
|
|
629
|
+
### Ecosystem Files
|
|
630
|
+
|
|
631
|
+
An ecosystem file defines your entire application topology in a single configuration. BM2 supports JSON and TypeScript ecosystem files.
|
|
632
|
+
|
|
633
|
+
```
|
|
634
|
+
bm2 start ecosystem.config.json
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
```
|
|
638
|
+
bm2 start ecosystem.config.ts
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
Example `ecosystem.config.json`:
|
|
642
|
+
|
|
643
|
+
```
|
|
644
|
+
{
|
|
645
|
+
"apps": [
|
|
646
|
+
{
|
|
647
|
+
"name": "api",
|
|
648
|
+
"script": "./src/api/server.ts",
|
|
649
|
+
"instances": 4,
|
|
650
|
+
"execMode": "cluster",
|
|
651
|
+
"port": 3000,
|
|
652
|
+
"env": {
|
|
653
|
+
"NODE_ENV": "production",
|
|
654
|
+
"DATABASE_URL": "postgres://localhost/mydb"
|
|
655
|
+
},
|
|
656
|
+
"maxMemoryRestart": "512M",
|
|
657
|
+
"healthCheckUrl": "http://localhost:3000/health",
|
|
658
|
+
"healthCheckInterval": 15000,
|
|
659
|
+
"logMaxSize": "50M",
|
|
660
|
+
"logRetain": 10,
|
|
661
|
+
"logCompress": true
|
|
662
|
+
},
|
|
663
|
+
{
|
|
664
|
+
"name": "worker",
|
|
665
|
+
"script": "./src/worker/index.ts",
|
|
666
|
+
"instances": 2,
|
|
667
|
+
"env": {
|
|
668
|
+
"NODE_ENV": "production",
|
|
669
|
+
"REDIS_URL": "redis://localhost:6379"
|
|
670
|
+
},
|
|
671
|
+
"cron": "0 */6 * * *",
|
|
672
|
+
"maxRestarts": 50
|
|
673
|
+
},
|
|
674
|
+
{
|
|
675
|
+
"name": "scheduler",
|
|
676
|
+
"script": "./src/scheduler/cron.ts",
|
|
677
|
+
"instances": 1,
|
|
678
|
+
"autorestart": true,
|
|
679
|
+
"watch": ["./src/scheduler"]
|
|
680
|
+
}
|
|
681
|
+
],
|
|
682
|
+
"deploy": {
|
|
683
|
+
"production": {
|
|
684
|
+
"user": "deploy",
|
|
685
|
+
"host": ["web1.example.com", "web2.example.com"],
|
|
686
|
+
"ref": "origin/main",
|
|
687
|
+
"repo": "git@github.com:your-org/your-app.git",
|
|
688
|
+
"path": "/var/www/app",
|
|
689
|
+
"preDeploy": "bun test",
|
|
690
|
+
"postDeploy": "bun install && bm2 reload ecosystem.config.json --env production"
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
Example `ecosystem.config.ts`:
|
|
697
|
+
|
|
698
|
+
```
|
|
699
|
+
// ecosystem.config.ts
|
|
700
|
+
import type { EcosystemConfig } from "bm2/types";
|
|
701
|
+
|
|
702
|
+
const config: EcosystemConfig = {
|
|
703
|
+
apps: [
|
|
704
|
+
{
|
|
705
|
+
name: "api",
|
|
706
|
+
script: "./src/server.ts",
|
|
707
|
+
instances: "max",
|
|
708
|
+
execMode: "cluster",
|
|
709
|
+
port: 3000,
|
|
710
|
+
env: {
|
|
711
|
+
NODE_ENV: "production",
|
|
712
|
+
},
|
|
713
|
+
maxMemoryRestart: "1G",
|
|
714
|
+
healthCheckUrl: "http://localhost:3000/health",
|
|
715
|
+
},
|
|
716
|
+
],
|
|
717
|
+
};
|
|
718
|
+
|
|
719
|
+
export default config;
|
|
720
|
+
```
|
|
721
|
+
|
|
722
|
+
---
|
|
723
|
+
|
|
724
|
+
### Environment Management
|
|
725
|
+
|
|
726
|
+
#### bm2 env set
|
|
727
|
+
|
|
728
|
+
Set an environment variable for a process.
|
|
729
|
+
|
|
730
|
+
```
|
|
731
|
+
bm2 env set my-api DATABASE_URL postgres://localhost/mydb
|
|
732
|
+
```
|
|
733
|
+
|
|
734
|
+
#### bm2 env get
|
|
735
|
+
|
|
736
|
+
List all stored environment variables for a process.
|
|
737
|
+
|
|
738
|
+
```
|
|
739
|
+
bm2 env get my-api
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
#### bm2 env delete
|
|
743
|
+
|
|
744
|
+
Remove an environment variable or all environment variables.
|
|
745
|
+
|
|
746
|
+
```
|
|
747
|
+
bm2 env delete my-api DATABASE_URL
|
|
748
|
+
bm2 env delete my-api
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
#### .env File Support
|
|
752
|
+
|
|
753
|
+
BM2 can load environment variables from `.env` files:
|
|
754
|
+
|
|
755
|
+
```
|
|
756
|
+
bm2 start server.ts --env-file .env.production
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
---
|
|
760
|
+
|
|
761
|
+
### Deployment
|
|
762
|
+
|
|
763
|
+
BM2 includes a built-in deployment system for SSH-based deployments with release management.
|
|
764
|
+
|
|
765
|
+
#### bm2 deploy setup
|
|
766
|
+
|
|
767
|
+
Initial setup of the remote server. Creates the directory structure and clones the repository.
|
|
768
|
+
|
|
769
|
+
```
|
|
770
|
+
bm2 deploy ecosystem.config.json production setup
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
This creates the following remote directory structure:
|
|
774
|
+
|
|
775
|
+
```
|
|
776
|
+
/var/www/app/
|
|
777
|
+
├── source/
|
|
778
|
+
├── releases/
|
|
779
|
+
│ ├── 2025-02-11T10-30-00-000Z/
|
|
780
|
+
│ └── 2025-02-10T15-45-00-000Z/
|
|
781
|
+
├── current -> releases/2025-02-11T10-30-00-000Z/
|
|
782
|
+
└── shared/
|
|
783
|
+
```
|
|
784
|
+
|
|
785
|
+
#### bm2 deploy
|
|
786
|
+
|
|
787
|
+
Deploy a new release.
|
|
788
|
+
|
|
789
|
+
```
|
|
790
|
+
bm2 deploy ecosystem.config.json production
|
|
791
|
+
```
|
|
792
|
+
|
|
793
|
+
The deploy process works as follows. It runs the `preDeploy` hook locally such as running tests. It connects via SSH to each configured host. It pulls the latest code from the configured ref. It creates a new timestamped release directory. It updates the current symlink to the new release. It runs the `postDeploy` hook remotely such as installing dependencies and reloading processes. It cleans up old releases, keeping only the 5 most recent.
|
|
794
|
+
|
|
795
|
+
Multi-host deployment is supported. Specify an array of hosts to deploy to all of them sequentially:
|
|
796
|
+
|
|
797
|
+
```
|
|
798
|
+
{
|
|
799
|
+
"host": ["web1.example.com", "web2.example.com", "web3.example.com"]
|
|
800
|
+
}
|
|
801
|
+
```
|
|
802
|
+
|
|
803
|
+
---
|
|
804
|
+
|
|
805
|
+
### Startup Scripts
|
|
806
|
+
|
|
807
|
+
#### bm2 startup
|
|
808
|
+
|
|
809
|
+
Generate and display a startup script for your operating system. On Linux, this generates a systemd unit file. On macOS, this generates a launchd plist.
|
|
810
|
+
|
|
811
|
+
```
|
|
812
|
+
bm2 startup
|
|
813
|
+
```
|
|
814
|
+
|
|
815
|
+
#### bm2 startup install
|
|
816
|
+
|
|
817
|
+
Automatically install the startup script so the BM2 daemon starts at boot.
|
|
818
|
+
|
|
819
|
+
```
|
|
820
|
+
bm2 startup install
|
|
821
|
+
```
|
|
822
|
+
|
|
823
|
+
#### bm2 startup uninstall
|
|
824
|
+
|
|
825
|
+
Remove the startup script.
|
|
826
|
+
|
|
827
|
+
```
|
|
828
|
+
bm2 startup uninstall
|
|
829
|
+
```
|
|
830
|
+
|
|
831
|
+
#### bm2 save
|
|
832
|
+
|
|
833
|
+
Save the current process list so it can be restored on daemon startup.
|
|
834
|
+
|
|
835
|
+
```
|
|
836
|
+
bm2 save
|
|
837
|
+
```
|
|
838
|
+
|
|
839
|
+
#### bm2 resurrect
|
|
840
|
+
|
|
841
|
+
Restore previously saved processes.
|
|
842
|
+
|
|
843
|
+
```
|
|
844
|
+
bm2 resurrect
|
|
845
|
+
```
|
|
846
|
+
|
|
847
|
+
Recommended boot setup:
|
|
848
|
+
|
|
849
|
+
```
|
|
850
|
+
bm2 start ecosystem.config.json
|
|
851
|
+
bm2 save
|
|
852
|
+
bm2 startup install
|
|
853
|
+
```
|
|
854
|
+
|
|
855
|
+
On reboot, systemd or launchd starts the BM2 daemon, and the daemon automatically runs resurrect to restore your processes.
|
|
856
|
+
|
|
857
|
+
---
|
|
858
|
+
|
|
859
|
+
### Modules
|
|
860
|
+
|
|
861
|
+
BM2 supports a plugin system for extending functionality.
|
|
862
|
+
|
|
863
|
+
#### bm2 module install
|
|
864
|
+
|
|
865
|
+
Install a module from a git URL, local path, or npm package name.
|
|
866
|
+
|
|
867
|
+
```
|
|
868
|
+
bm2 module install https://github.com/user/bm2-logrotate.git
|
|
869
|
+
bm2 module install ./my-bm2-module
|
|
870
|
+
bm2 module install bm2-prometheus-pushgateway
|
|
871
|
+
```
|
|
872
|
+
|
|
873
|
+
#### bm2 module list
|
|
874
|
+
|
|
875
|
+
List installed modules.
|
|
876
|
+
|
|
877
|
+
```
|
|
878
|
+
bm2 module list
|
|
879
|
+
```
|
|
880
|
+
|
|
881
|
+
#### bm2 module uninstall
|
|
882
|
+
|
|
883
|
+
Remove an installed module.
|
|
884
|
+
|
|
885
|
+
```
|
|
886
|
+
bm2 module uninstall bm2-prometheus-pushgateway
|
|
887
|
+
```
|
|
888
|
+
|
|
889
|
+
#### Writing a BM2 Module
|
|
890
|
+
|
|
891
|
+
A BM2 module is a package with a default export implementing the BM2Module interface:
|
|
892
|
+
|
|
893
|
+
```
|
|
894
|
+
// my-module/index.ts
|
|
895
|
+
import type { ProcessManager } from "bm2/process-manager";
|
|
896
|
+
|
|
897
|
+
export default {
|
|
898
|
+
name: "my-module",
|
|
899
|
+
version: "1.0.0",
|
|
900
|
+
|
|
901
|
+
init(pm: ProcessManager) {
|
|
902
|
+
console.log("[my-module] Initialized with", pm.list().length, "processes");
|
|
903
|
+
},
|
|
904
|
+
|
|
905
|
+
destroy() {
|
|
906
|
+
console.log("[my-module] Destroyed");
|
|
907
|
+
},
|
|
908
|
+
};
|
|
909
|
+
```
|
|
910
|
+
|
|
911
|
+
---
|
|
912
|
+
|
|
913
|
+
### Daemon Control
|
|
914
|
+
|
|
915
|
+
#### bm2 ping
|
|
916
|
+
|
|
917
|
+
Check if the daemon is running.
|
|
918
|
+
|
|
919
|
+
```
|
|
920
|
+
bm2 ping
|
|
921
|
+
```
|
|
922
|
+
|
|
923
|
+
#### bm2 kill
|
|
924
|
+
|
|
925
|
+
Stop all processes and kill the daemon.
|
|
926
|
+
|
|
927
|
+
```
|
|
928
|
+
bm2 kill
|
|
929
|
+
```
|
|
930
|
+
|
|
931
|
+
---
|
|
932
|
+
|
|
933
|
+
## Configuration Reference
|
|
934
|
+
|
|
935
|
+
### Ecosystem File Format
|
|
936
|
+
|
|
937
|
+
The ecosystem file is a JSON or TypeScript file with the following top-level structure:
|
|
938
|
+
|
|
939
|
+
```
|
|
940
|
+
interface EcosystemConfig {
|
|
941
|
+
apps: StartOptions[];
|
|
942
|
+
deploy?: Record<string, DeployConfig>;
|
|
943
|
+
}
|
|
944
|
+
```
|
|
945
|
+
|
|
946
|
+
---
|
|
947
|
+
|
|
948
|
+
### Process Options
|
|
949
|
+
|
|
950
|
+
The complete set of options available for each entry in the apps array:
|
|
951
|
+
|
|
952
|
+
| Option | Type | Default | Description |
|
|
953
|
+
|---|---|---|---|
|
|
954
|
+
| `name` | `string` | Filename | Process name |
|
|
955
|
+
| `script` | `string` | required | Path to the script to execute |
|
|
956
|
+
| `args` | `string[]` | `[]` | Arguments passed to the script |
|
|
957
|
+
| `cwd` | `string` | `process.cwd()` | Working directory |
|
|
958
|
+
| `env` | `Record<string, string>` | `{}` | Environment variables |
|
|
959
|
+
| `instances` | `number` or `"max"` | `1` | Number of instances |
|
|
960
|
+
| `execMode` | `"fork"` or `"cluster"` | `"fork"` | Execution mode |
|
|
961
|
+
| `autorestart` | `boolean` | `true` | Restart on crash |
|
|
962
|
+
| `maxRestarts` | `number` | `16` | Maximum restart attempts before giving up |
|
|
963
|
+
| `minUptime` | `number` | `1000` | Minimum ms a process must be up to be considered stable |
|
|
964
|
+
| `maxMemoryRestart` | `string` or `number` | — | Memory threshold for restart |
|
|
965
|
+
| `restartDelay` | `number` | `0` | Delay in ms between restart attempts |
|
|
966
|
+
| `killTimeout` | `number` | `5000` | Grace period in ms before SIGKILL |
|
|
967
|
+
| `interpreter` | `string` | Auto | Custom interpreter |
|
|
968
|
+
| `interpreterArgs` | `string[]` | — | Arguments for the interpreter |
|
|
969
|
+
| `nodeArgs` | `string[]` | — | Additional runtime arguments |
|
|
970
|
+
| `namespace` | `string` | — | Namespace for grouping processes |
|
|
971
|
+
| `sourceMapSupport` | `boolean` | `false` | Enable source map support |
|
|
972
|
+
| `waitReady` | `boolean` | `false` | Wait for process to emit ready signal |
|
|
973
|
+
| `listenTimeout` | `number` | `3000` | Timeout when waiting for ready signal |
|
|
974
|
+
|
|
975
|
+
---
|
|
976
|
+
|
|
977
|
+
### Cluster Options
|
|
978
|
+
|
|
979
|
+
| Option | Type | Default | Description |
|
|
980
|
+
|---|---|---|---|
|
|
981
|
+
| `instances` | `number` or `"max"` | `1` | Worker count |
|
|
982
|
+
| `execMode` | `"cluster"` | `"fork"` | Set to cluster for multi-instance mode |
|
|
983
|
+
| `port` | `number` | — | Base port. Worker i gets port + i |
|
|
984
|
+
|
|
985
|
+
---
|
|
986
|
+
|
|
987
|
+
### Log Options
|
|
988
|
+
|
|
989
|
+
| Option | Type | Default | Description |
|
|
990
|
+
|---|---|---|---|
|
|
991
|
+
| `outFile` | `string` | `~/.bm2/logs/<name>-<id>-out.log` | Custom stdout log path |
|
|
992
|
+
| `errorFile` | `string` | `~/.bm2/logs/<name>-<id>-error.log` | Custom stderr log path |
|
|
993
|
+
| `mergeLogs` | `boolean` | `false` | Merge all instance logs into one file |
|
|
994
|
+
| `logDateFormat` | `string` | — | Date format for log line prefixes |
|
|
995
|
+
| `logMaxSize` | `string` or `number` | `"10M"` | Max log file size before rotation |
|
|
996
|
+
| `logRetain` | `number` | `5` | Number of rotated files to keep |
|
|
997
|
+
| `logCompress` | `boolean` | `false` | Gzip-compress rotated log files |
|
|
998
|
+
|
|
999
|
+
---
|
|
1000
|
+
|
|
1001
|
+
### Health Check Options
|
|
1002
|
+
|
|
1003
|
+
| Option | Type | Default | Description |
|
|
1004
|
+
|---|---|---|---|
|
|
1005
|
+
| `healthCheckUrl` | `string` | — | URL to probe |
|
|
1006
|
+
| `healthCheckInterval` | `number` | `30000` | Probe interval in ms |
|
|
1007
|
+
| `healthCheckTimeout` | `number` | `5000` | Probe timeout in ms |
|
|
1008
|
+
| `healthCheckMaxFails` | `number` | `3` | Consecutive failures before restart |
|
|
1009
|
+
|
|
1010
|
+
---
|
|
1011
|
+
|
|
1012
|
+
### Watch Options
|
|
1013
|
+
|
|
1014
|
+
| Option | Type | Default | Description |
|
|
1015
|
+
|---|---|---|---|
|
|
1016
|
+
| `watch` | `boolean` or `string[]` | `false` | Enable file watching |
|
|
1017
|
+
| `ignoreWatch` | `string[]` | `["node_modules", ".git", ".bm2"]` | Patterns to ignore |
|
|
1018
|
+
|
|
1019
|
+
---
|
|
1020
|
+
|
|
1021
|
+
### Deploy Configuration
|
|
1022
|
+
|
|
1023
|
+
| Option | Type | Description |
|
|
1024
|
+
|---|---|---|
|
|
1025
|
+
| `user` | `string` | SSH user |
|
|
1026
|
+
| `host` | `string` or `string[]` | Remote host(s) |
|
|
1027
|
+
| `ref` | `string` | Git ref to deploy |
|
|
1028
|
+
| `repo` | `string` | Git repository URL |
|
|
1029
|
+
| `path` | `string` | Remote deployment path |
|
|
1030
|
+
| `preDeploy` | `string` | Command to run locally before deploy |
|
|
1031
|
+
| `postDeploy` | `string` | Command to run remotely after deploy |
|
|
1032
|
+
| `preSetup` | `string` | Command to run remotely during setup |
|
|
1033
|
+
| `postSetup` | `string` | Command to run remotely after setup |
|
|
1034
|
+
| `ssh_options` | `string` | Additional SSH options |
|
|
1035
|
+
| `env` | `Record<string, string>` | Environment variables for remote commands |
|
|
1036
|
+
|
|
1037
|
+
---
|
|
1038
|
+
|
|
1039
|
+
## Web Dashboard
|
|
1040
|
+
|
|
1041
|
+
The BM2 dashboard is a self-contained web application served directly by the daemon. It requires no external dependencies. The HTML, CSS, JavaScript, and WebSocket server are all built in.
|
|
1042
|
+
|
|
1043
|
+
### Dashboard Features
|
|
1044
|
+
|
|
1045
|
+
**Process Overview** — Four summary cards showing counts of online and errored processes, total CPU usage, and aggregate memory consumption.
|
|
1046
|
+
|
|
1047
|
+
**System Information** — Platform, CPU count, load average, and memory usage with a visual progress bar.
|
|
1048
|
+
|
|
1049
|
+
**CPU and Memory Chart** — A real-time canvas-rendered chart showing aggregate CPU percentage and memory usage over the last 60 data points, updating every 2 seconds.
|
|
1050
|
+
|
|
1051
|
+
**Process Table** — A detailed table showing every managed process with columns for ID, name, status with color-coded badges, PID, CPU, memory, restart count, uptime, and action buttons for restart, stop, and log viewing.
|
|
1052
|
+
|
|
1053
|
+
**Log Viewer** — A tabbed log panel that streams stdout and stderr from any selected process, with syntax highlighting for timestamps and error output. Logs auto-scroll to the latest entry.
|
|
1054
|
+
|
|
1055
|
+
**Live Updates** — All data is streamed over WebSocket with a visual pulse indicator confirming the live connection. If the connection drops, the dashboard automatically reconnects within 2 seconds.
|
|
1056
|
+
|
|
1057
|
+
---
|
|
1058
|
+
|
|
1059
|
+
### REST API
|
|
1060
|
+
|
|
1061
|
+
The dashboard exposes a REST API on the same port:
|
|
1062
|
+
|
|
1063
|
+
| Method | Endpoint | Description |
|
|
1064
|
+
|---|---|---|
|
|
1065
|
+
| `GET` | `/` | Dashboard HTML |
|
|
1066
|
+
| `GET` | `/api/processes` | List all processes as JSON |
|
|
1067
|
+
| `GET` | `/api/metrics` | Current metrics snapshot |
|
|
1068
|
+
| `GET` | `/api/metrics/history?seconds=300` | Historical metrics |
|
|
1069
|
+
| `GET` | `/api/prometheus` or `/metrics` | Prometheus text format |
|
|
1070
|
+
| `POST` | `/api/restart` | Restart process |
|
|
1071
|
+
| `POST` | `/api/stop` | Stop process |
|
|
1072
|
+
| `POST` | `/api/reload` | Graceful reload |
|
|
1073
|
+
| `POST` | `/api/delete` | Delete process |
|
|
1074
|
+
| `POST` | `/api/scale` | Scale process |
|
|
1075
|
+
| `POST` | `/api/flush` | Flush logs |
|
|
1076
|
+
|
|
1077
|
+
POST endpoints accept JSON body with `target` field for process identification and additional fields where applicable such as `count` for scaling.
|
|
1078
|
+
|
|
1079
|
+
Example using curl:
|
|
1080
|
+
|
|
1081
|
+
```
|
|
1082
|
+
curl http://localhost:9615/api/processes
|
|
1083
|
+
```
|
|
1084
|
+
|
|
1085
|
+
```
|
|
1086
|
+
curl -X POST http://localhost:9615/api/restart \
|
|
1087
|
+
-H "Content-Type: application/json" \
|
|
1088
|
+
-d '{"target": "my-api"}'
|
|
1089
|
+
```
|
|
1090
|
+
|
|
1091
|
+
```
|
|
1092
|
+
curl -X POST http://localhost:9615/api/scale \
|
|
1093
|
+
-H "Content-Type: application/json" \
|
|
1094
|
+
-d '{"target": "my-api", "count": 8}'
|
|
1095
|
+
```
|
|
1096
|
+
|
|
1097
|
+
```
|
|
1098
|
+
curl http://localhost:9615/metrics
|
|
1099
|
+
```
|
|
1100
|
+
|
|
1101
|
+
---
|
|
1102
|
+
|
|
1103
|
+
### WebSocket API
|
|
1104
|
+
|
|
1105
|
+
Connect to `ws://localhost:9615/ws` for real-time bidirectional communication.
|
|
1106
|
+
|
|
1107
|
+
Client to server messages:
|
|
1108
|
+
|
|
1109
|
+
```
|
|
1110
|
+
{ "type": "getState", "data": {} }
|
|
1111
|
+
```
|
|
1112
|
+
|
|
1113
|
+
```
|
|
1114
|
+
{ "type": "getLogs", "data": { "target": 0, "lines": 50 } }
|
|
1115
|
+
```
|
|
1116
|
+
|
|
1117
|
+
```
|
|
1118
|
+
{ "type": "restart", "data": { "target": "my-api" } }
|
|
1119
|
+
```
|
|
1120
|
+
|
|
1121
|
+
```
|
|
1122
|
+
{ "type": "stop", "data": { "target": 0 } }
|
|
1123
|
+
```
|
|
1124
|
+
|
|
1125
|
+
```
|
|
1126
|
+
{ "type": "reload", "data": { "target": "all" } }
|
|
1127
|
+
```
|
|
1128
|
+
|
|
1129
|
+
```
|
|
1130
|
+
{ "type": "scale", "data": { "target": "my-api", "count": 4 } }
|
|
1131
|
+
```
|
|
1132
|
+
|
|
1133
|
+
Server to client messages:
|
|
1134
|
+
|
|
1135
|
+
```
|
|
1136
|
+
{
|
|
1137
|
+
"type": "state",
|
|
1138
|
+
"data": {
|
|
1139
|
+
"processes": [],
|
|
1140
|
+
"metrics": {
|
|
1141
|
+
"timestamp": 1707650400000,
|
|
1142
|
+
"processes": [],
|
|
1143
|
+
"system": {}
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
```
|
|
1148
|
+
|
|
1149
|
+
```
|
|
1150
|
+
{
|
|
1151
|
+
"type": "logs",
|
|
1152
|
+
"data": [
|
|
1153
|
+
{ "name": "my-api-0", "id": 0, "out": "...", "err": "..." }
|
|
1154
|
+
]
|
|
1155
|
+
}
|
|
1156
|
+
```
|
|
1157
|
+
|
|
1158
|
+
---
|
|
1159
|
+
|
|
1160
|
+
## Prometheus and Grafana Integration
|
|
1161
|
+
|
|
1162
|
+
BM2 runs a dedicated Prometheus metrics server on default port 9616 separately from the dashboard, following best practices for metrics collection.
|
|
1163
|
+
|
|
1164
|
+
### Prometheus Configuration
|
|
1165
|
+
|
|
1166
|
+
Add the following to your `prometheus.yml`:
|
|
1167
|
+
|
|
1168
|
+
```
|
|
1169
|
+
scrape_configs:
|
|
1170
|
+
- job_name: "bm2"
|
|
1171
|
+
scrape_interval: 5s
|
|
1172
|
+
static_configs:
|
|
1173
|
+
- targets: ["localhost:9616"]
|
|
1174
|
+
```
|
|
1175
|
+
|
|
1176
|
+
### Available Metrics
|
|
1177
|
+
|
|
1178
|
+
| Metric | Type | Labels | Description |
|
|
1179
|
+
|---|---|---|---|
|
|
1180
|
+
| `bm2_process_cpu` | gauge | `name`, `id` | CPU usage percentage |
|
|
1181
|
+
| `bm2_process_memory_bytes` | gauge | `name`, `id` | Memory usage in bytes |
|
|
1182
|
+
| `bm2_process_restarts_total` | counter | `name`, `id` | Total restart count |
|
|
1183
|
+
| `bm2_process_uptime_seconds` | gauge | `name`, `id` | Uptime in seconds |
|
|
1184
|
+
| `bm2_process_status` | gauge | `name`, `id`, `status` | 1 if online, 0 otherwise |
|
|
1185
|
+
| `bm2_system_memory_total_bytes` | gauge | — | Total system memory |
|
|
1186
|
+
| `bm2_system_memory_free_bytes` | gauge | — | Free system memory |
|
|
1187
|
+
| `bm2_system_load_average` | gauge | `period` | Load average (1m, 5m, 15m) |
|
|
1188
|
+
|
|
1189
|
+
### Grafana Dashboard
|
|
1190
|
+
|
|
1191
|
+
Import a dashboard with the following panels for comprehensive monitoring: Process Status Overview as a stat panel colored by status, CPU Usage per Process as a time series with `bm2_process_cpu` grouped by name, Memory Usage per Process as a time series with `bm2_process_memory_bytes` grouped by name, Restart Rate as a graph of `rate(bm2_process_restarts_total[5m])` to detect instability, System Load as a time series of `bm2_system_load_average` across all periods, and Memory Pressure as a gauge computing `1 - (bm2_system_memory_free_bytes / bm2_system_memory_total_bytes)`.
|
|
1192
|
+
|
|
1193
|
+
### Alert Rules Example
|
|
1194
|
+
|
|
1195
|
+
```
|
|
1196
|
+
groups:
|
|
1197
|
+
- name: bm2
|
|
1198
|
+
rules:
|
|
1199
|
+
- alert: ProcessDown
|
|
1200
|
+
expr: bm2_process_status == 0
|
|
1201
|
+
for: 1m
|
|
1202
|
+
labels:
|
|
1203
|
+
severity: critical
|
|
1204
|
+
annotations:
|
|
1205
|
+
summary: "Process {{ \$labels.name }} is down"
|
|
1206
|
+
|
|
1207
|
+
- alert: HighRestartRate
|
|
1208
|
+
expr: rate(bm2_process_restarts_total[5m]) > 0.1
|
|
1209
|
+
for: 5m
|
|
1210
|
+
labels:
|
|
1211
|
+
severity: warning
|
|
1212
|
+
annotations:
|
|
1213
|
+
summary: "Process {{ \$labels.name }} is restarting frequently"
|
|
1214
|
+
|
|
1215
|
+
- alert: HighMemoryUsage
|
|
1216
|
+
expr: bm2_process_memory_bytes > 1e9
|
|
1217
|
+
for: 5m
|
|
1218
|
+
labels:
|
|
1219
|
+
severity: warning
|
|
1220
|
+
annotations:
|
|
1221
|
+
summary: "Process {{ \$labels.name }} using > 1GB memory"
|
|
1222
|
+
```
|
|
1223
|
+
|
|
1224
|
+
---
|
|
1225
|
+
|
|
1226
|
+
## Programmatic API
|
|
1227
|
+
|
|
1228
|
+
BM2 can be used as a library in your own Bun applications:
|
|
1229
|
+
|
|
1230
|
+
```
|
|
1231
|
+
// app.ts
|
|
1232
|
+
import { ProcessManager } from "bm2/process-manager";
|
|
1233
|
+
import { Dashboard } from "bm2/dashboard";
|
|
1234
|
+
|
|
1235
|
+
const pm = new ProcessManager();
|
|
1236
|
+
|
|
1237
|
+
// Start a process
|
|
1238
|
+
const states = await pm.start({
|
|
1239
|
+
name: "my-api",
|
|
1240
|
+
script: "./server.ts",
|
|
1241
|
+
instances: 4,
|
|
1242
|
+
execMode: "cluster",
|
|
1243
|
+
port: 3000,
|
|
1244
|
+
env: { NODE_ENV: "production" },
|
|
1245
|
+
maxMemoryRestart: "512M",
|
|
1246
|
+
healthCheckUrl: "http://localhost:3000/health",
|
|
1247
|
+
});
|
|
1248
|
+
|
|
1249
|
+
console.log("Started:", states.map((s) => `${s.name} (pid: ${s.pid})`));
|
|
1250
|
+
|
|
1251
|
+
// List processes
|
|
1252
|
+
const list = pm.list();
|
|
1253
|
+
|
|
1254
|
+
// Get metrics
|
|
1255
|
+
const metrics = await pm.getMetrics();
|
|
1256
|
+
|
|
1257
|
+
// Scale
|
|
1258
|
+
await pm.scale("my-api", 8);
|
|
1259
|
+
|
|
1260
|
+
// Graceful reload
|
|
1261
|
+
await pm.reload("my-api");
|
|
1262
|
+
|
|
1263
|
+
// Start the web dashboard
|
|
1264
|
+
const dashboard = new Dashboard(pm);
|
|
1265
|
+
dashboard.start(9615, 9616);
|
|
1266
|
+
|
|
1267
|
+
// Get Prometheus-format metrics
|
|
1268
|
+
const promText = pm.getPrometheusMetrics();
|
|
1269
|
+
|
|
1270
|
+
// Save and restore
|
|
1271
|
+
await pm.save();
|
|
1272
|
+
await pm.resurrect();
|
|
1273
|
+
|
|
1274
|
+
// Stop everything
|
|
1275
|
+
await pm.stopAll();
|
|
1276
|
+
```
|
|
1277
|
+
|
|
1278
|
+
---
|
|
1279
|
+
|
|
1280
|
+
## Architecture
|
|
1281
|
+
|
|
1282
|
+
```
|
|
1283
|
+
┌─────────────────────────────────────────────────────────┐
|
|
1284
|
+
│ BM2 CLI │
|
|
1285
|
+
│ (bm2 start, bm2 list, bm2 restart, bm2 dashboard) │
|
|
1286
|
+
└────────────────────────┬────────────────────────────────┘
|
|
1287
|
+
│ Unix Socket (WebSocket)
|
|
1288
|
+
│ ~/.bm2/daemon.sock
|
|
1289
|
+
▼
|
|
1290
|
+
┌─────────────────────────────────────────────────────────┐
|
|
1291
|
+
│ BM2 Daemon │
|
|
1292
|
+
│ │
|
|
1293
|
+
│ ┌─────────────────┐ ┌──────────────┐ ┌───────────┐ │
|
|
1294
|
+
│ │ Process Manager │ │ Dashboard │ │ Modules │ │
|
|
1295
|
+
│ │ │ │ (Bun.serve) │ │ (Plugins)│ │
|
|
1296
|
+
│ │ ┌───────────┐ │ │ HTTP + WS │ └───────────┘ │
|
|
1297
|
+
│ │ │ Container │ │ │ REST API │ │
|
|
1298
|
+
│ │ │ (Bun.spawn)│ │ └──────────────┘ │
|
|
1299
|
+
│ │ └───────────┘ │ │
|
|
1300
|
+
│ │ ┌───────────┐ │ ┌──────────────┐ ┌───────────┐ │
|
|
1301
|
+
│ │ │ Container │ │ │ Monitor │ │ Metrics │ │
|
|
1302
|
+
│ │ │ (Bun.spawn)│ │ │ CPU/Memory │ │ Prometheus│ │
|
|
1303
|
+
│ │ └───────────┘ │ └──────────────┘ │ :9616 │ │
|
|
1304
|
+
│ │ ┌───────────┐ │ └───────────┘ │
|
|
1305
|
+
│ │ │ Container │ │ ┌──────────────┐ │
|
|
1306
|
+
│ │ │ (Bun.spawn)│ │ │ Health Check │ │
|
|
1307
|
+
│ │ └───────────┘ │ │ HTTP Probes │ │
|
|
1308
|
+
│ └─────────────────┘ └──────────────┘ │
|
|
1309
|
+
│ │
|
|
1310
|
+
│ ┌──────────┐ ┌──────────┐ ┌────────┐ ┌─────────────┐ │
|
|
1311
|
+
│ │ Cluster │ │ Logs │ │ Cron │ │ Deploy │ │
|
|
1312
|
+
│ │ Manager │ │ Manager │ │Manager │ │ Manager │ │
|
|
1313
|
+
│ └──────────┘ └──────────┘ └────────┘ └─────────────┘ │
|
|
1314
|
+
└─────────────────────────────────────────────────────────┘
|
|
1315
|
+
```
|
|
1316
|
+
|
|
1317
|
+
**Daemon Process** — The daemon is a long-running Bun process that manages all child processes. It listens on a Unix domain socket at `~/.bm2/daemon.sock` for commands from the CLI. The daemon is automatically started when you first run a BM2 command and can be explicitly killed with `bm2 kill`.
|
|
1318
|
+
|
|
1319
|
+
**Process Container** — Each managed process is wrapped in a ProcessContainer that handles spawning via `Bun.spawn`, log piping, monitoring, restart logic, health checking, watch mode, and signal handling.
|
|
1320
|
+
|
|
1321
|
+
**IPC Protocol** — The CLI and daemon communicate over WebSocket on a Unix socket. Messages are JSON-encoded with a `type` field for routing and an `id` field for request-response correlation.
|
|
1322
|
+
|
|
1323
|
+
**Dashboard** — The dashboard is served by a `Bun.serve` instance with WebSocket upgrade support. A single HTTP server handles the dashboard UI, REST API, and WebSocket connections.
|
|
1324
|
+
|
|
1325
|
+
**Metrics Server** — A separate `Bun.serve` instance on port 9616 serves Prometheus metrics, keeping the scrape endpoint isolated from dashboard traffic.
|
|
1326
|
+
|
|
1327
|
+
---
|
|
1328
|
+
|
|
1329
|
+
## Comparison with PM2
|
|
1330
|
+
|
|
1331
|
+
| Feature | PM2 | BM2 |
|
|
1332
|
+
|---|---|---|
|
|
1333
|
+
| Runtime | Node.js | Bun |
|
|
1334
|
+
| Language | JavaScript | TypeScript |
|
|
1335
|
+
| Dependencies | ~40+ packages | Zero (Bun built-ins only) |
|
|
1336
|
+
| Process Spawning | `child_process.fork` | `Bun.spawn` |
|
|
1337
|
+
| IPC | Custom protocol over pipes | WebSocket over Unix socket |
|
|
1338
|
+
| HTTP Server | Express/http | `Bun.serve` |
|
|
1339
|
+
| Log Compression | External `pm2-logrotate` module | Built-in `Bun.gzipSync` |
|
|
1340
|
+
| Dashboard | PM2 Plus (paid) or `pm2-monit` | Built-in web dashboard (free) |
|
|
1341
|
+
| Prometheus Metrics | `pm2-prometheus-exporter` module | Built-in native export |
|
|
1342
|
+
| Startup Time | ~500ms | ~50ms |
|
|
1343
|
+
| Memory Overhead | ~40MB (daemon) | ~12MB (daemon) |
|
|
1344
|
+
| Cluster Mode | `cluster` module | `Bun.spawn` with env-based routing |
|
|
1345
|
+
| Ecosystem Files | JSON, JS, YAML | JSON, TypeScript |
|
|
1346
|
+
| Deploy System | Built-in | Built-in |
|
|
1347
|
+
| Module System | `pm2 install` | `bm2 module install` |
|
|
1348
|
+
| TypeScript | Requires compilation | Native support |
|
|
1349
|
+
| File Watching | `chokidar` | Native `fs.watch` |
|
|
1350
|
+
|
|
1351
|
+
---
|
|
1352
|
+
|
|
1353
|
+
## Recipes and Examples
|
|
1354
|
+
|
|
1355
|
+
### Basic HTTP Server
|
|
1356
|
+
|
|
1357
|
+
```
|
|
1358
|
+
bm2 start server.ts --name api
|
|
1359
|
+
```
|
|
1360
|
+
|
|
1361
|
+
### Production API with Clustering and Health Checks
|
|
1362
|
+
|
|
1363
|
+
```
|
|
1364
|
+
bm2 start server.ts \
|
|
1365
|
+
--name api \
|
|
1366
|
+
--instances max \
|
|
1367
|
+
--port 3000 \
|
|
1368
|
+
--max-memory-restart 512M \
|
|
1369
|
+
--health-check-url http://localhost:3000/health \
|
|
1370
|
+
--health-check-interval 15000 \
|
|
1371
|
+
--log-max-size 50M \
|
|
1372
|
+
--log-retain 10 \
|
|
1373
|
+
--log-compress
|
|
1374
|
+
```
|
|
1375
|
+
|
|
1376
|
+
### Development Mode with Watch
|
|
1377
|
+
|
|
1378
|
+
```
|
|
1379
|
+
bm2 start server.ts --name dev-api --watch --ignore-watch node_modules,.git,dist
|
|
1380
|
+
```
|
|
1381
|
+
|
|
1382
|
+
### Python Script
|
|
1383
|
+
|
|
1384
|
+
```
|
|
1385
|
+
bm2 start worker.py --name py-worker --interpreter python3
|
|
1386
|
+
```
|
|
1387
|
+
|
|
1388
|
+
### Scheduled Restart (Daily at 3 AM)
|
|
1389
|
+
|
|
1390
|
+
```
|
|
1391
|
+
bm2 start server.ts --name api --cron "0 3 * * *"
|
|
1392
|
+
```
|
|
1393
|
+
|
|
1394
|
+
### Multiple Environments via Ecosystem
|
|
1395
|
+
|
|
1396
|
+
```
|
|
1397
|
+
{
|
|
1398
|
+
"apps": [
|
|
1399
|
+
{
|
|
1400
|
+
"name": "api-staging",
|
|
1401
|
+
"script": "./server.ts",
|
|
1402
|
+
"env": { "NODE_ENV": "staging", "PORT": "3000" }
|
|
1403
|
+
},
|
|
1404
|
+
{
|
|
1405
|
+
"name": "api-production",
|
|
1406
|
+
"script": "./server.ts",
|
|
1407
|
+
"env": { "NODE_ENV": "production", "PORT": "8080" }
|
|
1408
|
+
}
|
|
1409
|
+
]
|
|
1410
|
+
}
|
|
1411
|
+
```
|
|
1412
|
+
|
|
1413
|
+
### Full Production Setup
|
|
1414
|
+
|
|
1415
|
+
```
|
|
1416
|
+
bm2 start ecosystem.config.json
|
|
1417
|
+
bm2 save
|
|
1418
|
+
bm2 startup install
|
|
1419
|
+
bm2 dashboard
|
|
1420
|
+
bm2 list
|
|
1421
|
+
```
|
|
1422
|
+
|
|
1423
|
+
### Monitoring with Prometheus and Grafana
|
|
1424
|
+
|
|
1425
|
+
```
|
|
1426
|
+
bm2 dashboard --metrics-port 9616
|
|
1427
|
+
curl http://localhost:9616/metrics
|
|
1428
|
+
```
|
|
1429
|
+
|
|
1430
|
+
Then add the target to your `prometheus.yml` and import the Grafana dashboard.
|
|
1431
|
+
|
|
1432
|
+
### Zero-Downtime Deploy
|
|
1433
|
+
|
|
1434
|
+
```
|
|
1435
|
+
bm2 deploy ecosystem.config.json production
|
|
1436
|
+
```
|
|
1437
|
+
|
|
1438
|
+
Or manually:
|
|
1439
|
+
|
|
1440
|
+
```
|
|
1441
|
+
git pull origin main
|
|
1442
|
+
bun install
|
|
1443
|
+
bm2 reload all
|
|
1444
|
+
```
|
|
1445
|
+
|
|
1446
|
+
---
|
|
1447
|
+
|
|
1448
|
+
## Troubleshooting
|
|
1449
|
+
|
|
1450
|
+
### Daemon won't start
|
|
1451
|
+
|
|
1452
|
+
If BM2 commands hang or return connection errors, the daemon may have died without cleanup.
|
|
1453
|
+
|
|
1454
|
+
```
|
|
1455
|
+
rm -f ~/.bm2/daemon.sock ~/.bm2/daemon.pid
|
|
1456
|
+
bm2 list
|
|
1457
|
+
```
|
|
1458
|
+
|
|
1459
|
+
### Process keeps restarting
|
|
1460
|
+
|
|
1461
|
+
Check the error logs for crash information:
|
|
1462
|
+
|
|
1463
|
+
```
|
|
1464
|
+
bm2 logs my-app --err --lines 100
|
|
1465
|
+
```
|
|
1466
|
+
|
|
1467
|
+
If the process exits too quickly, it may hit the max restart limit. Check `minUptime` and `maxRestarts` settings:
|
|
1468
|
+
|
|
1469
|
+
```
|
|
1470
|
+
bm2 describe my-app
|
|
1471
|
+
```
|
|
1472
|
+
|
|
1473
|
+
Reset the counter if needed:
|
|
1474
|
+
|
|
1475
|
+
```
|
|
1476
|
+
bm2 reset my-app
|
|
1477
|
+
```
|
|
1478
|
+
|
|
1479
|
+
### High memory usage
|
|
1480
|
+
|
|
1481
|
+
If a process is using excessive memory and you have `maxMemoryRestart` configured, BM2 will restart it automatically. You can also check the metrics history:
|
|
1482
|
+
|
|
1483
|
+
```
|
|
1484
|
+
bm2 metrics --history 3600
|
|
1485
|
+
```
|
|
1486
|
+
|
|
1487
|
+
### Port conflicts
|
|
1488
|
+
|
|
1489
|
+
In cluster mode, each instance uses `basePort + instanceIndex`. Ensure no other services are using those ports:
|
|
1490
|
+
|
|
1491
|
+
```
|
|
1492
|
+
lsof -i :3000-3007
|
|
1493
|
+
```
|
|
1494
|
+
|
|
1495
|
+
### Log files growing too large
|
|
1496
|
+
|
|
1497
|
+
Enable log rotation:
|
|
1498
|
+
|
|
1499
|
+
```
|
|
1500
|
+
bm2 start server.ts --log-max-size 50M --log-retain 5 --log-compress
|
|
1501
|
+
```
|
|
1502
|
+
|
|
1503
|
+
Or flush existing logs:
|
|
1504
|
+
|
|
1505
|
+
```
|
|
1506
|
+
bm2 flush my-app
|
|
1507
|
+
```
|
|
1508
|
+
|
|
1509
|
+
### Dashboard not accessible
|
|
1510
|
+
|
|
1511
|
+
Ensure the dashboard is started and check the port:
|
|
1512
|
+
|
|
1513
|
+
```
|
|
1514
|
+
bm2 dashboard --port 9615
|
|
1515
|
+
curl http://localhost:9615
|
|
1516
|
+
```
|
|
1517
|
+
|
|
1518
|
+
If running behind a firewall, ensure port 9615 (dashboard) and 9616 (metrics) are open.
|
|
1519
|
+
|
|
1520
|
+
### Checking daemon health
|
|
1521
|
+
|
|
1522
|
+
```
|
|
1523
|
+
bm2 ping
|
|
1524
|
+
```
|
|
1525
|
+
|
|
1526
|
+
This returns the daemon PID and uptime. If it doesn't respond, the daemon needs to be restarted.
|
|
1527
|
+
|
|
1528
|
+
---
|
|
1529
|
+
|
|
1530
|
+
## File Structure
|
|
1531
|
+
|
|
1532
|
+
BM2 stores all data in `~/.bm2/`:
|
|
1533
|
+
|
|
1534
|
+
```
|
|
1535
|
+
~/.bm2/
|
|
1536
|
+
├── daemon.sock # Unix domain socket for IPC
|
|
1537
|
+
├── daemon.pid # Daemon process ID
|
|
1538
|
+
├── dump.json # Saved process list (bm2 save)
|
|
1539
|
+
├── config.json # Global configuration
|
|
1540
|
+
├── env-registry.json # Stored environment variables
|
|
1541
|
+
├── logs/ # Process log files
|
|
1542
|
+
│ ├── my-api-0-out.log
|
|
1543
|
+
│ ├── my-api-0-error.log
|
|
1544
|
+
│ ├── my-api-0-out.log.1.gz
|
|
1545
|
+
│ └── daemon-out.log
|
|
1546
|
+
├── pids/ # PID files
|
|
1547
|
+
│ └── my-api-0.pid
|
|
1548
|
+
├── metrics/ # Persisted metric snapshots
|
|
1549
|
+
└── modules/ # Installed BM2 modules
|
|
1550
|
+
```
|
|
1551
|
+
|
|
1552
|
+
---
|
|
1553
|
+
|
|
1554
|
+
## Contributing
|
|
1555
|
+
|
|
1556
|
+
Contributions are welcome. Please follow these guidelines:
|
|
1557
|
+
|
|
1558
|
+
1. Fork the repository and create a feature branch.
|
|
1559
|
+
2. Write tests for new functionality.
|
|
1560
|
+
3. Follow the existing code style — TypeScript strict mode, no `any` where avoidable.
|
|
1561
|
+
4. Run the test suite before submitting: `bun test`.
|
|
1562
|
+
5. Submit a pull request with a clear description of the change.
|
|
1563
|
+
|
|
1564
|
+
### Development Setup
|
|
1565
|
+
|
|
1566
|
+
```
|
|
1567
|
+
git clone https://github.com/aspect-dev/bm2.git
|
|
1568
|
+
cd bm2
|
|
1569
|
+
bun install
|
|
1570
|
+
bun run src/index.ts list
|
|
1571
|
+
bun test
|
|
1572
|
+
```
|
|
1573
|
+
|
|
1574
|
+
---
|
|
1575
|
+
|
|
1576
|
+
## License
|
|
1577
|
+
|
|
1578
|
+
MIT License
|
|
1579
|
+
|
|
1580
|
+
Copyright (c) 2025 MaxxPainn Team
|
|
1581
|
+
|
|
1582
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
1583
|
+
|
|
1584
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
1585
|
+
|
|
1586
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
1587
|
+
|
|
1588
|
+
---
|
|
1589
|
+
|
|
1590
|
+
**Built with ❤️ by the [MaxxPainn Team](https://maxxpainn.com)**
|
|
1591
|
+
📧 Support: [hello@maxxpainn.com](mailto:hello@maxxpainn.com)
|