OpenCode Web — Setup Guide for Ubuntu 24.04¶
Step-by-step guide to deploy the OpenCode web interface behind Nginx with SSL on a fresh Ubuntu 24.04 server. All commands are copy-pasteable. Replace your-server.com with your domain and your-username with your Linux user.
1. Server Preparation¶
1.1 Update system¶
sudo apt update && sudo apt upgrade -y
1.2 Configure UFW firewall¶
sudo ufw allow 22 # SSH
sudo ufw allow 80 # HTTP
sudo ufw allow 443 # HTTPS
sudo ufw enable
1.3 Install and configure fail2ban¶
sudo apt install fail2ban -y
sudo systemctl enable --now fail2ban
1.4 Optional: Add swap (4 GB recommended for low-memory VPS)¶
sudo fallocate -l 4G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
1.5 Install unattended-upgrades¶
sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure -plow unattended-upgrades # choose "Yes"
2. Install OpenCode via npm (nvm)¶
Run these commands as your deploy user (your-username), not as root.
2.1 Install nvm and Node.js¶
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
# Restart your shell or source .bashrc, then:
nvm install 24
node --version # verify: v24.x.x
2.2 Install OpenCode globally¶
npm install -g opencode-ai
opencode --version # verify
2.3 Verify the installation¶
which opencode
# Typically: /home/your-username/.nvm/versions/node/v24.x.x/bin/opencode
opencode --version
3. Create the systemd Service¶
Create the service file with sudo:
sudo nano /etc/systemd/system/opencode-web.service
Contents (replace your-username and the exact nvm bin path):
[Unit]
Description=OpenCode Web
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=your-username
Environment=HOME=/home/your-username
Environment=OPENCODE_SERVER_USERNAME=your-username
Environment=OPENCODE_SERVER_PASSWORD=your-secure-password
ExecStart=/bin/bash -c 'export NVM_DIR="$HOME/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" && opencode web --port 5173 --hostname 127.0.0.1'
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
The shell wrapper is used instead of a hardcoded binary path. This way the service survives Node.js upgrades via nvm — it always picks up the active version through
nvm.sh. SettingHOMEis required so nvm can locate its directory.
Reload systemd and test:
sudo systemctl daemon-reload
sudo systemctl start opencode-web
sudo systemctl status opencode-web
4. Configure Nginx¶
4.1 Install Nginx¶
sudo apt install nginx -y
4.2 Create the site config¶
sudo nano /etc/nginx/sites-available/your-server.com
server {
listen 80;
listen [::]:80;
server_name your-server.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name your-server.com;
# SSL certs — populated by Certbot later
# ssl_certificate /etc/letsencrypt/live/your-server.com/fullchain.pem;
# ssl_certificate_key /etc/letsencrypt/live/your-server.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:5173;
proxy_http_version 1.1;
# Standard proxy headers
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;
# WebSocket support — REQUIRED, otherwise the UI shows a white screen
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Long timeouts for WebSocket connections
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
}
The WebSocket headers are not optional. Without
Upgrade,Connection, and the long timeouts, the browser cannot establish a WebSocket connection to the OpenCode backend and the web UI renders as a blank white screen.
4.3 Enable the site¶
sudo ln -s /etc/nginx/sites-available/your-server.com /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default # remove default if present
sudo nginx -t
sudo systemctl reload nginx
4.4 Hide Nginx version¶
sudo sed -i 's/# server_tokens off;/server_tokens off;/' /etc/nginx/nginx.conf
# Or add it manually inside the http { } block if the line doesn't exist
sudo nginx -t && sudo systemctl reload nginx
4.5 Get SSL with Certbot¶
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d your-server.com
Certbot modifies the Nginx config automatically, adding the SSL lines and Let's Encrypt options. Choose redirect (option 2) when asked.
Test auto-renewal:
sudo certbot renew --dry-run
4‑alt. Configure Apache (Alternative)¶
Skip section 4 and follow this part if you prefer Apache over Nginx.
4‑alt.1 Install Apache and required modules¶
sudo apt install apache2 -y
sudo a2enmod proxy proxy_http proxy_wstunnel ssl rewrite
sudo systemctl restart apache2
4‑alt.2 Create VirtualHost¶
sudo nano /etc/apache2/sites-available/your-server.com.conf
<VirtualHost *:80>
ServerName your-server.com
Redirect permanent / https://your-server.com/
</VirtualHost>
<VirtualHost *:443>
ServerName your-server.com
# SSL certs — populated by Certbot later
# SSLEngine on
# SSLCertificateFile /etc/letsencrypt/live/your-server.com/fullchain.pem
# SSLCertificateKeyFile /etc/letsencrypt/live/your-server.com/privkey.pem
# WebSocket upgrade support — REQUIRED, otherwise white screen
RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule /(.*) ws://127.0.0.1:5173/$1 [P,L]
# Main proxy
ProxyPreserveHost On
ProxyPass / http://127.0.0.1:5173/
ProxyPassReverse / http://127.0.0.1:5173/
# Long timeouts for WebSocket connections
ProxyTimeout 86400
</VirtualHost>
The WebSocket rewrite is not optional. The
RewriteCond/RewriteRuleblock routes WebSocket upgrade requests throughmod_proxy_wstunnel. Without it the browser cannot establish a WebSocket connection and the UI renders as a blank white screen.
4‑alt.3 Enable the site¶
sudo a2ensite your-server.com.conf
sudo a2dissite 000-default.conf # remove default if present
sudo apache2ctl configtest
sudo systemctl reload apache2
4‑alt.4 Hide Apache version (optional)¶
sudo sed -i 's/ServerTokens.*/ServerTokens Prod/' /etc/apache2/conf-available/security.conf
sudo systemctl reload apache2
4‑alt.5 Get SSL with Certbot¶
sudo apt install certbot python3-certbot-apache -y
sudo certbot --apache -d your-server.com
Choose redirect (option 2) when asked. Certbot modifies the VirtualHost config and enables SSL automatically.
Test auto-renewal:
sudo certbot renew --dry-run
5. Set Authentication¶
Authentication is controlled by two environment variables in the systemd unit (already set in step 3):
OPENCODE_SERVER_USERNAME— the login username for the web UIOPENCODE_SERVER_PASSWORD— the login password
To change credentials later:
sudo systemctl edit opencode-web
Add or modify Environment= lines under [Service], then:
sudo systemctl daemon-reload
sudo systemctl restart opencode-web
Alternatively, edit the main unit file directly and reload.
6. Enable and Start Everything¶
sudo systemctl enable opencode-web
sudo systemctl start opencode-web
sudo systemctl reload nginx
sudo systemctl status opencode-web # should show "active (running)"
Visit https://your-server.com in a browser. You should see the OpenCode login screen.
7. Troubleshooting¶
White screen / blank page¶
The WebSocket proxy configuration is missing or incorrect. Verify these four lines are present in the location / block:
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
Also check nginx -t && sudo systemctl reload nginx after any edit.
Port 5173 already in use¶
sudo lsof -i :5173 # see what's listening
sudo systemctl stop opencode-web
sudo systemctl start opencode-web
Service exits with status 127 (command not found)¶
nvm is not being sourced correctly. Check:
- HOME is set in the unit file
- The ExecStart line sources $HOME/.nvm/nvm.sh before calling opencode
- nvm is installed for the same user that runs the service (User=)
If you installed nvm as a different user, adjust NVM_DIR and User accordingly.
Service exits with status 1 (permission / env)¶
- Check the
User=line matches the user who owns the nvm directory. - Check that
HOME=is set — some npm packages require a home directory. - Run
journalctl -u opencode-web -n 50for detailed logs.
upstream prematurely closed connection¶
The OpenCode process crashed. Check journalctl -u opencode-web -f for the stack trace. Common cause: missing or invalid OPENCODE_SERVER_PASSWORD — OpenCode requires both username and password env vars.
Quick Reference¶
| Component | Path / Command |
|---|---|
| systemd unit | /etc/systemd/system/opencode-web.service |
| Nginx site | /etc/nginx/sites-available/your-server.com |
| OpenCode port | 127.0.0.1:5173 |
| Service logs | journalctl -u openencode-web -f |
| Nginx logs | /var/log/nginx/access.log, /var/log/nginx/error.log |
| Reload after config change | sudo systemctl daemon-reload && sudo systemctl restart opencode-web |