Server Setup Guide
Complete guide to setting up a fresh Ubuntu server and installing Launchbox. You'll go from a blank VPS to a fully running deployment platform in about 15 minutes.
Your server username
All commands below will use this name
Do you already have a VPS?
Skip server setup steps if your VPS is ready
Create SSH Key (on your local PC)
The SSH key replaces password authentication — you'll use it to securely log into your server without typing a password each time.
ssh-keygen -t ed25519 -C "your-email@example.com"
Press Enter 3 times (default path, no passphrase). Then add the contents of ~/.ssh/id_ed25519.pub to your VPS provider's SSH key settings.
Create the Server
Create an Ubuntu server at your VPS provider (e.g. Hetzner, DigitalOcean) and select your SSH key. Create a firewall with the following inbound rules:
| Port | Protocol | Purpose |
|---|---|---|
| 22 | TCP | SSH access |
| 80 | TCP | HTTP — Launchbox Dashboard + Domain Routing |
| 443 | TCP | HTTPS (SSL via Let's Encrypt) |
You can also allow ICMP (for ping). Outbound rules: none needed (allow all).
Log into the Server
Connect as root — your SSH key will be used automatically.
ssh root@SERVER-IP
Update the System
Brings all pre-installed packages up to date. Always do this first on a fresh server.
apt update && apt upgrade -y
Create a New User
Never work as root — a typo could destroy your entire system. The new user gets sudo privileges so they can still run admin commands when needed.
adduser USER usermod -aG sudo USER
Set Up SSH Key for the New User
Copies the SSH key from root to the new user so you can also log in as USER via SSH.
mkdir -p /home/USER/.ssh cp ~/.ssh/authorized_keys /home/USER/.ssh/ chown -R USER:USER /home/USER/.ssh chmod 700 /home/USER/.ssh chmod 600 /home/USER/.ssh/authorized_keys
ssh USER@SERVER-IP works. If it does, proceed to step 7.Secure SSH
Disables root login and password authentication. After this, you can only log in with an SSH key as a normal user — much more secure.
# Disable root login sed -i 's/^PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config sed -i 's/^PermitRootLogin prohibit-password/PermitRootLogin no/' /etc/ssh/sshd_config # Disable password login sed -i 's/^#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config sed -i 's/^PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config systemctl restart ssh
ssh USER@SERVER-IP in a second terminal. Otherwise you could lock yourself out.Server Firewall (optional)
Second layer of protection directly on the server. If the provider's firewall is accidentally deleted, your server is still protected.
ufw default deny incoming ufw default allow outgoing ufw allow 22/tcp ufw allow 80/tcp ufw allow 443/tcp ufw enable
Install Node.js
Launchbox needs Node.js to build and run Next.js and Node apps.
curl -fsSL https://deb.nodesource.com/setup_22.x | bash - apt install -y nodejs
Install Git
Required to clone repos from GitHub.
apt install -y git
Create Launchbox Directory
Creates the directory where Launchbox and all app data will live.
mkdir -p /opt/launchbox/data chown -R USER:USER /opt/launchbox
Verify Everything is Installed
Quick check that all tools are available.
node -v npm -v git --version
exit ssh USER@SERVER-IP
Build & Upload Launchbox
On your local machine, build the Linux binary and copy it to the server. Go can cross-compile — you don't need Linux to build.
cd /path/to/launchbox # 1. Install frontend dependencies (first time only) cd frontend && npm install && cd .. # 2. Build the frontend (React → HTML/JS/CSS) cd frontend && npm run build && cd .. # 3. Build the Go binary for Linux (embeds the frontend) # Windows: set GOOS=linux set GOARCH=amd64 go build -o launchbox-linux . # Mac/Linux: GOOS=linux GOARCH=amd64 go build -o launchbox-linux . # 4. Upload to server scp launchbox-linux USER@SERVER-IP:/opt/launchbox/launchbox
Then on the server, make it executable:
ssh USER@SERVER-IP chmod +x /opt/launchbox/launchbox
Allow Port 80/443 Binding
Launchbox needs to listen on port 80 (and 443 for SSL) so domains are directly reachable. Linux only allows root to bind ports below 1024 — this command grants permission to your binary.
sudo setcap cap_net_bind_service=+ep /opt/launchbox/launchbox
Set Up systemd Service
Makes Launchbox start automatically — even after a server reboot or crash. Running apps are automatically restored on restart.
sudo tee /etc/systemd/system/launchbox.service << 'EOF' [Unit] Description=Launchbox PaaS After=network.target [Service] Type=simple User=USER WorkingDirectory=/opt/launchbox ExecStart=/opt/launchbox/launchbox Environment=LAUNCHBOX_PORT=80 Restart=always RestartSec=5 [Install] WantedBy=multi-user.target EOF sudo systemctl daemon-reload sudo systemctl enable --now launchbox
Optional: Add GitHub OAuth and login credentials as environment variables (add under [Service]):
Environment=GITHUB_CLIENT_ID=your-client-id Environment=GITHUB_CLIENT_SECRET=your-client-secret Environment=LAUNCHBOX_USER=your-username Environment=LAUNCHBOX_PASS=your-password
After editing the service file:
sudo systemctl daemon-reload sudo systemctl restart launchbox
Check if it's Running
Shows the service status. Should display "active (running)".
sudo systemctl status launchbox
Open in your browser: http://SERVER-IP
admin / admin — change immediately in Settings!Reference
Useful commands, update process, domain setup, and troubleshooting.
Useful Commands
| Command | Description |
|---|---|
| sudo systemctl status launchbox | Check status |
| sudo systemctl restart launchbox | Restart |
| sudo systemctl stop launchbox | Stop |
| sudo journalctl -u launchbox -f | Live logs |
| sudo journalctl -u launchbox -n 50 | Last 50 log lines |
Deploy an Update
When you've made changes to the code, rebuild locally and upload the new binary.
cd D:\path\to\launchbox # Rebuild frontend (only if UI changed) cd frontend npm run build cd .. # Build Linux binary (includes new frontend) set GOOS=linux set GOARCH=amd64 go build -o launchbox-linux .
# Stop the service ssh USER@SERVER-IP sudo systemctl stop launchbox exit # Upload new binary scp launchbox-linux USER@SERVER-IP:/opt/launchbox/launchbox # Set port permission + start ssh USER@SERVER-IP sudo setcap cap_net_bind_service=+ep /opt/launchbox/launchbox sudo systemctl start launchbox # Verify sudo systemctl status launchbox
Domain Setup
To make an app reachable under a custom domain:
- Set a DNS A record at your domain provider:
subdomain.yourdomain.com→SERVER-IP - Wait for DNS propagation (1–30 minutes, check with
nslookup) - In Launchbox → Domains → Add Domain → enter domain, select app
- DNS status is shown on the Domains page (green = connected)
- Your app is now reachable at
http://subdomain.yourdomain.com
Enable SSL (HTTPS)
- Ensure the DNS A record is correct (green status)
- Add the domain in Launchbox with SSL enabled
- Restart Launchbox:
sudo systemctl restart launchbox - Let's Encrypt automatically provisions a certificate
- HTTP requests are automatically redirected to HTTPS
- Certificates are stored in
./data/certs/and auto-renewed
Troubleshooting
"permission denied" on port 80/443
sudo setcap cap_net_bind_service=+ep /opt/launchbox/launchbox sudo systemctl restart launchbox
"EADDRINUSE: address already in use"
An old Node process is still running. Kill all and restart:
sudo killall -9 node sudo systemctl restart launchbox
"parse apps.json: unexpected..."
The apps.json is corrupted. Reset it:
echo '[]' > /opt/launchbox/data/apps.json sudo systemctl restart launchbox
SSL not working / TLS handshake errors
Delete old certificates and let Launchbox fetch new ones:
rm -rf /opt/launchbox/data/certs/* sudo systemctl restart launchbox
App deployed but not reachable via domain
- Check if the app is actually running (Dashboard → Status "Running"?)
- Check DNS:
nslookup your-domain.com→ correct server IP? - Check if Launchbox is listening on port 80:
sudo ss -tlnp | grep :80 - Check logs:
sudo journalctl -u launchbox -n 30
Service won't start after update
Usually missing setcap or wrong permissions:
chmod +x /opt/launchbox/launchbox sudo setcap cap_net_bind_service=+ep /opt/launchbox/launchbox sudo systemctl restart launchbox sudo journalctl -u launchbox -n 20
Full reset (delete all apps and repos)
sudo killall -9 node 2>/dev/null rm -rf /opt/launchbox/data/repos/* echo '[]' > /opt/launchbox/data/apps.json sudo systemctl restart launchbox
Directory Structure on Server
/opt/launchbox/ ├── launchbox ← The binary (contains frontend + backend) ├── data/ │ ├── apps.json ← App configurations │ ├── domains.json ← Domain mappings │ ├── github.json ← GitHub OAuth token │ ├── auth.json ← Login credentials │ ├── repos/ ← Cloned git repos │ │ ├── a1b2c3.../ ← App 1 (e.g. Next.js project) │ │ ├── d4e5f6.../ ← App 2 │ │ └── ... │ └── certs/ ← SSL certificates (Let's Encrypt)