Shipping Elysia.js + Bun to Production: What the Docs Skip
Practical notes on running Elysia.js on Bun for production services: PM2, observability, and the error patterns you should watch out for.
Wafik Ulinnuha
Backend Developer
Bun delivers fast startup and impressive throughput. Elysia.js adds type ergonomics on par with Hono or Fastify. The combination is tempting. But like any new runtime, plenty of production details are left for you to figure out.
Process supervisor
Bun does not yet have a mature equivalent of Node's cluster module. I use two approaches depending on load:
- PM2 in
forkmode with multiple instances. Great for stateless services. - systemd unit with
Restart=alwaysfor critical services. Simpler, onebunbinary, and logs go straight to journald.
[Service]
Type=simple
User=app
ExecStart=/usr/local/bin/bun run /var/www/app/src/index.ts
Restart=always
RestartSec=2
LimitNOFILE=65536
Environment=NODE_ENV=production
The right reverse proxy
Always put Nginx in front. Not because Bun struggles with TLS, but so rate-limits, security headers, and keep-alive tuning live in one place.
location /api/ {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 60s;
}
Minimal observability
Without instrumentation, Bun's performance is hard to debug. I always wire up:
- Simple
/healthzand/readyzendpoints. - A
pino-based JSON logger, piped to Loki via Vector. - Handler duration histograms via
prom-client; Elysia'sonAfterHandlehook is perfect for this.
Error patterns to watch
A few things that bit me early:
- Top-level await stalls at boot — the service hangs until the orchestrator's timeout fires.
- Multipart body parsing on Bun was once incompatible with some clients; always test against a real Android client.
- Date timezones on Alpine Linux builds: install
tzdataif you rely on local timezones.
Do I regret moving to Bun?
No. p95 latency on Gatsu dropped ~32% versus the Node version, and container startup became sub-second. But I stay careful: for mission-critical services that do not need a new innovation cycle, Node.js LTS is still a defensible choice.
Further reading
Further reading
Tuning PgBouncer on a Modest VPS for Bursty Workloads
A mis-configured connection pool is the cheapest way to make a healthy database look slow. Here are the numbers I ended up shipping.
Designing Multi-Tenant SaaS on PostgreSQL Without Regret
Your multi-tenant strategy locks in operational cost, security posture, and migration pain for years. Here is an honest guide from building Gatsu.