
Table of Contents
By Khimananda Oli | Last reviewed: August 2026
Achieving zero-downtime deployment for Bun requires more than just restarting the process; without specific OS-level integration, active requests will drop during every release. While Bun is incredibly fast, its single-binary nature means standard restarts sever existing TCP connections instantly. To solve this, you must decouple socket ownership from the application process using systemd socket activation or a load-balanced rolling update strategy. This guide covers the exact configuration needed to make your Bun deployments truly seamless on Linux servers.
How does systemd socket activation enable zero-downtime deployment for Bun?
Systemd socket activation is the most robust method for achieving zero-downtime deployment for Bun on a single server. In a traditional setup, when you issue systemctl restart bun-app, systemd sends a SIGTERM to the old process, which closes the listening socket before the new process can bind to it. Even with graceful shutdown logic, there is a race condition where new connections are refused because no process is holding the port.
Socket activation solves this by having systemd itself hold the listening socket. When traffic arrives, systemd passes the open file descriptor to the Bun process. During a restart, systemd keeps the socket open and accepting connections in a kernel backlog while the new Bun instance starts up. Once the new process signals readiness, systemd hands off the queued connections. This eliminates the "connection refused" window entirely.
Configuring the socket unit
Create a dedicated socket unit file at /etc/systemd/system/bun-app.socket. This unit defines the port and buffering behavior independent of your application code.
[Unit]
Description=Bun App Socket
[Socket]
ListenStream=0.0.0.0:3000
Accept=no
TriggerLimitIntervalSec=10s
TriggerLimitBurst=200
[Install]
WantedBy=sockets.target The Accept=no directive is critical. It tells systemd to pass the listening socket itself rather than spawning a new process per connection. This matches how Bun expects to manage its own HTTP server internally. The trigger limits prevent connection storms during recovery scenarios.
Adapting the service unit for socket inheritance
Your service unit at /etc/systemd/system/bun-app.service must reference the socket and avoid binding to a hardcoded port in the application code. Bun automatically detects the inherited file descriptor when started via systemd.
[Unit]
Description=Bun Application Service
Requires=bun-app.socket
After=bun-app.socket
[Service]
ExecStart=/opt/bun/bin/bun run /var/www/app/server.ts
Restart=on-failure
RestartSec=5
User=www-data
Group=www-data
Environment=NODE_ENV=production
NotifyAccess=all
Type=notify
[Install]
WantedBy=multi-user.target Note the Type=notify setting. For true zero-downtime behavior, your Bun application should call sd_notify("READY=1") once initialization completes. If your app doesn't support sd_notify, use Type=simple but accept that systemd may forward traffic slightly before the app is fully ready. For production workloads handling sensitive transactions, implementing the notification protocol is non-negotiable.
How do you configure Nginx as a load balancer for Bun rolling updates?
While socket activation handles single-instance deployments, teams running high-traffic applications often prefer a rolling update strategy behind Nginx. This approach aligns with patterns discussed in blue-green and canary deployment strategies, adapted for bare-metal or VPS environments. The core principle is maintaining at least one healthy upstream while others recycle.
Nginx acts as the buffer between clients and your Bun instances. By configuring multiple upstream servers on different ports (or Unix sockets), you can restart Bun processes sequentially without ever removing all backends from the pool. This is particularly valuable when deploying database migrations alongside code changes, as covered in zero-downtime migration guides.
Nginx upstream configuration with health checks
Configure your Nginx upstream block to include retry logic and failure detection. This ensures that if a Bun instance fails mid-request during deployment, Nginx transparently retries on another backend.
upstream bun_cluster {
server unix:/run/bun/app-1.sock max_fails=2 fail_timeout=10s;
server unix:/run/bun/app-2.sock max_fails=2 fail_timeout=10s;
server unix:/run/bun/app-3.sock max_fails=2 fail_timeout=10s backup;
keepalive 32;
}
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://bun_cluster;
proxy_next_upstream error timeout http_502 http_503;
proxy_next_upstream_tries 2;
proxy_connect_timeout 5s;
# Critical for WebSocket support in Bun
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
} Using Unix sockets instead of TCP ports reduces overhead and avoids port exhaustion during rapid restarts. The keepalive directive maintains persistent connections to Bun, reducing handshake latency significantly under load.
What are the common pitfalls when implementing graceful shutdown in Bun?
Even with perfect infrastructure, your application code can sabotage zero-downtime deployment for Bun if it doesn't handle termination signals correctly. A common mistake is assuming Bun's default signal handling is sufficient for production. While Bun does trap SIGTERM, complex applications with database pools, message queue consumers, or long-running SSE streams need explicit cleanup logic.
Another frequent issue is mismatched timeouts. If your Nginx proxy_read_timeout is 60 seconds but your systemd TimeoutStopSec is 30 seconds, systemd will SIGKILL the process while Nginx still expects it to be draining connections. Always ensure your infrastructure timeouts form a coherent chain: app drain time < systemd stop timeout < proxy timeout. Observability plays a key role here; refer to the four golden signals to validate that your deployment pipeline isn't silently dropping requests.
Implementing proper signal handling
Add explicit shutdown hooks to your Bun server entry point. This ensures in-flight requests complete and resources release cleanly before the process exits.
const server = Bun.serve({
port: process.env.PORT || 3000,
fetch(req) {
return new Response("Hello World");
},
});
const shutdown = async (signal: string) => {
console.log(`Received ${signal}. Starting graceful shutdown...`);
// Stop accepting new connections
server.stop(true); // true = wait for in-flight requests
// Close database pools, Redis clients, etc.
await db.end();
await redis.quit();
console.log("Graceful shutdown complete.");
process.exit(0);
};
process.on("SIGTERM", () => shutdown("SIGTERM"));
process.on("SIGINT", () => shutdown("SIGINT")); The server.stop(true) call is essential. Passing true tells Bun to wait for existing requests to finish rather than aborting them immediately. Without this parameter, even socket activation won't save your users from partial responses.
How do socket activation and rolling updates compare for Bun deployments?
Choosing between systemd socket activation and Nginx-based rolling updates depends on your operational complexity tolerance and traffic profile. Both achieve zero-downtime deployment for Bun, but they optimize for different constraints.
| Criteria | Systemd Socket Activation | Nginx Rolling Updates |
|---|---|---|
| Complexity | Low (single unit config) | Medium (multiple units + LB) |
| Resource Overhead | Minimal (one process) | Higher (N+1 processes) |
| Deployment Speed | Instant handoff | Sequential drain cycles |
| Fault Isolation | None (single point) | High (independent instances) |
| Best For | APIs, internal tools, low-memory VPS | High-traffic public apps, critical SLAs |
| Horizontal Scaling | Requires external LB anyway | Built-in distribution |
For most Nepal-based startups and SMEs operating on budget VPS instances, socket activation provides the best balance of reliability and resource efficiency. Reserve rolling updates for applications where a single failed deployment would cause significant business impact or where you're already running multiple instances for performance reasons.
How do you verify zero-downtime behavior during Bun deployments?
Configuration alone doesn't guarantee success; you must validate that your deployment actually drops zero connections. Many engineers configure socket activation or rolling updates and assume it works until a customer reports an error weeks later. Verification should be part of your CI/CD pipeline, not an afterthought.
Use a continuous request generator like hey or wrk during deployment. Run the load test in a separate terminal while triggering your deployment script. Any non-2xx response or connection reset indicates a gap in your zero-downtime setup. For production validation, integrate deployment metrics into your observability stack as described in Prometheus monitoring fundamentals. Track bun_requests_total with status code labels and alert on any spike in 502/503 errors correlated with deployment timestamps.
Automated verification script
Incorporate this check into your deployment automation. The script below hammers the endpoint throughout the deploy and fails the pipeline if any request drops.
#!/bin/bash
# Run in background during deploy
hey -n 10000 -c 50 -q 100 https://api.example.com/health > /tmp/loadtest.log &
LOAD_PID=$!
# Trigger deployment
systemctl reload-or-restart bun-app
# Wait for load test to complete
wait $LOAD_PID
# Check for failures
FAILURES=$(grep -c "Error\|502\|503\|connection reset" /tmp/loadtest.log)
if [ "$FAILURES" -gt 0 ]; then
echo "DEPLOYMENT FAILED: $FAILURES dropped requests detected"
exit 1
fi
echo "Zero-downtime verified successfully" Next Steps for Production-Ready Bun Deployments
Implementing zero-downtime deployment for Bun transforms your release process from a source of anxiety into a routine operation. Start with systemd socket activation for single-instance deployments—it's simpler, uses fewer resources, and covers the majority of use cases for teams in Nepal and globally. Graduate to Nginx rolling updates only when your traffic volume or availability requirements demand horizontal scaling. Regardless of the method chosen, always pair infrastructure changes with proper application-level signal handling and automated verification. If you need help auditing your current Bun deployment pipeline or designing a compliant infrastructure for SOC 2 or ISO 27001, reach out to discuss your architecture.