Files
agent/.agent/skills/tech-stack/elysiajs/references/deployment.md

6.9 KiB

Deployment

Production Build

bun build \
  --compile \
  --minify-whitespace \
  --minify-syntax \
  --target bun \
  --outfile server \
  src/index.ts

Benefits:

  • No runtime needed on deployment server
  • Smaller memory footprint (2-3x reduction)
  • Faster startup
  • Single portable executable

Run the binary:

./server

Compile to JavaScript

bun build \
  --minify-whitespace \
  --minify-syntax \
  --outfile ./dist/index.js \
  src/index.ts

Run:

NODE_ENV=production bun ./dist/index.js

Docker

Basic Dockerfile

FROM oven/bun:1 AS build

WORKDIR /app

# Cache dependencies
COPY package.json bun.lock ./
RUN bun install

COPY ./src ./src

ENV NODE_ENV=production

RUN bun build \
  --compile \
  --minify-whitespace \
  --minify-syntax \
  --outfile server \
  src/index.ts

FROM gcr.io/distroless/base

WORKDIR /app

COPY --from=build /app/server server

ENV NODE_ENV=production

CMD ["./server"]

EXPOSE 3000

Build and Run

docker build -t my-elysia-app .
docker run -p 3000:3000 my-elysia-app

With Environment Variables

FROM gcr.io/distroless/base

WORKDIR /app

COPY --from=build /app/server server

ENV NODE_ENV=production
ENV PORT=3000
ENV DATABASE_URL=""
ENV JWT_SECRET=""

CMD ["./server"]

EXPOSE 3000

Cluster Mode (Multiple CPU Cores)

// src/index.ts
import cluster from 'node:cluster'
import os from 'node:os'
import process from 'node:process'

if (cluster.isPrimary) {
  for (let i = 0; i < os.availableParallelism(); i++) {
    cluster.fork()
  }
} else {
  await import('./server')
  console.log(`Worker ${process.pid} started`)
}
// src/server.ts
import { Elysia } from 'elysia'

new Elysia()
  .get('/', () => 'Hello World!')
  .listen(3000)

Environment Variables

.env File

NODE_ENV=production
PORT=3000
DATABASE_URL=postgresql://user:password@localhost:5432/db
JWT_SECRET=your-secret-key
CORS_ORIGIN=https://example.com

Load in App

import { Elysia } from 'elysia'

const app = new Elysia()
  .get('/env', () => ({
    env: process.env.NODE_ENV,
    port: process.env.PORT
  }))
  .listen(parseInt(process.env.PORT || '3000'))

Platform-Specific Deployments

Railway

// Railway assigns random PORT via env variable
new Elysia()
  .get('/', () => 'Hello Railway')
  .listen(process.env.PORT ?? 3000)

Vercel

// src/index.ts
import { Elysia } from 'elysia'

export default new Elysia()
  .get('/', () => 'Hello Vercel')

export const GET = app.fetch
export const POST = app.fetch
// vercel.json
{
  "$schema": "https://openapi.vercel.sh/vercel.json",
  "bunVersion": "1.x"
}

Cloudflare Workers

import { Elysia } from 'elysia'
import { CloudflareAdapter } from 'elysia/adapter/cloudflare-worker'

export default new Elysia({
  adapter: CloudflareAdapter
})
  .get('/', () => 'Hello Cloudflare!')
  .compile()
# wrangler.toml
name = "elysia-app"
main = "src/index.ts"
compatibility_date = "2025-06-01"

Node.js Adapter

import { Elysia } from 'elysia'
import { node } from '@elysiajs/node'

const app = new Elysia({ adapter: node() })
  .get('/', () => 'Hello Node.js')
  .listen(3000)

Performance Optimization

Enable AoT Compilation

new Elysia({
  aot: true  // Ahead-of-time compilation
})

Use Native Static Response

new Elysia({
  nativeStaticResponse: true
})
  .get('/version', 1)  // Optimized for Bun.serve.static

Precompile Routes

new Elysia({
  precompile: true  // Compile all routes ahead of time
})

Health Checks

new Elysia()
  .get('/health', () => ({
    status: 'ok',
    timestamp: Date.now()
  }))
  .get('/ready', ({ db }) => {
    // Check database connection
    const isDbReady = checkDbConnection()
    
    if (!isDbReady) {
      return status(503, { status: 'not ready' })
    }
    
    return { status: 'ready' }
  })

Graceful Shutdown

import { Elysia } from 'elysia'

const app = new Elysia()
  .get('/', () => 'Hello')
  .listen(3000)

process.on('SIGTERM', () => {
  console.log('SIGTERM received, shutting down gracefully')
  app.stop()
  process.exit(0)
})

process.on('SIGINT', () => {
  console.log('SIGINT received, shutting down gracefully')
  app.stop()
  process.exit(0)
})

Monitoring

OpenTelemetry

import { opentelemetry } from '@elysiajs/opentelemetry'

new Elysia()
  .use(opentelemetry({
    serviceName: 'my-service',
    endpoint: 'http://localhost:4318'
  }))

Custom Logging

.onRequest(({ request }) => {
  console.log(`[${new Date().toISOString()}] ${request.method} ${request.url}`)
})
.onAfterResponse(({ request, set }) => {
  console.log(`[${new Date().toISOString()}] ${request.method} ${request.url} - ${set.status}`)
})

SSL/TLS (HTTPS)

import { Elysia, file } from 'elysia'

new Elysia({
  serve: {
    tls: {
      cert: file('cert.pem'),
      key: file('key.pem')
    }
  }
})
  .get('/', () => 'Hello HTTPS')
  .listen(3000)

Best Practices

  1. Always compile to binary for production

    • Reduces memory usage
    • Smaller deployment size
    • No runtime needed
  2. Use environment variables

    • Never hardcode secrets
    • Use different configs per environment
  3. Enable health checks

    • Essential for load balancers
    • K8s/Docker orchestration
  4. Implement graceful shutdown

    • Handle SIGTERM/SIGINT
    • Close connections properly
  5. Use cluster mode

    • Utilize all CPU cores
    • Better performance under load
  6. Monitor your app

    • Use OpenTelemetry
    • Log requests/responses
    • Track errors

Example Production Setup

// src/server.ts
import { Elysia } from 'elysia'
import { cors } from '@elysiajs/cors'
import { opentelemetry } from '@elysiajs/opentelemetry'

export const app = new Elysia({
  aot: true,
  nativeStaticResponse: true
})
  .use(cors({
    origin: process.env.CORS_ORIGIN || 'http://localhost:3000'
  }))
  .use(opentelemetry({
    serviceName: 'my-service'
  }))
  .get('/health', () => ({ status: 'ok' }))
  .get('/', () => 'Hello Production')
  .listen(parseInt(process.env.PORT || '3000'))

// Graceful shutdown
process.on('SIGTERM', () => {
  app.stop()
  process.exit(0)
})
// src/index.ts (cluster)
import cluster from 'node:cluster'
import os from 'node:os'

if (cluster.isPrimary) {
  for (let i = 0; i < os.availableParallelism(); i++) {
    cluster.fork()
  }
} else {
  await import('./server')
}
# Dockerfile
FROM oven/bun:1 AS build

WORKDIR /app

COPY package.json bun.lock ./
RUN bun install

COPY ./src ./src

ENV NODE_ENV=production

RUN bun build --compile --outfile server src/index.ts

FROM gcr.io/distroless/base

WORKDIR /app

COPY --from=build /app/server server

ENV NODE_ENV=production

CMD ["./server"]

EXPOSE 3000