<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Empellio.com]]></title><description><![CDATA[Empellio.com]]></description><link>https://empellio.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Sat, 12 Sep 2026 07:52:17 GMT</lastBuildDate><atom:link href="https://empellio.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[What Is Crash Recovery? How Process Managers Keep Your App Online After Failures]]></title><description><![CDATA[What Is Crash Recovery?
Your production app crashes. A bug slips through, memory spikes, a network dependency times out and throws an unhandled exception — it doesn't matter why. What matters is what ]]></description><link>https://empellio.hashnode.dev/what-is-crash-recovery-how-process-managers-keep-your-app-online-after-failures</link><guid isPermaLink="true">https://empellio.hashnode.dev/what-is-crash-recovery-how-process-managers-keep-your-app-online-after-failures</guid><category><![CDATA[Node.js]]></category><category><![CDATA[node]]></category><category><![CDATA[Oxmgr]]></category><category><![CDATA[pm2]]></category><category><![CDATA[process Manager]]></category><dc:creator><![CDATA[Empellio]]></dc:creator><pubDate>Thu, 12 Mar 2026 10:37:39 GMT</pubDate><content:encoded><![CDATA[<h1>What Is Crash Recovery?</h1>
<p>Your production app crashes. A bug slips through, memory spikes, a network dependency times out and throws an unhandled exception — it doesn't matter why. What matters is what happens next.</p>
<p><strong>Crash recovery</strong> is the automatic process of detecting that an application has died and restarting it as fast as possible, before your users have time to notice.</p>
<p>Without crash recovery, a process that crashes stays dead until a human intervenes. With it, the same crash can be invisible — the process restarts in milliseconds and keeps serving traffic.</p>
<p>Crash recovery is one of the core reasons you need a <a href="https://oxmgr.empellio.com/blog/what-is-a-process-manager">process manager</a> in production — without one, there's nothing watching your app to trigger a restart.</p>
<h2>How Crash Recovery Works</h2>
<p>Every operating system gives processes a way to signal their exit. When a process terminates — whether it crashes, runs out of memory, or is killed — it emits an exit event with a status code.</p>
<p>A process manager listens for these events:</p>
<pre><code class="language-plaintext">App process exits (status: 1 — error)
        ↓
Process manager receives exit event
        ↓
Check: is this process configured to restart?
        ↓
Yes → spawn new process
        ↓
Wait for process to be ready (health check or port listen)
        ↓
Resume serving traffic
</code></pre>
<p>The critical variable is how long this takes. The gap between the exit event and the new process serving traffic is your <strong>downtime window</strong>.</p>
<h2>What Determines Recovery Speed</h2>
<p>Three factors control how fast a process manager can recover from a crash:</p>
<h3>1. The Manager's Own Runtime</h3>
<p>A process manager written in a scripting language (JavaScript, Python, Ruby) has to do real work to respond to an exit event — the VM needs to be scheduled, the garbage collector might pause, the event loop might be busy.</p>
<p>A compiled binary (Rust, Go, C) responds in microseconds. There's no VM, no GC, no interpreter. The exit handler fires and the spawn call happens immediately.</p>
<p>This is the biggest factor. PM2 (Node.js daemon) recovers in ~400ms. Oxmgr (Rust binary) recovers in ~11ms.</p>
<h3>2. Process Spawn Time</h3>
<p>Spawning a new process takes time regardless of the manager. For a Node.js app:</p>
<ul>
<li><p>OS process creation: ~1–5ms</p>
</li>
<li><p>Node.js startup: ~50–200ms (depending on module load time)</p>
</li>
<li><p>Application initialization: varies</p>
</li>
</ul>
<p>The process manager can't control how fast your app starts. But it can start the spawn immediately after detecting the crash, rather than waiting for polling intervals.</p>
<h3>3. Health Check Configuration</h3>
<p>After spawning, the manager needs to know when the process is ready. Two approaches:</p>
<p><strong>Port listening</strong> — wait until the process binds to its port. Simple, but doesn't guarantee the app is actually serving valid responses.</p>
<p><strong>HTTP health check</strong> — poll an endpoint until it returns 200. Slower to confirm readiness, but more accurate.</p>
<pre><code class="language-plaintext">[processes.api.health_check]
endpoint = "http://localhost:3000/health"
interval_secs = 2
timeout_secs = 5
</code></pre>
<p>For crash recovery, the key is not waiting <em>longer</em> than necessary. If your health check polls every 30 seconds but a crash recovers in 50ms, you're waiting 30 seconds to confirm what already happened.</p>
<h2>What Happens If an App Keeps Crashing?</h2>
<p>Automatic restart can create a "crash loop" — the app restarts, crashes immediately, restarts again, endlessly. This is worse than staying down in some ways: it makes logs unreadable and consumes CPU spinning up processes.</p>
<p>Most process managers handle this with restart limits and backoff:</p>
<pre><code class="language-plaintext">[processes.api]
max_restarts = 10          # stop trying after 10 crashes
restart_delay_ms = 500     # wait 500ms before each restart
</code></pre>
<p>Exponential backoff is more sophisticated — the delay doubles each time:</p>
<ul>
<li><p>Crash 1: restart after 100ms</p>
</li>
<li><p>Crash 2: restart after 200ms</p>
</li>
<li><p>Crash 3: restart after 400ms</p>
</li>
<li><p>...</p>
</li>
</ul>
<p>This gives transient issues (network blips, temporary resource exhaustion) time to resolve while preventing runaway loops.</p>
<h2>Crash Recovery vs. High Availability</h2>
<p>These are related but different concepts:</p>
<p><strong>Crash recovery</strong> handles the period <em>after</em> a single process crashes — the goal is to minimize downtime for that process.</p>
<p><strong>High availability</strong> uses redundancy to eliminate downtime entirely — run 2+ instances so when one crashes, others continue serving traffic while the crashed one recovers.</p>
<pre><code class="language-plaintext">[processes.api]
instances = 3    # crash recovery on one instance doesn't affect the other 2
</code></pre>
<p>With 3 instances and 11ms crash recovery, a user hitting the crashed instance during that window is the only exposure. In practice, load balancers have already stopped routing to the crashed process within a similar timeframe.</p>
<h2>Measuring Crash Recovery in Your Setup</h2>
<p>You can test your crash recovery speed manually:</p>
<pre><code class="language-plaintext"># Find your process PID
oxmgr status

# Kill it hard (no graceful shutdown)
kill -9 &lt;pid&gt;

# Measure how long until it responds again
time curl --retry 100 --retry-delay 0 --retry-connrefused http://localhost:3000/health
</code></pre>
<p>For PM2 users, the same test will show you real-world recovery times rather than theoretical numbers.</p>
<h2>Crash Recovery in Oxmgr</h2>
<p>Oxmgr is built around the assumption that crash recovery should be invisible to users. Key settings:</p>
<pre><code class="language-plaintext">[processes.api]
command = "node dist/server.js"
restart_on_exit = true
restart_delay_ms = 0         # restart immediately
max_restarts = 20            # allow 20 restarts before giving up
instances = 2                # run 2 instances for redundancy

[processes.api.health_check]
endpoint = "http://localhost:3000/health"
interval_secs = 10
timeout_secs = 3
</code></pre>
<p>With this config, a crash on one instance triggers an immediate restart. The other instance handles traffic during the ~50ms window (11ms manager + ~40ms Node.js startup for a simple app).</p>
<p>See the <a href="https://oxmgr.empellio.com/docs#health-checks">docs</a> for health check configuration and resource limit triggers.</p>
]]></content:encoded></item><item><title><![CDATA[PM2 Alternatives in 2026: What Are Your Options?]]></title><description><![CDATA[PM2 has been the default process manager for Node.js deployments for years. It works, it's battle-tested, and most tutorials assume you're using it.
But in 2026, there are real alternatives worth cons]]></description><link>https://empellio.hashnode.dev/pm2-alternatives-in-2026-what-are-your-options</link><guid isPermaLink="true">https://empellio.hashnode.dev/pm2-alternatives-in-2026-what-are-your-options</guid><category><![CDATA[Node.js]]></category><category><![CDATA[Devops]]></category><category><![CDATA[Rust]]></category><category><![CDATA[Programming Tips]]></category><category><![CDATA[Open Source]]></category><category><![CDATA[pm2]]></category><category><![CDATA[alternative]]></category><category><![CDATA[webdev]]></category><dc:creator><![CDATA[Empellio]]></dc:creator><pubDate>Fri, 06 Mar 2026 15:11:51 GMT</pubDate><content:encoded><![CDATA[<p>PM2 has been the default process manager for Node.js deployments for years. It works, it's battle-tested, and most tutorials assume you're using it.</p>
<p>But in 2026, there are real alternatives worth considering — especially if you're running memory-constrained VPS instances, managing multi-language stacks, or just tired of PM2's overhead. Here's an honest breakdown of what's available.</p>
<h2>Why Look for a PM2 Alternative?</h2>
<p>PM2 is a Node.js application. That means:</p>
<ul>
<li><p>The daemon itself consumes significant memory (~60MB idle, ~148MB at 100 processes)</p>
</li>
<li><p>Crash recovery routes through the Node.js event loop, adding latency</p>
</li>
<li><p>It's Node.js-only by design — managing Python or Go services is a workaround, not a feature For many teams these tradeoffs are fine. But if you're on a 1GB VPS or running a mixed-language stack, they start to matter.</p>
</li>
</ul>
<h2><strong>The Alternatives</strong></h2>
<h3><strong>1. Oxmgr</strong></h3>
<p><strong>Best for:</strong> mixed-language stacks, memory-constrained VPS, PM2 users who want a drop-in replacement Oxmgr is a Rust-based process manager built as a direct PM2 alternative. It supports PM2's <code>ecosystem.config.js</code> format for easy migration, and adds a native <code>oxfile.toml</code> config format for cleaner multi-service setups.</p>
<p><strong>Benchmark numbers (Linux, GitHub Actions runners):</strong></p>
<p><code>Crash detection: 4ms vs PM2's 170ms (42x faster)</code></p>
<p><code>Daemon memory: 7MB vs PM2's 148MB (19x lower at 100 processes)</code></p>
<p><code>Start 100 processes: 834ms vs PM2's 6.1s (7x faster)</code></p>
<p><strong>Key features:</strong></p>
<ul>
<li><p>Language-agnostic — Node.js, Python, Go, Rust, anything executable</p>
</li>
<li><p>Cross-platform — Linux, macOS, Windows</p>
</li>
<li><p>Zero-downtime reloads with health check gating</p>
</li>
<li><p>Git pull workflow <code>oxmgr pull</code> reloads only when commit changed)</p>
</li>
<li><p>Terminal UI <code>oxmgr ui</code>)</p>
</li>
<li><p>Installs as systemd/launchd/Task Scheduler service</p>
</li>
</ul>
<p><strong>Install:</strong></p>
<p><code>npm install -g oxmgr</code></p>
<p><strong>Repo</strong>: <a href="https://github.com/Vladimir-Urik/OxMgr">https://github.com/Vladimir-Urik/OxMgr</a><br /><strong>Caveat</strong> v0.1.x — actively developed but early. No hosted dashboard like PM2 Plus.</p>
<h3>2. systemd (Linux only)</h3>
<p><strong>Best for:</strong> production Linux servers where you want OS-native service management</p>
<p>systemd is the init system on most Linux distributions. You can manage any long-running service with a Unit file — no third-party tool required.</p>
<p><strong>Pros:</strong></p>
<ul>
<li><p>Built into Linux, zero overhead</p>
</li>
<li><p>Deep OS integration — dependency graph, journal logging, socket activation</p>
</li>
<li><p>Rock solid, battle-tested at scale</p>
</li>
</ul>
<p><strong>Cons:</strong></p>
<ul>
<li><p>Unit file per service — verbose for large fleets</p>
</li>
<li><p>Linux only</p>
</li>
<li><p>No quick <code>list</code> command showing CPU/RAM at a glance</p>
</li>
<li><p>Steeper learning curve for developers who just want to run a script</p>
</li>
</ul>
<p>systemd is the right choice for system-level services. For per-project or per-user process management, most developers reach for something on top of it.</p>
<h3>3. Supervisor</h3>
<p><strong>Best for:</strong> Python-heavy stacks, teams comfortable with Python tooling</p>
<p>Supervisor is a Python-based process control system that's been around since 2004. It's stable, well-documented, and widely used in Python deployments.</p>
<p><strong>Pros:</strong></p>
<ul>
<li><p>Mature and stable</p>
</li>
<li><p>Simple INI-style config</p>
</li>
<li><p>Good Python ecosystem integration</p>
</li>
</ul>
<p><strong>Cons:</strong></p>
<ul>
<li><p>Python dependency</p>
</li>
<li><p>No cross-platform Windows support</p>
</li>
<li><p>Limited terminal UI</p>
</li>
<li><p>Less active development in recent years</p>
</li>
</ul>
<h3>4. Foreman / Overmind</h3>
<p><strong>Best for:</strong> local development, Procfile-based workflows</p>
<p>Foreman (Ruby) and Overmind (Go) manage processes defined in a <code>Procfile</code>. Popular in Rails and Heroku-style workflows.</p>
<p><strong>Pros:</strong></p>
<ul>
<li><p>Simple Procfile format</p>
</li>
<li><p>Good for local dev multi-process setups</p>
</li>
<li><p>Overmind adds tmux integration</p>
</li>
</ul>
<p><strong>Cons:</strong></p>
<ul>
<li><p>Not designed for production supervision</p>
</li>
<li><p>No persistent daemon — processes stop when you close the terminal</p>
</li>
<li><p>Limited restart policies</p>
</li>
</ul>
<h3>5. Docker / Docker Compose</h3>
<p><strong>Best for:</strong> teams who want full containerization</p>
<p>If you're already containerizing your services, Docker Compose handles process orchestration as part of the container lifecycle.</p>
<p><strong>Pros:</strong></p>
<ul>
<li><p>Full environment isolation</p>
</li>
<li><p>Scales to Kubernetes if needed</p>
</li>
<li><p>Industry standard</p>
</li>
</ul>
<p><strong>Cons:</strong></p>
<ul>
<li><p>Significant overhead for simple VPS deployments</p>
</li>
<li><p>Overkill if you just want to run a few scripts on a server</p>
</li>
<li><p>Docker daemon adds its own memory footprint</p>
</li>
</ul>
<h2>Quick Comparison</h2>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Language</th>
<th>Cross-platform</th>
<th>Memory overhead</th>
<th>Crash recovery</th>
<th>Best for</th>
</tr>
</thead>
<tbody><tr>
<td>PM2</td>
<td>Node.js</td>
<td>✅</td>
<td>High (~148MB/100)</td>
<td>Slow (~170ms)</td>
<td>Node.js teams</td>
</tr>
<tr>
<td>Oxmgr</td>
<td>Rust</td>
<td>✅</td>
<td>Low (~7MB/100)</td>
<td>Fast (~4ms)</td>
<td>Mixed stacks, VPS</td>
</tr>
<tr>
<td>systemd</td>
<td>C</td>
<td>Linux only</td>
<td>Minimal</td>
<td>Very fast</td>
<td>Linux system services</td>
</tr>
<tr>
<td>Supervisor</td>
<td>Python</td>
<td>Linux/macOS</td>
<td>Low</td>
<td>Medium</td>
<td>Python stacks</td>
</tr>
<tr>
<td>Foreman/Overmind</td>
<td>Ruby/Go</td>
<td>macOS/Linux</td>
<td>Low</td>
<td>N/A</td>
<td>Local dev</td>
</tr>
<tr>
<td>Docker Compose</td>
<td>Go</td>
<td>✅</td>
<td>High</td>
<td>Depends</td>
<td>Containerized stacks</td>
</tr>
</tbody></table>
<h2>Which One Should You Choose?</h2>
<p><strong>Stick with PM2 if</strong> you're running a pure Node.js stack, you use PM2 Plus, and memory isn't a concern.</p>
<p><strong>Switch to Oxmgr if</strong> you're on a memory-constrained VPS, you manage multiple languages, or you want faster crash recovery without changing your workflow much.</p>
<p><strong>Use systemd if</strong> you're on Linux and want zero dependencies — just write Unit files and let the OS handle it.</p>
<p><strong>Use Supervisor if</strong> you're primarily in Python and want a battle-tested tool with minimal setup.</p>
<p><strong>Use Docker Compose if</strong> you're already containerizing and want to stay in that ecosystem.</p>
<hr />
<p><em>Oxmgr is open source under MIT. Repo and full benchmark methodology:</em> <a href="https://github.com/Vladimir-Urik/OxMgr"><em>github.com/Vladimir-Urik/OxMgr</em></a></p>
]]></content:encoded></item></channel></rss>