WebSprint.dev

Install OpenClaw on Ubuntu for an Automated Markdown Blog

Install and harden OpenClaw on Ubuntu, then explore our Markdown blog architecture: separate writer and editor agents with gated automatic publishing.

By WebSprint.dev · Published

We installed OpenClaw on our Ubuntu server to research and draft articles for a Markdown blog. A separate operator-owned script now publishes automatically after editorial, build, SEO, and secret checks pass. Neither agent commits or pushes.

Steps 1–6 show the installation and hardening path. Steps 7–8 explain our automated-blog architecture with sanitized invocation and handoff examples, not a complete release-script distribution. Replace angle-bracket placeholders before running commands; keep actual credentials out of commands and chat.

1. Preserve the server’s existing Node installation

Our server ran Ubuntu 26.04 LTS, x86_64, and systemd. Its APT-installed Node.js 22.x supported other tools, so we left it untouched. Have Bash, curl, a text viewer, a normal user account, an eligible ChatGPT/Codex subscription, and Brave Search API access ready.

The installation guide requires Node 24.16+ or 26.1+. Its private-prefix installer keeps OpenClaw and its runtime separate from system Node. We chose that route rather than changing a shared dependency.

Download the official installer and inspect it before execution:

mkdir -p "$HOME/openclaw-install-review"
cd "$HOME/openclaw-install-review"
curl -fsSL https://openclaw.ai/install-cli.sh -o install-cli.sh
less install-cli.sh

Review download sources, installation paths, and service-related branches against the installer reference. Run this reviewed copy, not a second download piped into Bash.

2. Install privately, set PATH, and inspect findings

Our major-only --node-version 26 attempt failed; the exact version worked. These are our recorded installation flags:

bash ./install-cli.sh \
  --install-method npm \
  --node-version 26.1.0 \
  --runtime-only \
  --no-onboard

"$HOME/.openclaw/bin/openclaw" --version
export PATH="$HOME/.openclaw/bin:$PATH"
openclaw --version
openclaw doctor --lint

The installation used ~/.openclaw with its own Node 26.1.0, without onboarding or a Gateway service. Inspect your shell startup file and add the PATH export once, preserving existing content. Reopen the shell and verify the CLI again.

Doctor lint reports findings without applying repairs. Plain Doctor can change state, so use lint first and resolve findings deliberately. The exact runtime version above records our setup; review supported versions before a new deployment.

3. Configure a dormant, loopback-only Gateway

We did not install a long-running Gateway service. Our scheduled jobs use embedded agent turns, so no OpenClaw Gateway listens between runs. This does not describe other services on the server.

We still configured safe Gateway settings for any later operator use:

openclaw config set gateway.mode local
openclaw config set gateway.bind loopback
openclaw config set gateway.port 18789
openclaw config set gateway.auth.mode token
openclaw config set discovery.mdns.mode off
openclaw config set gateway.terminal.enabled false

Config writes require the local Gateway stopped. Keep embedded runs and the timer stopped during setup too. On an existing installation, inspect each affected setting before changing it rather than replacing the configuration wholesale.

Loopback binding and disabled mDNS follow the network-exposure guidance. We also disabled the browser terminal because this pipeline does not need it.

Generate a strong token in your password manager. Choose an uppercase secret-store name, substitute it consistently below, and enter the token only at the no-echo prompt:

openclaw secrets store set '<GATEWAY_SECRET_NAME>' --kind secret
openclaw config set gateway.auth.token \
  --ref-source store --ref-provider default \
  --ref-id '<GATEWAY_SECRET_NAME>'
openclaw secrets audit --check

The Secrets CLI supports offline storage and interactive protected entry, so no browser bootstrap is necessary. The SecretRef contract supplies the built-in default store provider on a fresh configuration.

Protected entries are write-only, but the store is not encrypted at rest. Protect the host and backups. Never put credentials in committed config, shell history, or chat; use the secret store or a supported mode-600 file.

4. Restrict tools and pin the OpenClaw runtime

Our initial execution policy permitted host commands without prompts. We changed it before the first agent run:

openclaw config set tools.exec.mode ask
openclaw config set tools.fs.workspaceOnly true
openclaw config set tools.codeMode false --strict-json
openclaw config set tools.deny '["computer"]' --strict-json
openclaw config set 'agents.defaults.models["openai/*"].agentRuntime.id' openclaw
openclaw approvals get
openclaw exec-policy show

The deny-list command is for a fresh installation. On an existing one, preserve every existing denial and add computer; setting an array replaces its contents.

Ask mode prompts on allowlist misses, not every command. Workspace-only access restricts file tools, not the operating-system reach of an approved shell command. Do not approve shell access to publisher files.

Disabling Code Mode keeps the ordinary tool interface. Inspect agent/model overrides, which can take precedence. The runtime wildcard pins OpenAI routes to OpenClaw; exact model or session overrides can supersede it. This avoids assuming that OpenClaw file controls automatically describe a different harness’s execution behavior.

5. Sign in and create the two workspaces

Use the documented headless OpenAI sign-in:

openclaw models auth login --provider openai --device-code
openclaw models list --provider openai
openclaw config set agents.defaults.model.primary 'openai/<MODEL_NAME>'

Complete the private verification flow and choose an available model from the returned catalog. Do not save login codes or account details in research notes.

Create separate agents using the Agents CLI. Substitute simple names and distinct absolute workspace paths:

openclaw agents add '<WRITER_AGENT>' \
  --workspace '<WRITER_WORKSPACE>' --non-interactive
openclaw agents add '<EDITOR_AGENT>' \
  --workspace '<EDITOR_WORKSPACE>' --non-interactive
openclaw config set 'agents.entries.<EDITOR_AGENT>.tools.deny' \
  '["exec","process","edit","apply_patch","computer"]' --strict-json
openclaw agents list
openclaw models status --agent '<WRITER_AGENT>'
openclaw models status --agent '<EDITOR_AGENT>'

Check usable authentication for both agents; shared-base OAuth inheritance is documented, but agent-local credentials can differ. Resolve any authentication error before scheduling.

Our editor retains write to produce its review file. Its instructions permit only that output, while the listed tools are denied. This is not a read-only workspace: write could overwrite an inbox copy. The original draft stays in the writer workspace, and the operator should reject changed review inputs.

Give each workspace an AGENTS.md: the writer produces cited evidence and a draft without publishing; the editor checks claims, links, originality, formatting, and secrets, then writes a verdict without rewriting the article. Restrict unnecessary delegation and administrative tools as part of the effective-policy review.

6. Bind Brave to its protected credential

Inspect and enable the official Brave plugin using the plugin CLI, then configure Brave Search:

openclaw plugins inspect brave
openclaw plugins enable brave
openclaw secrets store set '<BRAVE_SECRET_NAME>' --kind secret
openclaw secrets store set '<BRAVE_SECRET_NAME>' --allow-host '<BRAVE_API_HOST>'
openclaw config set plugins.entries.brave.config.webSearch.apiKey \
  --ref-source store --ref-provider default --ref-id '<BRAVE_SECRET_NAME>'
openclaw config set tools.web.search.provider brave
openclaw config set plugins.entries.brave.config.webSearch.mode web
openclaw secrets audit --check
openclaw config validate
openclaw doctor --lint

Use the official API hostname documented on the Brave page, without a scheme or path, for the host placeholder. If plugin inspection reports it missing, install the official catalog entry before proceeding.

The API-key SecretRef is what lets the plugin consume the protected value. Host binding alone neither activates the key nor enables an egress proxy. Keep the plugin’s official endpoint and test one search through the writer; verify returned sources, not merely plugin enablement. Log searches against a budget. Brave results are not Google Trends measurements.

7. Pass an evidence package through the operator script

The following is our blog architecture, with a suggested explicit handoff contract. It needs your repository’s validator and release implementation. This is the orchestration layer of our business automation work.

Our daily systemd user timer calls an operator-owned Bash script outside both agent workspaces. The script runs each step serially using the documented embedded agent invocation:

openclaw agent --local --agent '<WRITER_AGENT>' \
  --session-id '<WRITER_RUN_SESSION>' --json \
  --message 'Read the assigned topic and owner notes. Write drafts/<SLUG>.md and evidence/<SLUG>.md. Do not publish.'

After successful completion, the script copies the draft, evidence, topic JSON, and optional owner notes into the editor’s inbox. Neither agent needs the other workspace. Then it invokes the editor:

openclaw agent --local --agent '<EDITOR_AGENT>' \
  --session-id '<EDITOR_RUN_SESSION>' --json \
  --message 'Review inbox/<SLUG>/ using AGENTS.md. Write only reviews/<SLUG>.json with verdict, checks, and requiredChanges. Do not modify inputs.'

The Agent CLI returns failure status for unsuccessful turns. The script must also check the expected artifacts: process success alone is not editorial approval.

For your implementation, record an operator-computed digest of the draft and evidence package before review, bind the review file to that package, and compare again before release. Reject missing or malformed reviews, non-pass verdicts, changed inbox inputs, or a changed source draft. This digest check is an acceptance requirement proposed here, not a claimed feature of our existing script.

8. Release automatically only after the gates

Our Next.js site on Vercel renders Markdown into sanitized static HTML. A push to the main branch deploys it. Our script requires an editor pass, blog validation, a production build, and SEO/page checks against a loopback-only check server: one H1, canonical URL, BlogPosting schema, and sitemap inclusion.

Before pushing, it also runs a regex scan for credentials, private keys, email addresses, private network addresses, and home paths. Any match fails the release. Treat that scan as an additional check, not proof that every possible secret is detectable.

The release script generates an image through openclaw infer image generate, using gpt-image-2 through our ChatGPT sign-in, then crops it to 1600×900 WebP and sets the Open Graph image. These are owner-recorded pipeline steps; the production scripts are not reproduced here.

After one complete dry run passed every gate without pushing, the owner enabled automatic release with DRY_RUN=0. Our first article was pushed manually after equivalent checks; the second was published by the script. New posts are shared to our Facebook Page after the live check.

When implementing this architecture, keep the timer, script, and deployment credentials operator-owned. Configure the daily job to invoke <OPERATOR_SCRIPT> with an explicit working directory and private-prefix executable path. Start with release actions disabled, prevent overlapping runs, and preserve failure logs. Commit only the blog folder after all gates pass; a failed live check must stop social sharing.

9. Check quota use and first-run acceptance

Our measured daily run—five discovery categories, one article, and one review—used about 7% of the weekly ChatGPT allowance. A separate one-category discovery run used about 2%. These observations are not a per-article cash price or a capacity guarantee; cash cost was not measured.

Two server lessons mattered: build-time API-client imports needed placeholder environment values, and Next.js renamed its process to next-server. Clean up the check server by its known process group or port after verifying ownership, not by its original process name.

Before enabling your timer’s release stage, require:

  • CLI version, config validation, secret audit, and lint results reviewed; both agents authenticate.
  • A writer search returns sources; file access outside its workspace is refused.
  • Effective runtime is OpenClaw; unexpected commands require approval or fail unattended; editor command tools are unavailable.
  • Editor writes a review while original draft and review inputs remain unchanged.
  • A changed draft, failed verdict, forced build failure, or harmless scanner-test fixture blocks commit and push.
  • A complete dry run passes without release side effects.
  • Owner-authorized live release contains only blog changes; the live page, metadata, and image pass checks before social sharing.