The oxfile.toml is the single source of truth for everything Oxmgr manages. No CLI flags to remember, no hidden state — your entire process configuration is in one file, version-controlled, reviewable in PRs.
This is the complete reference. Every field, every option, with working examples.
File Location
Oxmgr looks for oxfile.toml in this order:
- Path passed via
--configflag:oxmgr start --config /etc/myapp/oxfile.toml - Current working directory:
./oxfile.toml - User config dir:
~/.config/oxmgr/oxfile.toml
Commit oxfile.toml to your repository root alongside package.json.
Minimal Configuration
The smallest valid oxfile:
[processes.app]
command = "node server.js" This starts node server.js, restarts it on crash, and uses sensible defaults for everything else.
Process Configuration
Every process lives under [processes.<name>]. The name is how you reference it in CLI commands.
command (required)
The shell command to run:
[processes.api]
command = "node dist/server.js"
[processes.worker]
command = "python -m celery worker -A tasks"
[processes.proxy]
command = "/usr/local/bin/caddy run --config /etc/caddy/Caddyfile" The command runs in a shell (/bin/sh -c), so pipes and redirects work:
[processes.migrator]
command = "node migrate.js && echo 'done'" working_dir
Working directory for the process. Defaults to the directory containing oxfile.toml.
[processes.frontend]
command = "npm run preview"
working_dir = "/var/www/frontend" instances
Number of parallel instances to run. Useful for CPU-bound Node.js apps with cluster mode, or to run multiple identical workers:
[processes.api]
command = "node dist/server.js"
instances = 4 # Run 4 parallel instances When instances > 1, Oxmgr passes the instance index via OXMGR_INSTANCE_ID (0-indexed). Use it to assign unique ports if your app doesn’t share a port via a load balancer:
[processes.api]
command = "node dist/server.js"
instances = 4
env = { PORT = "3000" } # BASE port — OXMGR_INSTANCE_ID offsets it // In your server.js
const port = parseInt(process.env.PORT) + parseInt(process.env.OXMGR_INSTANCE_ID || '0');
server.listen(port); restart_on_exit
Restart the process when it exits, regardless of exit code:
[processes.api]
command = "node server.js"
restart_on_exit = true # default: false Set to false for one-shot processes (migrations, jobs):
[processes.migrate]
command = "node migrate.js"
restart_on_exit = false restart_delay_ms
Milliseconds to wait before restarting after a crash. Prevents tight restart loops:
[processes.api]
command = "node server.js"
restart_on_exit = true
restart_delay_ms = 1000 # wait 1 second before restarting Use exponential backoff for flaky services by increasing this value:
[processes.flaky-integration]
command = "node integration.js"
restart_on_exit = true
restart_delay_ms = 5000 # wait 5 seconds — gives external service time to recover max_restarts
Stop restarting after N consecutive crashes. Prevents infinite crash loops that consume system resources:
[processes.api]
command = "node server.js"
restart_on_exit = true
max_restarts = 10 # give up after 10 crashes, leave the process stopped When max_restarts is reached, oxmgr status shows the process as crashed and requires manual intervention (oxmgr restart api).
Set to 0 for unlimited restarts (use with caution):
restart_on_exit = true
max_restarts = 0 # never give up — useful if the app must always run stop_signal
Signal sent to the process when stopping or reloading. Default is SIGTERM:
[processes.api]
command = "node server.js"
stop_signal = "SIGTERM" # default — use for graceful shutdown Use SIGINT if your app handles Ctrl+C but not SIGTERM:
stop_signal = "SIGINT" stop_timeout_ms
How long to wait for graceful shutdown before force-killing with SIGKILL. Default: 10000 (10 seconds):
[processes.api]
command = "node server.js"
stop_timeout_ms = 30000 # wait up to 30s for graceful shutdown If your app has long-running requests (file uploads, streaming), increase this:
stop_timeout_ms = 60000 # 60 seconds for long-running requests Environment Variables
env — Inline Variables
[processes.api]
command = "node server.js"
env = { NODE_ENV = "production", PORT = "3000", LOG_LEVEL = "info" } For many variables, use multi-line TOML syntax:
[processes.api.env]
NODE_ENV = "production"
PORT = "3000"
LOG_LEVEL = "info"
DATABASE_URL = "postgresql://user:pass@localhost/mydb"
REDIS_URL = "redis://localhost:6379"
JWT_SECRET = "your-secret-here" env_file — Load from File
Load variables from a .env file:
[processes.api]
command = "node server.js"
env_file = ".env.production" Variables in env take precedence over env_file if both are set. Use this pattern for secrets: keep secrets in .env.production (not committed), and non-secrets in env:
[processes.api]
command = "node server.js"
env_file = ".env.production" # contains DATABASE_URL, JWT_SECRET, etc.
[processes.api.env]
NODE_ENV = "production" # safe to commit
PORT = "3000" Health Checks
Health checks determine if a process is healthy. Oxmgr polls the endpoint; if it returns non-200, the process is marked unhealthy and restarted.
[processes.api.health_check]
endpoint = "http://localhost:3000/health"
interval_secs = 30 # check every 30 seconds
timeout_secs = 5 # fail if no response within 5 seconds
healthy_threshold = 1 # passes after 1 successful check
unhealthy_threshold = 3 # fails after 3 consecutive failures
initial_delay_secs = 10 # wait 10s after start before first check initial_delay_secs is critical for slow-starting apps. Without it, Oxmgr may restart a perfectly healthy app that just hasn’t finished initializing.
For apps with database migrations or heavy startup:
[processes.api.health_check]
endpoint = "http://localhost:3000/health"
interval_secs = 10
initial_delay_secs = 60 # app takes up to 60s to be ready
unhealthy_threshold = 5 Health checks on oxmgr reload (rolling restart): Oxmgr waits for the new instance to pass healthy_threshold consecutive checks before stopping the old instance. This is what makes zero-downtime deploys work.
File Watching
Automatically restart the process when files change. Designed for development, but useful in production for interpreted languages:
[processes.api]
command = "node server.js"
watch = ["src", "config"] # watch these directories
watch_extensions = [".js", ".ts", ".json"] # only restart for these file types
watch_ignore = ["node_modules", "*.log"] # ignore these patterns Disable watch in production by not setting watch. Or use separate config files per environment:
# Development
oxmgr start --config oxfile.dev.toml
# Production
oxmgr start --config oxfile.toml Resource Limits
Cap CPU and memory per process. When limits are exceeded, Oxmgr sends stop_signal and restarts:
[processes.api.limits]
memory_mb = 512 # restart if RSS exceeds 512 MB
cpu_percent = 80 # restart if CPU >80% for the sustained period
cpu_window_secs = 60 # measure CPU over 60-second windows Useful for containing memory leaks — set a generous limit above normal usage, and the process restarts when it hits the ceiling rather than crashing the whole server.
[processes.worker.limits]
memory_mb = 256 # worker normally uses 64MB, 256MB means "something is wrong" Log Configuration
[processes.api]
command = "node server.js"
log_file = "/var/log/api/out.log"
error_log_file = "/var/log/api/error.log"
log_date_format = "YYYY-MM-DD HH:mm:ss" Disable log files to let systemd/journald capture output:
[processes.api]
command = "node server.js"
# No log_file — stdout/stderr go to the terminal or journald Log rotation — Oxmgr rotates log files automatically:
[processes.api.logs]
max_size_mb = 100 # rotate when log file reaches 100 MB
max_files = 5 # keep 5 rotated files
compress = true # compress old files with gzip Git Webhooks
Auto-deploy when a branch is pushed to. Oxmgr starts a webhook server that listens for GitHub/GitLab push events:
[processes.api.webhook]
enabled = true
port = 9000
secret = "your-webhook-secret" # must match GitHub webhook secret
branch = "main" # only deploy on pushes to main
# Commands to run on deploy
on_push = [
"git pull origin main",
"npm ci",
"npm run build",
"oxmgr reload api"
] In GitHub: Settings → Webhooks → Add webhook:
- Payload URL:
http://your-server.com:9000 - Content type:
application/json - Secret: same as
secretabove - Events:
Just the push event
Complete Example: Multi-Service App
A realistic oxfile for a web app with API, background worker, and scheduled job:
# oxfile.toml
[processes.api]
command = "node dist/api/server.js"
instances = 2
restart_on_exit = true
restart_delay_ms = 1000
max_restarts = 20
stop_timeout_ms = 30000
env_file = ".env.production"
[processes.api.env]
NODE_ENV = "production"
PORT = "3000"
LOG_LEVEL = "info"
[processes.api.health_check]
endpoint = "http://localhost:3000/health"
interval_secs = 30
initial_delay_secs = 15
unhealthy_threshold = 3
healthy_threshold = 2
[processes.api.limits]
memory_mb = 1024
[processes.api.logs]
max_size_mb = 200
max_files = 7
compress = true
[processes.worker]
command = "node dist/worker/index.js"
instances = 1
restart_on_exit = true
restart_delay_ms = 2000
max_restarts = 10
env_file = ".env.production"
[processes.worker.env]
NODE_ENV = "production"
WORKER_CONCURRENCY = "5"
[processes.worker.limits]
memory_mb = 512
[processes.scheduler]
command = "node dist/scheduler.js"
instances = 1
restart_on_exit = true
max_restarts = 5
[processes.scheduler.env]
NODE_ENV = "production"
[processes.migrate]
command = "node dist/migrate.js"
restart_on_exit = false # run once, don't restart Start everything:
oxmgr start Start only the API:
oxmgr start api Deploy with zero downtime:
npm run build && oxmgr reload api CLI Command Reference
All commands accept an optional process name. Without a name, they apply to all processes.
oxmgr start # start all processes
oxmgr start api # start only 'api'
oxmgr stop # stop all
oxmgr stop worker # stop only 'worker'
oxmgr restart api # hard restart (gaps between stop and start)
oxmgr reload api # rolling restart — zero downtime
oxmgr status # show all process status
oxmgr status --json # machine-readable JSON output
oxmgr logs api # tail logs for 'api'
oxmgr logs api -n 200 # last 200 lines
oxmgr import ecosystem.config.js # migrate from PM2 Validating Your Config
Before deploying, validate syntax:
oxmgr validate
# ✓ oxfile.toml is valid
# 3 processes defined: api, worker, scheduler Dry-run to see what would happen:
oxmgr start --dry-run
# Would start: api (×2 instances), worker, scheduler Full documentation at oxmgr.empellio.com/docs.