Browse guides
Back to Guides
BeginnerGetting StartedOpenClaw

OpenClaw on Linux (Ubuntu/Debian): Setup Guide

A guide to installing and configuring OpenClaw on Linux: VPS and dedicated hardware, prerequisites, installation, systemd setup, and firewall configuration.

Sam Okafor40 minutes

#Linux as an OpenClaw Host

Linux is an excellent choice for running OpenClaw, especially on cloud VPS instances or dedicated servers. With systemd service management, low resource overhead, and rock-solid uptime, a Linux server can keep your agent running indefinitely without intervention.

This guide focuses on Ubuntu 22.04 LTS and Ubuntu 24.04 LTS, which are the most popular choices in the OpenClaw community. The instructions also apply to Debian 12 (Bookworm) with minor adjustments. For other distributions, adapt the package manager commands accordingly.

#VPS vs Dedicated Hardware

FactorVPS (Cloud)Dedicated Hardware
Cost$5-20/monthOne-time hardware cost
Uptime99.9%+ SLADepends on your power/internet
Setup time5 minutes30+ minutes
MaintenanceProvider handles hardwareYou handle everything
PrivacyData on provider's serversData stays local
PerformanceShared resourcesDedicated resources
Best forQuick start, remote accessPrivacy-focused, power users

Recommended VPS providers: Hetzner (best value), DigitalOcean, Linode, Vultr. A $5-10/month instance with 1 vCPU and 1 GB RAM is sufficient for a single OpenClaw agent.

Recommended hardware: Any x86_64 or ARM64 machine running Ubuntu or Debian. A Raspberry Pi 5 works but is not recommended for production use due to SD card reliability.


#Step 1: System Prerequisites

#Update Your System

Start with a fresh, updated system:

$sudo apt update && sudo apt upgrade -y

#Install Required Packages

Install the essential build tools and dependencies:

$sudo apt install -y curl wget git build-essential ca-certificates gnupg

#What Node.js Version Does OpenClaw Need?

The install docs list the supported runtimes as "Node 24.16+ or 26.1+", and recommend Node 26. Use the NodeSource repository to get one:

$curl -fsSL https://deb.nodesource.com/setup_26.x | sudo -E bash -
$sudo apt install -y nodejs

Verify the installation:

$node --version && npm --version

Ubuntu's own nodejs package is usually older than the supported range for the latest release listed as of September 19, 2026, which is why the NodeSource step comes first.

#Install pnpm (Optional)

$npm install -g pnpm

#Step 2: Create a Dedicated User

Running OpenClaw as root is a security risk. Create a dedicated user account:

$sudo adduser --system --group --home /home/openclaw --shell /bin/bash openclaw

This creates a system user with:

  • Home directory at /home/openclaw
  • A dedicated group named openclaw
  • Bash as the default shell
  • No password login (use sudo su - openclaw to switch)

Grant the user necessary permissions:

$sudo usermod -aG sudo openclaw

Adding the openclaw user to the sudo group is optional and only needed if your skills require elevated permissions. For maximum security, do not add sudo access and instead grant specific permissions as needed.

Switch to the new user for the rest of the installation:

$sudo su - openclaw

#Step 3: Install OpenClaw

Run the documented installer script as the openclaw user:

$curl -fsSL https://openclaw.ai/install.sh | bash

If you prefer a global package install, the docs give an npm and a pnpm path. npm 12 blocks unapproved package lifecycle scripts by default, hence the flag:

$npm install -g openclaw@latest --allow-scripts=openclaw
$pnpm add -g --allow-build=openclaw openclaw@latest

Then run onboarding, which is also where the managed service gets installed:

$openclaw onboard --install-daemon

#What Does Onboarding Actually Ask?

openclaw onboard is "Guided setup that establishes inference first: it detects existing AI access, waits for your provider choice, verifies that connection, persists only the working route, and then starts OpenClaw to configure the rest." The documented phases are:

  1. Security notice and agent naming

    The wizard opens with a security notice, then names your first agent. Use --agent-name <name> to set it non-interactively when no roster exists.

  2. Discovery access-level choice

    Choose how much discovery access the agent gets.

  3. AI provider detection and selection

    OpenClaw detects existing AI access and waits for your provider choice. See the API Key Setup guide for details on obtaining keys. The docs do not name a default or recommended model, so choose on your own cost and capability trade-off.

  4. Live connection verification

    The wizard verifies the connection and persists only the working route.

  5. Memory import offer

    If there is anything to import, the wizard offers to bring it across.

  6. Standard setup application

    Config, workspace and bootstrap files are written. Pass --skip-health if you only want those writes in automation.

  7. Plugin and skill recommendations

    The wizard suggests plugins and skills for your setup.

  8. Control UI dashboard handoff

    The wizard hands off to the Control UI dashboard, or falls back to the terminal.

--install-daemon "starts the managed gateway install path first". Without a daemon flag, a local gateway must already be running (for example openclaw gateway run). openclaw onboard --classic opens the full step-by-step wizard, and --flow manual gives full prompts for port, bind and secret storage.

Messaging channels are configured after onboarding; see the Messaging Channels Guide.

#Verify Installation

After onboarding completes, check that OpenClaw is working:

$openclaw health

openclaw health fetches "a health snapshot from the running Gateway over WebSocket RPC (no direct channel sockets from the CLI)". It takes --json, --timeout <ms> (default 10000) and --verbose, which "forces a live probe and expands output across all configured accounts and agents". There is no --quiet flag, so parse --json rather than grepping prose:

$openclaw health --json

#Step 4: Set Up systemd Service

The systemd service ensures OpenClaw starts on boot and restarts automatically if it crashes. This is the Linux equivalent of the macOS LaunchAgent.

#Automatic Installation

The easiest approach is to use the built-in service installer. There is no bare openclaw --install-daemon flag. openclaw gateway install "installs and starts the service"; the --install-daemon flag belongs to onboarding, where it "starts the managed gateway install path first".

$openclaw gateway install

#What Is the Service Actually Called?

openclaw-gateway.service, not openclaw.service. The docs name the unit openclaw-gateway.service, or openclaw-gateway-<profile>.service for a named profile. Get it running and enabled:

$systemctl --user enable --now openclaw-gateway.service

Inspect the unit OpenClaw wrote:

$systemctl --user cat openclaw-gateway.service

#Manual Installation

If you prefer to write the unit yourself, the Gateway runbook's example uses these directives:

ini
# ~/.config/systemd/user/openclaw-gateway.service
[Unit]
Description=OpenClaw Gateway
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/local/bin/openclaw gateway --port 18789
Restart=always
RestartSec=10
TimeoutStopSec=330
RestartPreventExitStatus=78
[Install]
WantedBy=default.target

Two of those directives are load-bearing and easy to get wrong:

  • TimeoutStopSec=330 covers the Gateway's maximum 315-second stop drain. systemd's own limit is 330 seconds; launchd's is 20.
  • RestartPreventExitStatus=78 stops a restart loop on config errors. Exit code 78 signals invalid configuration.

Note that there is no --foreground flag. openclaw gateway (explicitly, openclaw gateway run) runs in the foreground already, which is what a Type=simple unit wants. Adjust the ExecStart= path if your openclaw binary lives elsewhere. For a system-wide unit, install it under /etc/systemd/system/openclaw-gateway.service and add a User= line to [Service].

Save the file and enable it:

$mkdir -p ~/.config/systemd/user
$systemctl --user daemon-reload
$systemctl --user enable --now openclaw-gateway.service

For a system unit instead:

$sudo systemctl daemon-reload && sudo systemctl enable --now openclaw-gateway.service

#Enable Lingering

By default, systemd user services stop when the user logs out. Enable lingering so the service runs even when no session is active:

$sudo loginctl enable-linger $(whoami)

This is critical for headless servers and VPS instances.

#Verify the Service

$systemctl --user status openclaw-gateway.service

You should see active (running) in the output. openclaw gateway status --deep adds system-level service scanning, and openclaw doctor --fix audits and repairs service config drift.

View logs:

$journalctl --user -u openclaw-gateway -f

The journal only carries what the unit writes to stdout and stderr. OpenClaw's own log file defaults to /tmp/openclaw/openclaw-YYYY-MM-DD.log (/tmp/openclaw/openclaw-<profile>-YYYY-MM-DD.log for named profiles), and you can redirect it in ~/.openclaw/openclaw.json:

json
{
"logging": {
"file": "/path/to/openclaw.log"
}
}

Separately, Gateway lifecycle mutations "append best-effort key-value audit records to <state-dir>/logs/gateway-restart.log".


#Step 5: Configure the Firewall

If your server is accessible from the internet (especially on a VPS), you must configure the firewall to protect your OpenClaw instance.

#Install and Enable UFW

$sudo apt install -y ufw

Set default policies:

$sudo ufw default deny incoming
$sudo ufw default allow outgoing

#Allow SSH

Before enabling the firewall, make sure SSH is allowed or you will lock yourself out:

$sudo ufw allow ssh

#Allow the Gateway Port (Optional)

The Gateway multiplexes "WebSocket + HTTP (Control UI, hooks, etc.)" onto a single port. The documented default is 18789, with precedence --port > OPENCLAW_GATEWAY_PORT > gateway.port > 18789. If you genuinely need it reachable off-host:

$sudo ufw allow 18789/tcp

The Gateway does have built-in auth, and it is required: "Gateway auth is required by default - with no valid auth path configured, the Gateway refuses WebSocket connections (fail-closed)." The gateway.auth.mode values are token ("shared bearer token (recommended for most setups)"), password and trusted-proxy. That is a reason to configure auth properly, not a reason to open the port: the docs say the lan, tailnet and custom bind modes "expand the attack surface. Only use with gateway auth ... and a real firewall", advise preferring Tailscale Serve over LAN binds, and say to "never expose the Gateway unauthenticated on 0.0.0.0". Non-loopback Control UI deployments also require gateway.controlUi.allowedOrigins; allowing all origins is "an explicit allow-all policy, not a hardened default".

In most cases, leave gateway.bind on its loopback default and reach the box over Tailscale or an SSH tunnel instead of punching a hole in the firewall.

#Enable the Firewall

$sudo ufw enable

Check the status:

$sudo ufw status verbose

#Step 6: Remote Access

#Option A: SSH (Simplest)

If your server has a public IP or you are on the same network:

bash
ssh openclaw@your-server-ip

For VPS instances, use the IP address provided by your hosting provider.

Tailscale creates a secure mesh VPN, allowing you to access your server from anywhere without exposing ports to the internet.

Install Tailscale:

$curl -fsSL https://tailscale.com/install.sh | sh
$sudo tailscale up

Follow the authentication link printed in the terminal. Once connected, your server gets a stable Tailscale IP (e.g., 100.x.y.z) that you can use from any device on your Tailscale network.

#Option C: Cloudflare Tunnel (Advanced)

For exposing the dashboard securely without opening ports:

$curl -fsSL https://pkg.cloudflare.com/cloudflared-stable-linux-amd64.deb -o cloudflared.deb && sudo dpkg -i cloudflared.deb

Configure a tunnel pointing to localhost:18789, the default Gateway port. This gives you a custom domain with Cloudflare's authentication layer in front of the Gateway's own. If the Control UI will be reached on a non-loopback origin, set gateway.controlUi.allowedOrigins and consider gateway.publicOrigin, documented as an "optional externally reachable HTTPS origin".


#Step 7: Verify Everything Works

Run the comprehensive diagnostic:

$openclaw doctor

Send a test message through your configured messaging channel and confirm the agent responds.

#Monitoring the Agent

Set up basic monitoring to ensure your agent stays healthy:

Check logs for errors:

$journalctl --user -u openclaw-gateway --since '1 hour ago' --priority err

Monitor resource usage:

$systemctl --user status openclaw-gateway | grep Memory

Set up a simple health check cron:

openclaw health has no --quiet flag, so branch on --json output instead of prose:

bash
# Add to crontab: checks health every 5 minutes, restarts if the snapshot fails
*/5 * * * * openclaw health --json --timeout 5000 >/dev/null 2>&1 || systemctl --user restart openclaw-gateway.service

OpenClaw also has its own scheduler, so a host cron entry is only for the case where the Gateway itself is down. Anything the agent should do on a schedule belongs in an automation: openclaw automations list --all shows every job including disabled ones, and the command "registers as openclaw cron with openclaw automations as an alias".


#Where Does OpenClaw Put Its Files?

"Everything lives under $OPENCLAW_STATE_DIR (default: ~/.openclaw)", which for the dedicated user on this guide's setup is /home/openclaw/.openclaw. The docs describe it as holding "config, channel/provider state, auth profiles, sessions, logs". These are the paths they actually name:

text
/home/openclaw/.openclaw/
openclaw.json # Main configuration file
.env # Global environment fallback
secrets.json # Secret values referenced from config
credentials/oauth.json # OAuth credentials
state/openclaw.sqlite # Shared state database
skills/ # Shared managed skills
agents/<agentId>/agent/auth.json # Per-agent auth profile
agents/<agentId>/agent/openclaw-agent.sqlite
agents/<agentId>/sessions/ # Per-agent session records
workspace/ # Agent workspace (agents.defaults.workspace)

The agent workspace defaults to OPENCLAW_WORKSPACE_DIR when set, otherwise <state-dir>/workspace. Bootstrap writes these files into it:

text
/home/openclaw/.openclaw/workspace/
AGENTS.md # Operating rules, including a `## Tools` section
SOUL.md # Agent personality (optional bootstrap file)
IDENTITY.md # Name, role and avatar (optional bootstrap file)
USER.md # Context about you (optional bootstrap file)
BOOTSTRAP.md # First-run only
MEMORY.md # Curated long-term memory (optional)
BOOT.md # Optional
memory/YYYY-MM-DD.md # Daily memory files
skills/ # Optional workspace-local skills

Worth knowing:

  • There is no data/ or backups/ directory in the documented layout, and skills/ is not split into built-in, community and custom folders.
  • TOOLS.md and HEARTBEAT.md are retired. Tool and environment notes "now live in the ## Tools section of AGENTS.md", and HEARTBEAT.md "is accepted but a no-op since heartbeat context moved to cron monitor scratch".
  • .env here is a global fallback. OpenClaw reads env vars from the parent process (shell, launchd/systemd, CI) and additionally loads a .env from the current working directory plus the global one at ~/.openclaw/.env. Neither normally overrides an existing variable. On fresh Ubuntu installs using the default state dir, OpenClaw also reads ~/.config/openclaw/gateway.env as a compatibility fallback after the global .env; if the two disagree it keeps ~/.openclaw/.env and prints a warning.
  • Logs are not under the state directory by default. See the logging note in the service section above.

There is also no openclaw config edit. To find the file your editor should open, use config file, which "Prints the active config file path, resolved from OPENCLAW_CONFIG_PATH or the default location", and edit that file directly:

$openclaw config file

Or directly:

$nano ~/.openclaw/openclaw.json

#Managing the Service

Common systemd commands for managing your OpenClaw instance:

CommandDescription
systemctl --user start openclaw-gatewayStart the service
systemctl --user stop openclaw-gatewayStop the service
systemctl --user restart openclaw-gatewayRestart the service
systemctl --user status openclaw-gatewayCheck service status
journalctl --user -u openclaw-gateway -fStream the unit's stdout/stderr
journalctl --user -u openclaw-gateway --since todayView today's journal entries
systemctl --user enable openclaw-gatewayEnable start on boot
systemctl --user disable openclaw-gatewayDisable start on boot

You can also use the OpenClaw CLI commands, which wrap these systemd operations:

$openclaw gateway start
$openclaw gateway stop
$openclaw gateway restart
$openclaw logs --follow

#Updating OpenClaw on Linux

To update to the latest version:

$openclaw update

openclaw update is documented as "Update OpenClaw and switch between stable/extended-stable/beta/dev channels." What the docs actually promise:

  • It restarts the Gateway service after a successful update unless you pass --no-restart.
  • Package-manager updates that do restart "verify the restarted service reports the expected version before the command succeeds."
  • --channel sets and persists the update channel after core update success.
  • --dry-run previews the planned actions "without writing config, installing, syncing plugins, or restarting."

The docs make no claim that update backs up your config, so take your own backup of openclaw.json before a risky upgrade.

$openclaw update --dry-run

If you installed globally with npm and prefer to drive the package manager yourself:

$npm install -g openclaw@latest --allow-scripts=openclaw
$systemctl --user restart openclaw-gateway.service

#Troubleshooting Linux-Specific Issues

#Service fails to start

Check the journal for error details:

$journalctl --user -u openclaw-gateway --no-pager -n 50

Common causes:

  • Node.js not found in PATH, or below the supported "Node 24.16+ or 26.1+" (fix: add the full path to ExecStart in the service file, or upgrade Node)
  • Port 18789 already in use (fix: set gateway.port in openclaw.json, pass --port, or stop the conflicting service)
  • Invalid configuration, which exits with code 78 (fix: run openclaw config validate, which "validates the current config against the active schema without starting the gateway")
  • Missing API keys (fix: run openclaw config file to locate openclaw.json, then add your keys)

#Permission denied errors

If OpenClaw cannot write to its data directory:

$sudo chown -R openclaw:openclaw /home/openclaw/.openclaw

#DNS resolution failures

Some minimal VPS images do not have proper DNS configuration:

$echo 'nameserver 1.1.1.1' | sudo tee /etc/resolv.conf

#High memory usage

OpenClaw publishes no memory or CPU budget for the Gateway, so there is no documented "normal" to compare against. Measure first with systemctl --user status openclaw-gateway, then run openclaw doctor before reaching for a cap. systemd's MemoryMax and CPUQuota will happily throttle or OOM-kill a healthy Gateway mid-turn, so set them only once you know what your own workload needs, and keep TimeoutStopSec=330 so a restart still gets its full stop drain.


#Next Steps

Sources

  1. OpenClaw: Where things live on disk
  2. OpenClaw: Agent workspace
  3. OpenClaw: Configuration: agent workspace and bootstrap
  4. OpenClaw: Config
  5. OpenClaw: Gateway
  6. OpenClaw: Manage the Gateway service
  7. OpenClaw: Onboard
  8. OpenClaw: Logs
  9. OpenClaw: Install
  10. OpenClaw: Env vars and .env loading
  11. OpenClaw: Configuration: gateway
  12. OpenClaw: Gateway ports, already running, and remote mode
  13. OpenClaw: Network exposure
  14. OpenClaw: Gateway runbook
  15. OpenClaw: Run the Gateway
  16. OpenClaw: Logging
  17. OpenClaw: Health
  18. OpenClaw: Dashboard
  19. OpenClaw: Update

Skip the trial and error

ClawDocx members get 500+ tested prompts, ready-to-install SKILL.md files, SOUL.md and CLAUDE.md templates, and an MCP server so your agent can pull all of it directly while it works.