Install and configure Bun JavaScript runtime with systemd and reverse proxy

Beginner 25 min Apr 17, 2026 11 views
Ubuntu 24.04 Ubuntu 22.04 Debian 12 AlmaLinux 9 Rocky Linux 9 Fedora 41

Deploy Bun JavaScript runtime on Linux with automatic service management through systemd and secure SSL reverse proxy configuration with Nginx for production applications.

Prerequisites

  • Root or sudo access
  • Domain name with DNS configured
  • Basic familiarity with command line

What this solves

Bun is a fast JavaScript runtime and package manager that offers better performance than Node.js for many applications. This tutorial shows you how to install Bun on Linux, create a production-ready web application, configure it as a systemd service for automatic startup and management, and set up Nginx as a reverse proxy with SSL termination for secure access.

Step-by-step installation

Update system packages

Start by updating your package manager to ensure you have the latest security patches and package definitions.

sudo apt update && sudo apt upgrade -y
sudo dnf update -y

Install required dependencies

Install curl and unzip which are needed for the Bun installation script and SSL certificate management.

sudo apt install -y curl unzip nginx certbot python3-certbot-nginx
sudo dnf install -y curl unzip nginx certbot python3-certbot-nginx

Create application user

Create a dedicated user for running the Bun application. This follows security best practices by avoiding running applications as root.

sudo useradd --system --create-home --shell /bin/bash bunapp

Install Bun runtime

Switch to the bunapp user and install Bun using the official installation script. This downloads and installs the latest stable version.

sudo -u bunapp bash -c 'curl -fsSL https://bun.sh/install | bash'
sudo -u bunapp bash -c 'echo "export PATH=\$PATH:/home/bunapp/.bun/bin" >> /home/bunapp/.bashrc'

Create sample web application

Create a directory for the application and build a simple web server to test the installation.

sudo -u bunapp mkdir -p /home/bunapp/webapp
sudo -u bunapp tee /home/bunapp/webapp/server.js > /dev/null << 'EOF'
const server = Bun.serve({
  port: 3000,
  hostname: '127.0.0.1',
  fetch(request) {
    const url = new URL(request.url);
    
    if (url.pathname === '/') {
      return new Response(`
        
        
        Bun Server
        
          

Bun JavaScript Runtime

Server is running on port 3000

Bun version: ${Bun.version}

Current time: ${new Date().toISOString()}

`, { headers: { 'Content-Type': 'text/html' } }); } if (url.pathname === '/api/health') { return Response.json({ status: 'ok', version: Bun.version, timestamp: new Date().toISOString() }); } return new Response('Not Found', { status: 404 }); }, }); console.log(Server running at http://${server.hostname}:${server.port}); EOF

Create package.json file

Add a package.json file to define the application metadata and scripts for easier management.

sudo -u bunapp tee /home/bunapp/webapp/package.json > /dev/null << 'EOF'
{
  "name": "bun-webapp",
  "version": "1.0.0",
  "description": "Sample Bun web application",
  "main": "server.js",
  "scripts": {
    "start": "bun run server.js",
    "dev": "bun --watch server.js"
  },
  "dependencies": {}
}
EOF

Test the application manually

Verify that Bun can run the application correctly before creating the systemd service.

sudo -u bunapp bash -c 'cd /home/bunapp/webapp && /home/bunapp/.bun/bin/bun run server.js &'
sleep 2
curl http://127.0.0.1:3000/api/health
pkill -f "bun run server.js"

Configure systemd service

Create systemd service file

Create a systemd unit file that will manage the Bun application lifecycle, including automatic restarts and proper logging.

sudo tee /etc/systemd/system/bunapp.service > /dev/null << 'EOF'
[Unit]
Description=Bun Web Application
After=network.target
Requires=network.target

[Service]
Type=simple
User=bunapp
Group=bunapp
WorkingDirectory=/home/bunapp/webapp
Environment=NODE_ENV=production
Environment=PORT=3000
Environment=PATH=/home/bunapp/.bun/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
ExecStart=/home/bunapp/.bun/bin/bun run server.js
Restart=always
RestartSec=3
StandardOutput=journal
StandardError=journal
SyslogIdentifier=bunapp

Security settings

NoNewPrivileges=true PrivateTmp=true ProtectSystem=strict ProtectHome=true ReadWritePaths=/home/bunapp/webapp

Resource limits

LimitNOFILE=65536 LimitNPROC=4096 [Install] WantedBy=multi-user.target EOF

Enable and start the service

Reload systemd to recognize the new service, enable it for automatic startup, and start it immediately.

sudo systemctl daemon-reload
sudo systemctl enable bunapp
sudo systemctl start bunapp

Verify service status

Check that the service started successfully and is listening on the correct port.

sudo systemctl status bunapp
curl http://127.0.0.1:3000/api/health

Set up Nginx reverse proxy with SSL

Configure Nginx virtual host

Create an Nginx configuration that proxies requests to the Bun application with proper headers and security settings.

sudo tee /etc/nginx/sites-available/bunapp > /dev/null << 'EOF'
server {
    listen 80;
    server_name example.com www.example.com;
    
    # Security headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    
    # Proxy to Bun application
    location / {
        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 Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
        proxy_read_timeout 86400;
        proxy_redirect off;
    }
    
    # Health check endpoint
    location /api/health {
        proxy_pass http://127.0.0.1:3000/api/health;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        access_log off;
    }
    
    # Logging
    access_log /var/log/nginx/bunapp_access.log;
    error_log /var/log/nginx/bunapp_error.log;
}
EOF

Enable the site and test configuration

Enable the Nginx site and verify the configuration syntax before reloading.

sudo ln -s /etc/nginx/sites-available/bunapp /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Configure SSL with Let's Encrypt

Obtain and configure SSL certificates using Certbot. Replace example.com with your actual domain name.

Note: Make sure your domain's DNS A record points to your server's IP address before running this command.
sudo certbot --nginx -d example.com -d www.example.com

Configure automatic SSL renewal

Set up a cron job to automatically renew SSL certificates before they expire.

sudo crontab -e

Add this line to renew certificates twice daily:

0 12,0   * /usr/bin/certbot renew --quiet

Configure firewall rules

Allow HTTP and HTTPS traffic

Configure the firewall to allow web traffic while keeping the Bun application port (3000) internal only.

sudo ufw allow 'Nginx Full'
sudo ufw --force enable
sudo ufw status
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
sudo firewall-cmd --list-services

Verify your setup

Test all components to ensure they're working correctly together.

# Check Bun application status
sudo systemctl status bunapp

Verify Bun is listening on port 3000

sudo netstat -tlnp | grep :3000

Test direct application access

curl http://127.0.0.1:3000/api/health

Check Nginx status

sudo systemctl status nginx

Test through reverse proxy (replace with your domain)

curl -H "Host: example.com" http://localhost/api/health

Test SSL certificate (replace with your domain)

curl -I https://example.com

Check service logs

sudo journalctl -u bunapp -f --lines 20

Common issues

SymptomCauseFix
Bun command not foundPATH not set correctlyAdd /home/bunapp/.bun/bin to PATH in service file
Service fails to startPermission issuesCheck sudo journalctl -u bunapp and verify file ownership
502 Bad Gateway from NginxBun app not runningCheck sudo systemctl status bunapp and restart if needed
SSL certificate errorsDomain not pointing to serverVerify DNS A record and run sudo certbot --nginx again
Port 3000 accessible externallyFirewall misconfigurationEnsure only ports 80/443 are open, not 3000
Application crashes on restartMissing dependenciesCheck logs and ensure all npm packages are installed

Performance optimization

Configure Nginx caching

Add caching directives to improve performance for static assets and API responses.

# Add inside the server block, before existing location blocks
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
    proxy_pass http://127.0.0.1:3000;
    proxy_set_header Host $host;
    expires 1y;
    add_header Cache-Control "public, immutable";
    access_log off;
}

Cache API responses for 5 minutes

location /api/ { proxy_pass http://127.0.0.1:3000; proxy_set_header Host $host; proxy_cache_valid 200 5m; add_header X-Cache-Status $upstream_cache_status; }

Reload Nginx after making changes:

sudo nginx -t && sudo systemctl reload nginx

Monitoring and maintenance

Set up log rotation

Configure logrotate to manage application and Nginx logs to prevent disk space issues.

sudo tee /etc/logrotate.d/bunapp > /dev/null << 'EOF'
/var/log/nginx/bunapp_*.log {
    daily
    missingok
    rotate 14
    compress
    delaycompress
    notifempty
    create 644 www-data adm
    postrotate
        if [ -f /var/run/nginx.pid ]; then
            kill -USR1 cat /var/run/nginx.pid
        fi
    endscript
}
EOF

Create health check script

Set up automated health monitoring to detect and alert on application issues.

sudo -u bunapp tee /home/bunapp/health-check.sh > /dev/null << 'EOF'
#!/bin/bash

Health check script for Bun application

HEALTH_URL="http://127.0.0.1:3000/api/health" LOG_FILE="/home/bunapp/health-check.log" TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')

Test application health

if curl -s --max-time 10 "$HEALTH_URL" > /dev/null; then echo "[$TIMESTAMP] Health check passed" >> "$LOG_FILE" exit 0 else echo "[$TIMESTAMP] Health check failed - attempting restart" >> "$LOG_FILE" systemctl restart bunapp sleep 5 # Test again after restart if curl -s --max-time 10 "$HEALTH_URL" > /dev/null; then echo "[$TIMESTAMP] Service restarted successfully" >> "$LOG_FILE" exit 0 else echo "[$TIMESTAMP] Service restart failed" >> "$LOG_FILE" exit 1 fi fi EOF sudo chmod +x /home/bunapp/health-check.sh

Next steps

Running this in production?

Want this handled for you? This works for a single server. When you run multiple environments or need this available 24/7, keeping it healthy is a different job. See how we run infrastructure like this for European teams.

Automated install script

Run this to automate the entire setup

Need help?

Don't want to manage this yourself?

We handle managed cloud infrastructure for businesses that depend on uptime. From initial setup to ongoing operations.