Skip to main content
Historical Migration DocumentationThis document describes the PM2 to Bun migration that occurred in v7.1.0 (December 2025). If you’re installing claude-mem for the first time, this migration has already been completed and you can use the current Bun-based system documented in the main guides.This documentation is preserved for users upgrading from versions older than v7.1.0.

PM2 to Bun Migration: Complete Technical Documentation

Version: 7.1.0 Date: December 2025 Migration Type: Process Management (PM2 → Bun) + Database Driver (better-sqlite3 → bun:sqlite)

Executive Summary

Claude-mem version 7.1.0 introduces two major architectural migrations:
  1. Process Management: PM2 → Custom Bun-based ProcessManager
  2. Database Driver: better-sqlite3 npm package → bun:sqlite runtime module
Both migrations are automatic and transparent to end users. The first time a hook fires after updating to 7.1.0+, the system performs a one-time cleanup of legacy PM2 processes and transitions to the new architecture.

Key Benefits

  • Simplified Dependencies: Removes PM2 and better-sqlite3 npm packages
  • Improved Cross-Platform Support: Better Windows compatibility
  • Faster Installation: No native module compilation required
  • Built-in Runtime: Leverages Bun’s built-in process management and SQLite
  • Reduced Complexity: Custom ProcessManager is simpler than PM2 integration

Migration Impact

  • Data Preservation: User data, settings, and database remain unchanged
  • Automatic Cleanup: Old PM2 processes automatically terminated (all platforms)
  • No User Action Required: Migration happens automatically on first hook trigger
  • Backward Compatible: SQLite database format unchanged (only driver changed)

Architecture Comparison

Old System (PM2-based)

Component: PM2 (Process Manager 2)
  • Package: pm2 npm dependency
  • Process Name: claude-mem-worker
  • Management: External PM2 daemon manages lifecycle
  • Discovery: pm2 list, pm2 describe commands
  • Auto-restart: PM2 automatically restarts on crash
  • Logs: ~/.pm2/logs/claude-mem-worker-*.log
  • PID File: ~/.pm2/pids/claude-mem-worker.pid
Lifecycle Commands:
Pain Points:
  • Additional npm dependency required
  • PM2 daemon must be running
  • Potential conflicts with other PM2 processes
  • Windows compatibility issues
  • Complex configuration for simple use case
Component: better-sqlite3
  • Package: better-sqlite3 npm package (native module)
  • Installation: Requires native compilation (node-gyp)
  • Windows: Requires Visual Studio build tools + Python
  • Import: import Database from 'better-sqlite3'
Installation Requirements:
  • Node.js development headers
  • C++ compiler (gcc/clang on Mac/Linux, MSVC on Windows)
  • Python (for node-gyp)
  • Windows: Visual Studio Build Tools

New System (Bun-based)

Component: Custom ProcessManager (src/services/process/ProcessManager.ts)
  • Package: Built-in Bun APIs (no external dependency)
  • Process Spawn: Bun.spawn() with detached mode
  • Management: Direct process control via PID file
  • Discovery: PID file + process existence check + HTTP health check
  • Auto-restart: Hook-triggered restart on failure detection
  • Logs: ~/.claude-mem/logs/worker-YYYY-MM-DD.log
  • PID File: ~/.claude-mem/.worker.pid
  • Port File: ~/.claude-mem/.worker.port (new)
Lifecycle Commands:
Core Mechanisms:
  1. PID File Management:
    • File: ~/.claude-mem/.worker.pid
    • Content: Process ID (e.g., “35557”)
    • Validation: Process existence via kill(pid, 0) signal
  2. Port File Management:
    • File: ~/.claude-mem/.worker.port
    • Content: Two lines (port number, PID)
    • Purpose: Track port binding and validate PID match
  3. Health Checking:
    • Layer 1: PID file exists?
    • Layer 2: Process alive? (kill(pid, 0))
    • Layer 3: HTTP health check (GET /health)
    • All three must pass for “healthy” status
Advantages:
  • No external dependencies
  • Simpler codebase (direct control)
  • Better error handling and validation
  • Platform-agnostic (Bun handles platform differences)
Component: bun:sqlite
  • Package: Built into Bun runtime (no npm package)
  • Installation: None required (comes with Bun ≥1.0)
  • Platform: Works anywhere Bun works
  • Import: import { Database } from 'bun:sqlite'
  • API: Similar to better-sqlite3 (synchronous)
Installation Requirements:
  • Bun ≥1.0 (automatically installed if missing)
  • No native compilation required
  • No platform-specific build tools needed
Compatibility:
  • SQLite database format: Unchanged
  • Database file: ~/.claude-mem/claude-mem.db (same location)
  • Query syntax: Identical (both use SQLite SQL)

Migration Mechanics

One-Time PM2 Cleanup

The migration system uses a marker-based approach to perform PM2 cleanup exactly once. Implementation: src/shared/worker-utils.ts:73-86

Migration Trigger Points

1

Hook Execution

SessionStart, UserPromptSubmit, or PostToolUse hooks execute using new 7.1.0 code
2

Worker Status Check

ensureWorkerRunning() checks if ~/.claude-mem/.worker.pid exists (it doesn’t for first run after update)
3

Start Worker Decision

Worker not running → Call startWorker()
4

Migration Check

Check if ~/.claude-mem/.pm2-migrated exists
5

PM2 Cleanup

Execute pm2 delete claude-mem-worker (errors ignored), create marker file
6

New Worker Start

Spawn new Bun-managed worker process with PID and port files

Marker File

Location: ~/.claude-mem/.pm2-migrated Content: ISO 8601 timestamp
Purpose:
  • One-time migration flag
  • Prevents repeated PM2 cleanup on every start
  • Persists across restarts and reboots
Lifecycle:
  • Created: First hook trigger after update to 7.1.0+ (all platforms)
  • Updated: Never
  • Deleted: Never (user could manually delete to force re-migration)

User Experience Timeline

First Session After Update

This is the critical migration moment. The process takes approximately 2-5 seconds.
Step-by-Step Execution:
  1. Hook fires (SessionStart most common)
  2. Worker status check: No PID file → worker not running
  3. Migration check: No marker file → run PM2 cleanup
  4. PM2 cleanup: pm2 delete claude-mem-worker (old worker terminated)
  5. Marker creation: ~/.claude-mem/.pm2-migrated with timestamp
  6. New worker start: Bun process spawned, PID/port files created
  7. Verification: Process check + HTTP health check
  8. Hook completes: Claude Code session starts normally
User Observable Behavior:
  • Slight delay on first startup (PM2 cleanup + new worker spawn)
  • No error messages (cleanup failures silently handled)
  • Worker appears running via npm run worker:status
  • Old PM2 worker no longer in pm2 list

Subsequent Sessions

After migration completes, every hook trigger follows the fast path:
  1. PID file exists? YES
  2. Process alive? YES
  3. HTTP health check? SUCCESS
  4. Result: Worker already running, done (~50ms)
No migration logic runs on subsequent sessions.

Platform-Specific Behavior

Platform Comparison

Platform Notes

  • POSIX signal handling works natively
  • Bun fully supported
  • No platform-specific workarounds needed

Observable Changes

Command Changes

File Location Changes

Logs:
PID Files:
Process State:
Database (unchanged):

User-Visible Changes

Before Update:
After Update:

Orphaned Files

After migration, these PM2 files may remain (safe to delete):
Cleanup (optional):

File System State

State Directory Structure

Before Migration (PM2 system):
After Migration (Bun system):

Edge Cases and Troubleshooting

Scenario 1: Migration Fails (PM2 Still Running)

This is rare but can happen if PM2 has watch mode enabled or the process is manually restarted.
Symptoms:
  • pm2 list still shows claude-mem-worker
  • Port conflict errors in logs
  • Worker fails to start
Resolution:

Scenario 2: Stale PID File (Process Dead)

Symptoms:
  • npm run worker:status shows “not running”
  • .worker.pid file exists
  • Process ID doesn’t exist
Automatic Recovery: Next hook trigger detects dead process and starts a fresh worker. Manual Resolution:

Scenario 3: Port Already in Use

Error: EADDRINUSE: address already in use Resolution:

Common Error Messages

Developer Notes

Testing the Migration

Architecture Decisions

Why Custom ProcessManager Instead of PM2?
  1. Simplicity: Direct control, no external daemon
  2. Dependencies: Remove npm dependency
  3. Cross-platform: Bun handles platform differences
  4. Bundle Size: Reduce plugin package size
  5. Control: Fine-grained error handling and validation
Why One-Time Marker Instead of Always Running PM2 Delete?
  1. Performance: Avoid unnecessary process spawning
  2. Idempotency: Migration runs exactly once
  3. Debugging: Timestamp shows when migration occurred
  4. Simplicity: Clear migration state
Why Run PM2 Cleanup on All Platforms?
  1. Quality Migration: Clean up orphaned processes
  2. Consistency: Same behavior across all platforms
  3. Safety: Error handling already in place (try/catch)
  4. No Downside: If PM2 not installed, error is caught and ignored

Summary

The migration from PM2 to Bun-based ProcessManager is a one-time, automatic, transparent transition that:
  1. Removes external dependencies (PM2, better-sqlite3)
  2. Simplifies architecture (direct process control)
  3. Improves cross-platform support (especially Windows)
  4. Preserves user data (database, settings, logs unchanged)
  5. Requires no user action (automatic on first hook trigger)
Key Migration Moment: First hook trigger after update to 7.1.0+ Duration: ~2-5 seconds (one-time delay) Impact: Seamless transition, user-invisible Rollback: Not needed (migration is forward-only, safe) For most users, the migration will be completely transparent - they’ll see no errors, no data loss, and experience improved reliability and simpler troubleshooting going forward.