Running Production Bun Microservices with Systemd and Caddy
Deploy high-throughput TypeScript microservices with the Bun runtime on Ubuntu 24.04, securing processes with systemd sandboxing and Caddy.
Netbay Infrastructure Team
Netbay Engineering
On this page
Bun has rapidly grown from an experimental JavaScript toolchain into a battle-tested, high-performance production runtime. Built on Apple's WebKit JavaScriptCore engine rather than Google's V8, Bun executes TypeScript natively without transpilation passes, starts up in milliseconds, and provides built-in HTTP server primitives capable of handling over 100,000 requests per second on a single multi-core instance.
Running Bun in production requires pairing it with systemd process supervision for automatic restart on crash, strict Linux namespace sandboxing, and Caddy as an automated TLS reverse proxy binding directly to Unix Domain Sockets.
Why Unix Domain Sockets Beat Localhost TCP
When reverse proxying web traffic from an edge proxy like Caddy or Nginx to an application service running on the same host, developers almost universally default to binding a local TCP port (e.g. 127.0.0.1:3000). While functional, this routing forces the Linux kernel to traverse the entire network stack:
- Memory allocations for socket buffers.
- TCP checksum calculations and handshake roundtrips.
- Ephemeral port exhaustion under heavy concurrent load (TIME_WAIT socket accumulation).
Unix Domain Sockets (UDS) bypass the network layer completely. Communication occurs via memory buffers managed directly inside the kernel Virtual File System (VFS). This slashes inter-process communication latency by 30-40% while eliminating port scanning and port conflict vectors.
Installing Bun on Ubuntu 24.04
Install the official Bun binary directly into a shared system path for access across system users:
# Install Bun globally
sudo apt-get update
sudo apt-get install -y unzip curl
curl -fsSL https://bun.sh/install | bash
# Move binary to system PATH
sudo mv ~/.bun/bin/bun /usr/local/bin/bun
sudo chmod +x /usr/local/bin/bun
# Verify version
bun --versionCreate a dedicated system user www-bun with no login shell to run the application process:
sudo useradd -r -s /usr/sbin/nologin -d /srv/bun-app www-bun
sudo mkdir -p /srv/bun-app
sudo chown -R www-bun:www-bun /srv/bun-appWriting a Native Bun Microservice
Bun includes a fast built-in HTTP server. Instead of binding to a TCP port, bind directly to a Unix Domain Socket to bypass Linux network stack overhead and eliminate port collision risks:
// /srv/bun-app/server.ts
import { serve } from "bun";
import { unlinkSync, existsSync } from "node:fs";
const socketPath = "/run/bun-app/server.sock";
// Clean up stale socket file if it exists
if (existsSync(socketPath)) {
unlinkSync(socketPath);
}
const server = serve({
unix: socketPath,
fetch(req) {
const url = new URL(req.url);
if (url.pathname === "/health") {
return new Response(JSON.stringify({ status: "healthy", uptime: process.uptime() }), {
headers: { "Content-Type": "application/json" },
});
}
if (url.pathname === "/api/compute") {
// Simulate fast CPU-bound JSON work
return Response.json({
engine: "Bun JavaScriptCore",
timestamp: Date.now(),
worker_id: process.pid,
});
}
return new Response("Not Found", { status: 404 });
},
});
console.log("Bun production microservice listening on unix:" + socketPath);Sandboxing with Systemd
Create a hardened systemd unit file at /etc/systemd/system/bun-app.service. Use systemd's RuntimeDirectory directive to automatically create the /run/bun-app directory with proper ownership:
# /etc/systemd/system/bun-app.service
[Unit]
Description=Bun Production Microservice
After=network.target
[Service]
Type=simple
User=www-bun
Group=www-bun
WorkingDirectory=/srv/bun-app
RuntimeDirectory=bun-app
RuntimeDirectoryMode=0755
ExecStart=/usr/local/bin/bun run server.ts
Restart=always
RestartSec=2s
LimitNOFILE=65535
# Linux process sandboxing controls
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
NoNewPrivileges=true
ProtectKernelTunables=true
ProtectControlGroups=true
[Install]
WantedBy=multi-user.targetReload systemd and start the Bun service:
sudo systemctl daemon-reload
sudo systemctl enable --now bun-app
sudo systemctl status bun-app
ls -la /run/bun-app/server.sockReverse Proxying with Caddy
Caddy provides automatic HTTPS with Let's Encrypt and native HTTP/3 support. Configure Caddy to proxy incoming requests to the Unix socket:
# /etc/caddy/Caddyfile
api.example.com {
reverse_proxy unix//run/bun-app/server.sock {
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
}
encode zstd gzip
log {
output file /var/log/caddy/bun_access.log
}
}Reload Caddy:
sudo systemctl reload caddyVerification and Benchmark Testing
Query the endpoint to verify socket communication and benchmark latency:
# Verify health endpoint over HTTPS
curl -I https://api.example.com/health
# Benchmark throughput directly against unix socket
curl --unix-socket /run/bun-app/server.sock http://localhost/api/computeBun combines rapid developer ergonomics with elite runtime performance for modern backend microservices.
Deploy Bun-powered microservices on Netbay Linux VPS in under 60 seconds with High-Speed SSD in Lucknow DC01 — netbayhosts.in.
Keep reading
Follow along on a real VPS
Deploy Linux in under 60 seconds
These guides are written against Ubuntu, Debian, and RHEL-family images — the same ones on NetBay.
Deploy an instance