Install and maintain OpenClaw on Ubuntu
OpenClaw is installed for a non-root user with Node.js 24, a user-owned npm prefix, Google Chrome and automation tools, and Nginx TLS proxying to localhost port 18789.
Before you start
- Use a clean, supported Ubuntu server with root or sudo access.
- Point
app.example.comto the server before requesting a public TLS certificate. - Replace every value written as
CHANGE_MEand store the generated credentials in a password manager. - Take a snapshot before changing an existing installation.
- An existing non-root Linux user dedicated to running OpenClaw.
- A domain and TLS email; the Control UI allowed origin is set to the public HTTPS URL.
- Review the long list of document, media, browser, database-client, and development packages before installing on a minimal host.
What the AppStore installation creates
- Node.js 24 and user-owned npm prefix
~/.npm-global - OpenClaw state below
~/.openclaw - Google Chrome, browser support, document/OCR/media tools, and selected messaging SDKs
- Nginx TLS/websocket reverse proxy to port 18789
1. Install Node.js 24 and base packages
sudo apt update
curl -fsSL https://deb.nodesource.com/setup_24.x | sudo bash -
sudo apt install -y nodejs nginx certbot python3-certbot-nginx build-essential \
git curl jq rsync ripgrep fzf tree btop ncdu zip unzip ffmpeg pandoc \
sqlite3 postgresql-client mariadb-client redis-tools python3-venv pipx ufw fail2ban
sudo install -d -m 0755 /etc/apt/keyrings
curl -fsSL https://dl.google.com/linux/linux_signing_key.pub | sudo gpg --dearmor -o /etc/apt/keyrings/google-chrome.gpg
echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/google-chrome.gpg] http://dl.google.com/linux/chrome/deb/ stable main" | sudo tee /etc/apt/sources.list.d/google-chrome.list
sudo apt update
sudo apt install -y google-chrome-stable xvfb xauth dbus-x11 fonts-liberation
2. Create a user-owned npm prefix
Replace openclaw with the real non-root account.
sudo -u openclaw mkdir -p /home/openclaw/.npm-global
echo 'export PATH="$HOME/.npm-global/bin:$PATH"' | sudo -u openclaw tee -a /home/openclaw/.bashrc
sudo -u openclaw env HOME=/home/openclaw npm config set prefix /home/openclaw/.npm-global
3. Install OpenClaw as the application user
The playbook uses the official installer with onboarding disabled, then sets the allowed Control UI origin.
sudo -iu openclaw
export PATH="$HOME/.npm-global/bin:$PATH"
curl -fsSL https://openclaw.ai/install.sh | bash -s -- --no-onboard
openclaw config set gateway.controlUi.allowedOrigins '["https://app.example.com"]'
exit
4. Configure the Nginx TLS proxy
Proxy HTTP and websocket traffic to 127.0.0.1:18789; validate Nginx before requesting TLS.
sudo nano /etc/nginx/sites-available/openclaw
sudo ln -s /etc/nginx/sites-available/openclaw /etc/nginx/sites-enabled/openclaw
sudo nginx -t
sudo systemctl reload nginx
sudo certbot --nginx -d app.example.com -m admin@example.com --agree-tos --redirect
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw --force enable
Important files and data
- OpenClaw state/configuration:
/home/openclaw/.openclaw - User npm packages:
/home/openclaw/.npm-global - Nginx site:
/etc/nginx/sites-available/openclaw - Backend endpoint:
http://127.0.0.1:18789
Health checks and logs
Run these checks after installation and after each upgrade:
sudo -iu openclaw openclaw --version
curl -I http://127.0.0.1:18789
sudo nginx -t
sudo systemctl status nginx --no-pager
sudo ss -ltnp | grep 18789
Routine maintenance
Review release notes and take a backup or snapshot before upgrading. Use the following playbook-aligned commands as the starting point:
sudo -iu openclaw
export PATH="$HOME/.npm-global/bin:$PATH"
openclaw --version
# Use the OpenClaw-supported updater after backing up ~/.openclaw.
exit
sudo nginx -t
Backup scope
/home/openclaw/.openclawwith strict access controls- Any user-owned workspace or integration data OpenClaw is allowed to use
- Nginx configuration and an inventory of installed global packages
A usable backup needs both application files and application data. Test restoration on a separate server; an untested backup is not a recovery plan.
Troubleshooting
- Confirm DNS with
dig +short app.example.combefore retrying Certbot. - Test the web-server configuration before reloading it:
sudo nginx -torsudo apache2ctl configtest. - Check free space with
df -hand listening ports withsudo ss -ltnup. - If a service fails, inspect its systemd journal before changing configuration.
- If the command works interactively but not as a service, confirm that
~/.npm-global/binis in the service PATH. - If the Control UI rejects the browser, verify the configured allowed origin exactly matches the public HTTPS URL.
- Keep port 18789 private; public traffic should enter through Nginx on 443.
Security notes
- Do not paste passwords, API keys, repository credentials, private keys, or access tokens into tickets or public logs.
- Expose only the documented public ports. Keep database and application backend ports bound to localhost or a private network.
- Keep SSH access working before enabling UFW, then allow only the ports this guide lists.
- Renewal can be tested safely with
sudo certbot renew --dry-runwhere Certbot manages TLS.

