Deploy Hono + Bun on a VPS
Ultrafast web framework meets ultrafast runtime. systemd for process management, Caddy for automatic HTTPS — production-ready in ~30 minutes.
Prerequisites
- A RamNode KVM VPS running Ubuntu 22.04 or 24.04
- A non-root sudo user (or root access)
- A domain name pointed at your VPS IP (optional but recommended for HTTPS)
- SSH access to your server
Update the System
sudo apt update && sudo apt upgrade -yInstall Bun
Bun provides a single install script. Run it as your non-root user:
curl -fsSL https://bun.sh/install | bashReload your shell environment:
source ~/.bashrc
# or for zsh:
source ~/.zshrcVerify the install:
bun --versionNote: Bun requires a 64-bit CPU with AVX2 support. All RamNode KVM instances meet this requirement.
Create Your Hono App
If you're deploying an existing app, skip to Step 4. Otherwise, scaffold a new project:
mkdir ~/myapp && cd ~/myapp
bun init -yInstall Hono:
bun add honoCreate src/index.ts:
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => c.text('Hello from Hono + Bun on RamNode!'))
app.get('/health', (c) => c.json({ status: 'ok', timestamp: Date.now() }))
export default {
port: 3000,
fetch: app.fetch,
}Test it locally:
bun run src/index.tsBun should confirm the server is listening on port 3000. Press Ctrl+C to stop.
Deploy Your App Files
If pulling from a Git repo:
cd ~
git clone https://github.com/youruser/yourrepo.git myapp
cd myapp
bun installOr upload files directly with rsync:
rsync -avz ./myapp/ user@your-server-ip:~/myapp/Then install dependencies on the server:
cd ~/myapp
bun installCreate a systemd Service
Running your app as a systemd service keeps it alive across reboots and restarts it on failure.
[Unit]
Description=Hono + Bun App
After=network.target
[Service]
Type=simple
User=ubuntu
WorkingDirectory=/home/ubuntu/myapp
ExecStart=/home/ubuntu/.bun/bin/bun run src/index.ts
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal
SyslogIdentifier=myapp
# Environment variables
Environment=NODE_ENV=production
Environment=PORT=3000
[Install]
WantedBy=multi-user.targetTip: If your app uses a .env file, add EnvironmentFile=/home/ubuntu/myapp/.env under [Service].
Enable and start the service:
sudo systemctl daemon-reload
sudo systemctl enable myapp
sudo systemctl start myappCheck status and tail logs:
sudo systemctl status myapp
sudo journalctl -u myapp -fInstall Caddy as a Reverse Proxy
Caddy handles HTTPS automatically via Let's Encrypt:
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddyConfigure Caddy
Replace the default Caddyfile with your domain configuration:
yourdomain.com {
reverse_proxy localhost:3000
}If you're testing without a domain (HTTP only with IP):
:80 {
reverse_proxy localhost:3000
}Reload Caddy:
sudo systemctl reload caddyCaddy will automatically provision a TLS certificate for your domain via Let's Encrypt. Your app will be reachable at https://yourdomain.com within seconds.
Open the Firewall
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enableVerify the rules:
sudo ufw statusVerify the Deployment
Test your endpoints:
curl https://yourdomain.com
# Expected: Hello from Hono + Bun on RamNode!
curl https://yourdomain.com/health
# Expected: {"status":"ok","timestamp":...}Updating Your App
When you push new code, pull and restart:
cd ~/myapp
git pull
bun install # only needed if dependencies changed
sudo systemctl restart myappPerformance Notes
Bun's HTTP server is benchmarked significantly faster than Node.js for raw throughput. On a RamNode 512MB KVM instance, a Hono + Bun app can comfortably handle thousands of concurrent requests for typical API workloads. For CPU-intensive tasks, consider scaling to a 1GB or 2GB plan.
htop
# or for a quick snapshot:
sudo systemctl status myappTroubleshooting
App won't start
Check logs with sudo journalctl -u myapp -n 50. Common causes: wrong WorkingDirectory path, missing bun binary path, or port already in use.
Bun binary not found in systemd
systemd uses a minimal PATH. The ExecStart line uses the full path (/home/ubuntu/.bun/bin/bun) to avoid this. Verify with which bun.
Certificate not provisioning
Ensure your domain's A record points to your VPS IP and port 80 is open. Caddy uses the HTTP-01 challenge which requires port 80 to be reachable.
Port 3000 conflict
Check what's using the port: sudo ss -tlnp | grep 3000. Update the port in both your app and the systemd Environment=PORT=3000 line.
Next Steps
- Add a
.envfile for secrets and reference it withEnvironmentFilein your service - Set up log rotation with
logrotatefor long-running services - Explore Hono middleware for rate limiting, JWT auth, and request logging
- Consider Dokploy for multi-app management on your VPS
