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-appSelect bun when prompted.
Setup
1. Create New Project
bun create hono@latest my-app
cd my-app
bun install2. Add to Existing Project
On an existing Bun project:
bun add honoUpdate 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 app4. Run Locally
Start the development server:
bun run devAccess 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.htmlServe 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 appAccess files:
http://localhost:3000/static/hello.txthttp://localhost:3000/static/image.pnghttp://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 testWith watch mode:
bun test --watchEnvironment Variables
Bun automatically loads .env files:
Create .env
PORT=3000
API_URL=https://api.example.com
DATABASE_URL=postgresql://localhost/mydbAccess 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 bunAdd 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 startDocker 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-appDocker Compose
docker-compose.yml:
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- PORT=3000
restart: unless-stoppedRun:
docker-compose up -dPerformance 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_URLHandle 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
- Bun Documentation - Official Bun docs
- Node.js - Alternative runtime
- Best Practices - Hono app patterns