Skip to Content

Last Updated: 3/9/2026


Bun

Bun  is a fast all-in-one JavaScript runtime designed for speed. It includes a bundler, test runner, and package manager, and is significantly faster than Node.js for many workloads.

Hono works natively on Bun with excellent performance and zero configuration.

Requirements

Install Bun by following the official installation guide .

Quick Start

Create a new Hono app for Bun:

bun create hono@latest my-app

Select bun when prompted.

Setup

1. Create New Project

bun create hono@latest my-app cd my-app bun install

2. Add to Existing Project

On an existing Bun project:

bun add hono

Update package.json:

{ "scripts": { "dev": "bun run --hot src/index.ts" } }

3. Write Your Application

Create src/index.ts:

import { Hono } from 'hono' const app = new Hono() app.get('/', (c) => { return c.text('Hello Bun!') }) app.get('/api/hello', (c) => { return c.json({ message: 'Hello from Bun', version: Bun.version, }) }) export default app

4. Run Locally

Start the development server:

bun run dev

Access http://localhost:3000 in your browser.

Configuration

Change Port Number

Export a config object with port and fetch:

import { Hono } from 'hono' const app = new Hono() app.get('/', (c) => c.text('Hello Bun!')) export default { port: 8080, fetch: app.fetch, } console.log('Server running on http://localhost:8080')

Server Options

Configure additional Bun server options:

export default { port: 3000, fetch: app.fetch, hostname: '0.0.0.0', // Listen on all interfaces development: process.env.NODE_ENV !== 'production', }

Serving Static Files

Use serveStatic from hono/bun:

Directory Structure

. ├── favicon.ico ├── src │ └── index.ts └── static ├── hello.txt ├── image.png └── demo └── index.html

Serve Static Files

import { Hono } from 'hono' import { serveStatic } from 'hono/bun' const app = new Hono() // Serve files from ./static at /static/* app.use('/static/*', serveStatic({ root: './' })) // Serve favicon app.use('/favicon.ico', serveStatic({ path: './favicon.ico' })) // Serve all files from ./static at root app.use('*', serveStatic({ root: './static' })) app.get('/api/hello', (c) => c.text('API works!')) export default app

Access files:

  • http://localhost:3000/static/hello.txt
  • http://localhost:3000/static/image.png
  • http://localhost:3000/favicon.ico

Rewrite Request Paths

Map URLs to different directories:

app.use( '/static/*', serveStatic({ root: './', rewriteRequestPath: (path) => path.replace(/^\/static/, '/public'), }) )

Custom MIME Types

app.use( '/static/*', serveStatic({ mimes: { m3u8: 'application/vnd.apple.mpegurl', ts: 'video/mp2t', }, }) )

onFound Callback

Run logic when a file is found:

app.use( '/static/*', serveStatic({ onFound: (_path, c) => { c.header('Cache-Control', 'public, immutable, max-age=31536000') }, }) )

onNotFound Callback

Handle missing files:

app.use( '/static/*', serveStatic({ onNotFound: (path, c) => { console.log(`${path} not found, requested: ${c.req.path}`) }, }) )

Precompressed Files

Serve .br or .gz files based on Accept-Encoding:

app.use( '/static/*', serveStatic({ precompressed: true, }) )

Testing

Bun has a built-in test runner:

Write Tests

Create index.test.ts:

import { describe, expect, it } from 'bun:test' import app from './index' describe('Hono app', () => { it('returns 200 OK', async () => { const req = new Request('http://localhost/') const res = await app.fetch(req) expect(res.status).toBe(200) expect(await res.text()).toBe('Hello Bun!') }) it('returns JSON from API', async () => { const req = new Request('http://localhost/api/hello') const res = await app.fetch(req) const data = await res.json() expect(res.status).toBe(200) expect(data.message).toBe('Hello from Bun') }) })

Run Tests

bun test

With watch mode:

bun test --watch

Environment Variables

Bun automatically loads .env files:

Create .env

PORT=3000 API_URL=https://api.example.com DATABASE_URL=postgresql://localhost/mydb

Access in Code

const app = new Hono() app.get('/config', (c) => { return c.json({ port: process.env.PORT || 3000, apiUrl: process.env.API_URL, environment: process.env.NODE_ENV || 'development', }) }) export default { port: parseInt(process.env.PORT || '3000'), fetch: app.fetch, }

Building for Production

Build the Application

Bun can bundle your application:

bun build ./src/index.ts --outdir ./dist --target bun

Add to package.json:

{ "scripts": { "build": "bun build ./src/index.ts --outdir ./dist --target bun", "start": "bun run dist/index.js" } }

Run Production Build

bun run build bun run start

Docker Deployment

Dockerfile

# Use Bun image FROM oven/bun:1 AS base WORKDIR /app # Install dependencies FROM base AS install COPY package.json bun.lockb ./ RUN bun install --frozen-lockfile --production # Copy source and build FROM base AS build COPY --from=install /app/node_modules node_modules COPY . . # Run the app FROM base AS release COPY --from=install /app/node_modules node_modules COPY --from=build /app/src ./src COPY --from=build /app/package.json . USER bun EXPOSE 3000 ENTRYPOINT ["bun", "run", "src/index.ts"]

Build and Run

# Build image docker build -t my-hono-app . # Run container docker run -p 3000:3000 my-hono-app

Docker Compose

docker-compose.yml:

version: '3.8' services: app: build: . ports: - "3000:3000" environment: - NODE_ENV=production - PORT=3000 restart: unless-stopped

Run:

docker-compose up -d

Performance Tips

Use Bun.serve for Maximum Performance

For the absolute best performance, use Bun.serve directly:

import { Hono } from 'hono' const app = new Hono() app.get('/', (c) => c.text('Ultra fast!')) Bun.serve({ port: 3000, fetch: app.fetch, })

Enable HTTP/2

Bun.serve({ port: 3000, fetch: app.fetch, tls: { key: Bun.file('./key.pem'), cert: Bun.file('./cert.pem'), }, })

Use Bun APIs

Leverage Bun-specific APIs for better performance:

// Fast file reading const file = Bun.file('./data.json') const data = await file.json() // Fast hashing const hash = Bun.hash('some data') // Fast password hashing const hashed = await Bun.password.hash('password')

Deployment Platforms

Self-Hosted

  • VPS: DigitalOcean, Linode, Hetzner
  • Containers: Docker, Kubernetes
  • Process Managers: PM2, systemd

Platform-as-a-Service

  • Railway - Zero-config Bun support
  • Fly.io - Global application platform
  • Render - Easy deployment

Best Practices

Use Environment Variables

// ❌ Don't const dbUrl = 'postgresql://user:pass@localhost/db' // ✅ Do const dbUrl = process.env.DATABASE_URL

Handle Errors

app.onError((err, c) => { console.error('Error:', err) return c.json({ error: 'Internal Server Error' }, 500) })

Use Compression

import { compress } from 'hono/compress' app.use('*', compress())

Set Security Headers

import { secureHeaders } from 'hono/secure-headers' app.use('*', secureHeaders())

What’s Next