Install and configure Bun JavaScript runtime with systemd and reverse proxy

Beginner 25 min Apr 17, 2026 780 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(`
        
        
        
        
          <h1>Bun JavaScript Runtime</h1>
          <p>Server is running on port 3000</p>
          <p>Bun version: ${Bun.version}</p>
          <p>Current time: ${new Date().toISOString()}</p>
        
        
      `, {
        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(<code>Server running at http://${server.hostname}:${server.port}</code>);
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

Vous ne voulez pas gérer cela vous-même ?

Nous gérons l'infrastructure des entreprises qui dépendent de leur disponibilité. Entièrement infogéré, avec un interlocuteur fixe qui connaît votre environnement.

Vous avez un interlocuteur fixe qui connaît votre installation

Rotterdam 12:57 · joignable par message, sans formulaire de ticket