Instatic Server Architecture Using Bun.serve and Worker Pools: A Technical Deep Dive

Instatic runs a single Bun.serve instance to handle all HTTP traffic and WebSocket connections, while isolating CPU-intensive operations and untrusted plugin code in a managed pool of Bun.Worker processes.

Instatic, developed by CoreBunch, is an open-source CMS that leverages the Bun runtime's native APIs to create a high-performance server architecture. The system combines Bun.serve for synchronous request handling with a sophisticated worker pool pattern using Bun.Worker, ensuring that heavy background tasks never block the main event loop.

Single-Process HTTP Entry Point with Bun.serve

The server initializes through a single call to Bun.serve in server/index.ts (line 69). This creates one unified HTTP server that handles all incoming traffic—admin UI, public site requests, CMS API endpoints, and WebSocket upgrades—within the same process.

// server/index.ts (line 69)
const server = Bun.serve({
  fetch: router.handle,          // route every request through the router
  port: Number(process.env.PORT) || 3000,
  // Upgrade HTTP → WebSocket for collab
  upgrade: collabSocketUpgrade,
});

By centralizing the entry point, Instatic eliminates the complexity of multi-process HTTP servers while maintaining the ability to offload work through background workers.

Centralized Request Routing

All requests pass through the handcrafted router defined in server/router.ts. This router matches incoming paths and dispatches them to specialized handlers without spawning additional processes.

// server/router.ts (excerpt)
export const router = {
  async handle(req: Request) {
    const url = new URL(req.url);
    // CMS API
    if (url.pathname.startsWith('/admin/api/cms')) {
      return cmsRouter.handle(req);
    }
    // Public site
    if (url.pathname.startsWith('/_instatic')) {
      return publicRouter.handle(req);
    }
    // Fallback 404
    return new Response('Not found', { status: 404 });
  },
};

The router delegates to three primary destinations: CMS API handlers (server/handlers/cms/*.ts), public site routes (server/publish/*.ts), and WebSocket upgrade logic (server/collab/socket.ts) for real-time collaborative editing.

Worker Pool Architecture for Background Processing

For CPU-intensive or isolated tasks, Instatic does not spawn processes directly from request handlers. Instead, it maintains a managed pool of Bun.Worker instances orchestrated by server/plugins/host/workerPool.ts.

Isolating Plugins with Bun.Worker

Each plugin or specialized task runs inside its own Bun.Worker, providing isolation for untrusted code and establishing clean crash-recovery boundaries. The system creates workers on-demand using the native Worker constructor:

// server/plugins/host/workerPool.ts (excerpt)
export async function getWorker(pluginId: string): Promise<Worker> {
  const existing = workers.get(pluginId);
  if (existing) return existing;

  // Spawn a new worker that loads the plugin entrypoint
  const w = new Worker(
    new URL('../pluginWorker.ts', import.meta.url).href,
    { type: 'module' }
  );
  workers.set(pluginId, w);
  return w;
}

Worker Lifecycle and State Management

The worker pool tracks active instances in a shared Map<string, Worker> defined in server/plugins/host/workerState.ts. This state management enables the pool to route API calls to existing workers, handle crashes, and automatically respawn failed processes without restarting the main server.

Type-Safe Communication Protocol

All host-to-worker communication uses a typed message protocol defined in server/plugins/protocol/*.ts. These TypeBox-validated schemas guarantee type-safe RPC calls between the main thread and worker contexts:

// server/plugins/pluginWorker.ts (excerpt)
self.onmessage = async (event) => {
  const { kind, payload } = event.data;
  if (kind === 'imageVariant') {
    const image = await generateVariant(payload);
    self.postMessage({ kind: 'response', result: image });
  }
};

Concurrency Control

To prevent the server from being overwhelmed by queued jobs, the worker pool integrates with server/util/mapWithConcurrency.ts. This utility limits the number of simultaneous operations per worker pool, ensuring that background processing remains predictable under load.

Real-World Implementation: Image Variant Generation

The image processing pipeline exemplifies the architecture's separation of concerns. When a client requests an image variant, the handler in server/handlers/cms/imageVariantWorkerHost.ts delegates the computation to a dedicated worker while immediately freeing the main thread to serve other requests.

// server/handlers/cms/imageVariantWorkerHost.ts (excerpt)
export async function handleImageVariant(req: Request) {
  const worker = await getWorker('image-variant');
  const job = { kind: 'imageVariant', payload: {/* … */} };
  const result = await workerCall(worker, job);   // defined in workerPool.ts
  return new Response(result.image, {
    headers: { 'Content-Type': result.mime },
  });
}

The actual processing occurs in server/handlers/cms/imageVariantWorker.ts, which runs in complete isolation from the main server's memory space.

Summary

  • Bun.serve acts as the single entry point for all HTTP and WebSocket traffic, initialized in server/index.ts.
  • The central router (server/router.ts) dispatches requests to CMS APIs, public site handlers, or WebSocket upgrades without process forking.
  • Bun.Worker instances provide isolation for plugins and CPU-intensive tasks, with each worker managed through the pool in server/plugins/host/workerPool.ts.
  • TypeBox-validated protocols (server/plugins/protocol/*.ts) ensure type-safe communication between the host and workers.
  • Concurrency limits (server/util/mapWithConcurrency.ts) prevent worker pools from exhausting server resources.
  • Crash resilience is built into the worker lifecycle, allowing automatic respawning without affecting the main Bun.serve process.

Frequently Asked Questions

How does Instatic handle CPU-intensive tasks without blocking the main server?

Instatic delegates heavy operations to Bun.Worker instances running in separate JavaScript workers. The main server posts messages to these workers via server/plugins/host/workerPool.ts and continues serving other requests asynchronously. This pattern ensures that image processing or plugin execution never blocks the Bun.serve event loop.

What communication protocol does Instatic use between the host and workers?

The system uses a typed message protocol defined in server/plugins/protocol/*.ts, implemented with TypeBox schemas. This guarantees that all RPC calls between the main thread and workers are type-safe and validated at runtime, preventing serialization errors and interface mismatches.

How does Instatic ensure crash resilience for plugin workers?

The worker pool in server/plugins/host/workerPool.ts tracks each worker instance in a Map<string, Worker> (managed by server/plugins/host/workerState.ts). When a worker crashes, the pool detects the failure and automatically respawns a new instance for that plugin ID, maintaining service availability without restarting the main HTTP server.

Can the worker pool handle multiple concurrent image processing jobs?

Yes, the worker pool integrates with server/util/mapWithConcurrency.ts to limit simultaneous operations. While multiple jobs can be queued, the concurrency controller ensures that only a defined number run in parallel, preventing memory exhaustion and maintaining predictable performance for the main Bun.serve instance.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →