# How Instatic Uses Bun as Its JavaScript Runtime: Core Architecture Explained

> Discover how Instatic utilizes Bun as its JavaScript runtime, leveraging native APIs for HTTP requests, asset streaming, and plugin isolation without Node.js dependencies. Explore the core architecture.

- Repository: [CoreBunch/Instatic](https://github.com/CoreBunch/Instatic)
- Tags: internals
- Published: 2026-07-02

---

**TLDR:** Instatic leverages Bun's native APIs—including `Bun.serve`, `Bun.file`, and `Bun.Worker`—to handle HTTP requests, stream static assets, and isolate plugin processes without any Node.js dependencies or polyfills.

The **CoreBunch/Instatic** repository is a self-hosted CMS built exclusively on Bun, a fast JavaScript runtime that replaces Node.js for both development and production. By utilizing Bun's built-in APIs for server management, file I/O, compression, and process spawning, Instatic achieves a single-binary deployment model that serves both the CMS API and React admin interface with minimal overhead.

## HTTP Server Architecture with Bun.serve

At the heart of Instatic's runtime is [`server/index.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/index.ts), which initializes the application using **`Bun.serve`**. This native API replaces traditional Node.js HTTP servers like Express or Fastify, handling every incoming request—including CORS pre-flight and error handling—within a single process.

The server configuration sets `idleTimeout: 0` specifically to support long-running AI streams without disconnections. The `fetch` callback stamps client IPs using `server.requestIP(req)` for rate-limiting logic, applies security headers, and routes requests through `handleServerRequest`.

```typescript
// server/index.ts – starting the Bun server
import { createDbClient } from './db';
import { runMigrations } from './db/runMigrations';
import { handleServerRequest } from './router';
import { applySecurityHeaders } from './securityHeaders';
import { configurePublicOrigins, stampSocketIp } from './auth/security';

const config = readServerConfig();
configurePublicOrigins(config.publicOrigins);

const { db, migrations } = createDbClient(config.databaseUrl);
await runMigrations(db, migrations);
await syncSystemRoles(db);

Bun.serve({
  port: config.port,
  idleTimeout: 0,                     // no timeout for long‑running AI streams
  async fetch(req, server) {
    // CORS handling
    const cors = corsHeaders(req.headers.get('origin'));
    // IP stamping for rate‑limit logic
    stampSocketIp(req, server.requestIP(req)?.address ?? null);

    try {
      const res = await handleServerRequest(req, { db, staticDir: config.staticDir });
      for (const [k, v] of Object.entries(cors)) res.headers.set(k, v);
      return applySecurityHeaders(res, new URL(req.url).pathname);
    } catch (e) {
      console.error('[server] Unhandled request error:', e);
      return applySecurityHeaders(
        new Response(JSON.stringify({ error: 'Internal server error' }), {
          status: 500,
          headers: { 'Content-Type': 'application/json', ...cors },
        }),
        new URL(req.url).pathname,
      );
    }
  },
});

console.log(`[server] Listening on http://localhost:${config.port}`);

```

### Request Routing and Security Middleware

Before requests reach the router, `stampSocketIp` extracts the client address from Bun's native socket information, while `applySecurityHeaders` injects security policies into the response. This chain runs entirely within Bun's runtime, avoiding external middleware dependencies.

## Static File Handling and Compression

Instatic serves static assets—CSS, JavaScript, images, and the admin HTML shell—using **`Bun.file`** in [`server/static.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/static.ts). This API provides a `File` object that can be streamed directly or read into memory via `await file.arrayBuffer()`, eliminating the need for separate static-file server middleware.

### Optimized Delivery with Native Compression

Text responses and static assets undergo compression using **`Bun.gzipSync`** and **Brotli** (`brotliCompressSync`). The `serveStaticFile` function checks the `Accept-Encoding` header and returns compressed payloads with proper `Content-Encoding`, `Content-Type`, and `Cache-Control` headers.

```typescript
// server/static.ts – serving a static file with compression
import { resolve } from 'node:path';
import { BrotliCompressSync, constants as zlibConstants } from 'node:zlib';

export async function serveStaticFile(
  staticDir: string,
  pathname: string,
  req?: Request,
): Promise<Response | null> {
  const filePath = resolveStaticPath(staticDir, pathname);
  if (!filePath) return null;

  const file = Bun.file(filePath);
  if (!(await file.exists())) return null;

  const bytes = new Uint8Array(await file.arrayBuffer());
  const acceptEncoding = req?.headers.get('accept-encoding') ?? null;
  const encoding = selectEncoding(acceptEncoding);

  if (encoding === 'br') {
    const compressed = BrotliCompressSync(bytes, {
      params: { [zlibConstants.BROTLI_PARAM_QUALITY]: 5 },
    });
    return new Response(new Uint8Array(compressed), {
      headers: { 'content-encoding': 'br', 'content-type': contentType(filePath) },
    });
  }

  return new Response(bytes, { headers: { 'content-type': contentType(filePath) } });
}

```

## Worker Isolation for CPU-Intensive Tasks

To preserve the main server's responsiveness, Instatic offloads CPU-heavy operations—such as image-variant processing and plugin sandboxing—to isolated **`Bun.Worker`** instances. The [`server/plugins/host/workerPool.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/plugins/host/workerPool.ts) module manages these worker lifecycles, spawning QuickJS WASM sandboxes with limited permissions.

```typescript
// server/plugins/host/workerPool.ts – launching a sandboxed plugin worker
import { Worker } from 'bun';

export function ensureWorkerFor(pluginId: string, entrypoint: string): Worker {
  const worker = new Worker(entrypoint, {
    // Workers run in a QuickJS WASM sandbox with limited permissions
    env: { PLUGIN_ID: pluginId },
    stdio: ['pipe', 'pipe', 'pipe'],
  });
  // Attach message handlers, timeouts, etc.
  return worker;
}

```

## Development Workflow and Process Orchestration

Local development relies on helper scripts that use **`Bun.spawn`** to manage subprocesses. The [`scripts/start.ts`](https://github.com/CoreBunch/Instatic/blob/main/scripts/start.ts) file launches the server process, while [`scripts/dev.ts`](https://github.com/CoreBunch/Instatic/blob/main/scripts/dev.ts) orchestrates Docker checks and hot-reload functionality. This approach eliminates the need for external process managers like PM2 or Nodemon during development.

## Database Connectivity with Bun.SQL

The database client abstraction in [`server/db/client.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/db/client.ts) utilizes **`Bun.SQL`** to connect to both PostgreSQL and SQLite backends. This native database driver integrates seamlessly with the request lifecycle, providing async query capabilities without blocking the main thread.

## Summary

- **Bun.serve** in [`server/index.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/index.ts) handles all HTTP traffic through a single `fetch` handler with configurable `idleTimeout` for long-running streams.
- **Bun.file** in [`server/static.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/static.ts) streams static assets directly, with responses compressed using native **Bun.gzipSync** and Brotli implementations.
- **Bun.Worker** isolates CPU-intensive plugin operations via [`server/plugins/host/workerPool.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/plugins/host/workerPool.ts), preventing event loop blocking.
- **Bun.spawn** in [`scripts/start.ts`](https://github.com/CoreBunch/Instatic/blob/main/scripts/start.ts) and [`scripts/dev.ts`](https://github.com/CoreBunch/Instatic/blob/main/scripts/dev.ts) manages development subprocesses and hot-reload orchestration.
- **Bun.SQL** in [`server/db/client.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/db/client.ts) provides native database connectivity without external driver dependencies.

## Frequently Asked Questions

### Does Instatic require Node.js for any part of its runtime?

No. Instatic is built exclusively for the Bun JavaScript runtime. According to the CoreBunch/Instatic source code, the application uses Bun-native APIs like `Bun.serve`, `Bun.file`, and `Bun.Worker` throughout the codebase, eliminating the need for Node.js polyfills or compatibility layers.

### How does Instatic handle static file serving without Express or Fastify?

Instatic serves static files directly through `Bun.file` as implemented in [`server/static.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/static.ts). This API returns a `File` object that can be streamed to the client or read into memory via `await file.arrayBuffer()`, eliminating the need for external static-file middleware while supporting automatic compression.

### What is the purpose of Bun.Worker in Instatic?

`Bun.Worker` isolates CPU-intensive tasks such as image-variant processing and plugin sandboxing. The [`server/plugins/host/workerPool.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/plugins/host/workerPool.ts) module manages these workers to prevent long-running operations from blocking the main HTTP server's event loop, utilizing QuickJS WASM sandboxes for security.

### How does the development server handle hot reloading?

The [`scripts/dev.ts`](https://github.com/CoreBunch/Instatic/blob/main/scripts/dev.ts) file uses `Bun.spawn` to orchestrate subprocesses, enabling hot reload functionality during local development. This allows the server to restart automatically when code changes are detected without requiring external process managers or file watchers.