Deploy to Production
This page is a guide on how to deploy Elysia to production.
Cluster mode
Elysia is a single‑threaded by default. To take advantage of a multi‑core CPU, we can run Elysia in cluster mode.
Create a src/index.ts that imports the main server from src/server.ts and forks a worker for each CPU core:
// 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);
Elysia on Bun uses SO_REUSEPORT by default, which allows multiple instances to listen on the same port. This only works on Linux.
Compile to binary
We recommend running a build command before deploying to production as it can reduce memory usage and file size significantly. Compile Elysia into a single binary with:
bun build \
--compile \
--minify-whitespace \
--minify-syntax \
--target bun \
--outfile server \
src/index.ts
This will generate a portable binary server that you can run without needing Bun installed on the target machine.
Target
You can specify a target platform:
bun build \
--compile \
--minify-whitespace \
--minify-syntax \
--target bun-linux-x64 \
--outfile server \
src/index.ts
| Target | Operating System | Architecture | Modern | Baseline | Libc |
|---|---|---|---|---|---|
| bun-linux-x64 | Linux | x64 | ✅ | ✅ | glibc |
| bun-linux-arm64 | Linux | arm64 | ✅ | N/A | glibc |
| bun-windows-x64 | Windows | x64 | ✅ | ✅ | – |
| bun-windows-arm64 | Windows | arm64 | ❌ | ❌ | – |
| bun-darwin-x64 | macOS | x64 | ✅ | ✅ | – |
| bun-darwin-arm64 | macOS | arm64 | ✅ | N/A | – |
| bun-linux-x64-musl | Linux | x64 | ✅ | ✅ | musl |
| bun-linux-arm64-musl | Linux | arm64 | ✅ | N/A | musl |
Why not --minify
Bun’s --minify flag also reduces function names to a single character, which hampers OpenTelemetry tracing. If you’re not using OpenTelemetry, you may use --minify instead:
bun build \
--compile \
--minify \
--outfile server \
src/index.ts
Permission
Some Linux distributions may refuse to execute the binary if it lacks executable permissions. Grant them with:
chmod +x ./server
./server
Unknown random Chinese error
If you see an error with random Chinese characters when running the binary, the machine likely lacks AVX2 support. Bun requires AVX2; there is no workaround.
Compile to JavaScript
If you cannot compile to a binary or are deploying to a Windows server, bundle your server to a JavaScript file instead:
bun build \
--minify-whitespace \
--minify-syntax \
--outfile ./dist/index.js \
src/index.ts
Run it in production mode:
NODE_ENV=production bun ./dist/index.js
Docker
We recommend compiling to a binary for Docker to reduce the image size. Example Distroless Dockerfile:
# Build stage
FROM oven/bun AS build
WORKDIR /app
# Cache packages
COPY package.json package.json
COPY bun.lock 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
# Final stage
FROM gcr.io/distroless/base
WORKDIR /app
COPY --from=build /app/server server
ENV NODE_ENV=production
CMD ["./server"]
EXPOSE 3000
OpenTelemetry
When using OpenTelemetry, it monkey‑patches node_modules/<library>. Exclude the instrumented library from bundling:
bun build \
--compile \
--external pg \
--outfile server \
src/index.ts
Keep the node_modules directory on the production server and install only production dependencies:
bun install --production
Example package.json:
{
"dependencies": {
"pg": "^8.15.6"
},
"devDependencies": {
"@elysiajs/opentelemetry": "^1.2.0",
"@opentelemetry/instrumentation-pg": "^0.52.0",
"@types/pg": "^8.11.14",
"elysia": "^1.2.25"
}
}
Monorepo
For a Turborepo‑style monorepo, place a Dockerfile under the application folder (e.g. apps/server/Dockerfile). Example structure:
apps
├─ server
│ └─ Dockerfile
packages
└─ config
Docker command:
docker build -t elysia-mono .
Dockerfile:
FROM oven/bun:1 AS build
WORKDIR /app
# Cache packages
COPY package.json package.json
COPY bun.lock bun.lock
COPY /apps/server/package.json ./apps/server/package.json
COPY /packages/config/package.json ./packages/config/package.json
RUN bun install
RUN bun build \
--compile \
--minify-whitespace \
--minify-syntax \
--outfile server \
src/index.ts
# Final stage
FROM gcr.io/distroless/base
WORKDIR /app
COPY --from=build /app/server server
ENV NODE_ENV=production
CMD ["./server"]
EXPOSE 3000