My OpenClaw Setup: One Box That Can Make Websites

Posted
July 31, 2026
Updated
August 26, 2026
By
Jacob Lloyd β€” written with AI assistance, post-project
Read time
14 min read

In plain terms: This article explains my OpenClaw setup: one small Linux computer in my house whose AI helpers can make and maintain entire websites. They write draft articles, rebuild the site, and publish changes β€” but only through a safety script that previews everything first, backs up the server, and never deletes files. The helpers do the work; I decide when it goes live.

The website you’re reading right now is maintained by AI agents running on a small Linux box in my house β€” and the whole arrangement is built so they can do real work without ever being able to wreck anything.

Before anything else, a correction to what this page used to say. For a while it opened by telling you it refreshed itself once a week, and that was true: a scheduled job woke an OpenClaw agent, it re-read this page, fixed whatever had drifted, ran the guard checks and stamped the date. That job did not survive a restore from backup at the end of July, and I have not recreated it. So the honest version is this: everything in these diagrams still runs β€” the agents, the build, the guard checks, the gated deploy β€” but it runs when I sit down and ask for it, not on a timer. The “Updated” date at the top is stamped by whoever did the work, which is usually an agent, at my prompt. Step 3 still shows how the scheduled version was wired, because that part is worth copying even though mine is currently switched off.

tl;dr

  • What it is: a dedicated Linux mini-PC running OpenClaw (an open, self-hosted AI agent gateway) with local LLMs, wired to a static site generator and a gated deploy script.
  • What it costs: the hardware, electricity, and a cheap cloud API β€” DeepSeek runs most of my agent turns for a few dollars a month; free local models are the fallback tier.
  • What you need: any Linux box that can run a local model server, a static site, SSH access to your web host, and one afternoon. 16GB RAM handles small local models; 32GB+ opens up mid-size ones that write better prose.
  • What you end up with: agents that draft posts, rebuild the site, dry-run every deploy, back up the server before publishing, and refresh content on request behind secret-scan and content-quality guards.

The whole box at a glance

Everything lives on one machine. Models serve tokens β€” mostly DeepSeek’s cheap cloud API these days, with local models as the fallback β€” the OpenClaw gateway turns them into agents with tools, the agents edit plain-text content, a deterministic generator builds the site, and a single gated script is the only road to the server.

What you end up with

The end state, before any setup talk:

  • “Draft me a post about X” in chat β†’ an agent writes a Markdown article into the site’s content/ folder, following the site’s frontmatter schema and house style, and rebuilds the local preview.
  • “Deploy it” β†’ the agent runs the deploy script, which by default is a dry run: it shows exactly which files would change on the server and touches nothing. Publishing for real requires an explicit --go from me, and even then the script takes a server-side backup first and never deletes anything.
  • “Go over the site” β†’ an agent sweeps for stale dates, weak posts and dead links, with the same guard checks in front of anything that would publish (Step 3 has the details, including how to put that sweep on a timer).
  • laserlloyd.com itself is managed this way.

The division of labor is the point: the agents do the typing, the scripts enforce the rules, and I make the one decision that matters β€” whether a change goes live.

The hardware and OS: any Linux box works

I use a CHUWI AuBox Ai365 mini PC β€” it replaced an ASUS ROG Flow Z13 that developed a power fault after five months of 24/7 service. The CHUWI is modest hardware by home-lab standards, but for website management it’s plenty. Here’s what the current box looks like:

ComponentSpec
ModelCHUWI AuBox Ai365 (CHUWI Innovation And Technology, ShenZhen)
CPUAMD Ryzen AI 9 365 β€” 10 cores / 20 threads, Zen 5, up to 5.09 GHz
GPUAMD Radeon 880M (integrated, RDNA 3.5, Strix Point iGPU)
NPUAMD XDNA 2 (50 TOPS INT8)
RAM30 GB DDR5 β€” shared system + GPU, nominally 32 GB with ~2 GB reserved
Storage1 TB NVMe PCIe 4.0 SSD (TWSC TSC3CN1T-F1Q20S, Shenzhen Techwinsemi)
OSBluefin 44 (Fedora Silverblue atomic, GNOME 50.3, Linux 7.0.12)
NetworkingDual Realtek RTL8125 2.5GbE, Realtek RTL8851BE Wi-Fi 6
PortsUSB4, USB 3.2, HDMI, DisplayPort
Power~10W idle, ~40W CPU load, ~65W peak. Barrel connector (no USB-PD fragility)
CoolingSingle fan + heat pipe, near-silent at idle
Remote accessTailscale overlay network (private addresses, never exposed to the internet)

For website management specifically, the requirements are modest:

  • Any 64-bit Linux machine β€” a mini-PC, an old desktop, even a capable laptop. 16GB RAM runs small local models fine; 30–32GB opens up mid-size models that write noticeably better prose.
  • A distro with systemd (almost all of them) β€” the scheduling and service supervision below assume it.
  • Python 3 for the static site generator, rsync + SSH for deploys.

You don’t even strictly need a GPU. Small instruction-tuned models run acceptably on CPU for drafting and editing text. But I’ll be honest about where I landed after months of running both: DeepSeek’s cloud API is now the primary driver of my whole OpenClaw setup. The open local models were the original plan, and they still run, but the tradeoffs add up β€” minutes of model load time, VRAM juggling between services, and noticeably less intelligence than a frontier-cheap cloud model that costs fractions of a cent per message. Local is my fallback and my privacy tier, not my daily driver. (The real monthly bill: about $24.)

Step 1: install an agent gateway and a local model

Two pieces: something that serves a model over an OpenAI-compatible API, and something that turns that model into an agent β€” with tools, file access, and scheduled jobs.

Model server. LM Studio, Ollama, or llama.cpp’s server all work. Whichever you pick, run it as a systemd user service so it survives reboots:

# ~/.config/systemd/user/model-server.service
[Unit]
Description=Local LLM server
[Service]
ExecStart=/path/to/your/model-server --port 1234
Restart=on-failure
[Install]
WantedBy=default.target
systemctl --user enable --now model-server
curl -s http://localhost:1234/v1/models   # sanity check

Agent gateway. I use OpenClaw, an open self-hosted gateway that hosts named agents, gives them tools (shell, file read/write, web search), routes each agent to a model (local or cloud, with fallbacks), and exposes a chat interface. Install it, point a “webmaster” agent at your local model, and grant it tool access scoped to the website’s directory. Two configuration rules that matter regardless of which gateway you choose:

  • Bind the gateway to loopback and put any chat UI behind authentication. An agent with shell access is not a thing to leave open on your network.
  • Scope the agent’s working directory to the site repo. It needs to edit content/, not your home directory.

Two tiers: local agents for the routine, a strong cloud model for repairs

One box, two tiers of brainpower. The OpenClaw agents β€” riding DeepSeek’s cheap API, with local models behind them β€” handle the everyday, high-volume work: drafting, editing, link-checking, the content sweeps. But when the system itself breaks (a config regression, a crashed gateway, a build that won’t run), I escalate to a strong cloud coding model that treats the whole box as its patient. I wrote that story up separately in When OpenClaw Breaks, I Fix It with Claude Code: the OpenClaw agents are the workforce, the strong coding model is the mechanic.

The crew: every bot on the box

The gateway hosts a roster of named agents, each routed to the model that fits its job. They all show up in DisPatch, the chat app the family uses β€” but not all of them in both modes: DisPatch’s PIN lock splits the roster into a safe, always-available set and a full-power set that only appears after unlock.

BotJobRuns onDisPatch (locked)DisPatch (unlocked)
BitsFront desk β€” the agent you talk to; understands the ask, dispatches the right specialistDeepSeek flash (cloud)β€”βœ”
BrainsThe hard thinker β€” specs, audits, anything worth waiting forDeepSeek pro (cloud)β€”βœ”
FlashQuick jobs and scheduled follow-ups β€” the watchdog that chases late workDeepSeek flash (cloud)β€”βœ”
HermesThe coding-agent bridge β€” dispatched programming work, gated deploysDeepSeek pro (cloud)β€”βœ”
DoxyLocal workhorse β€” bulk and private jobs that shouldn't leave the house35B MoE (local, on this box)β€”βœ”
CharleyVision β€” "what's in this photo?", image-aware tasksGemma vision model (on a second PC)βœ”βœ”
Alpha & BetaFamily-safe general helpers β€” homework questions, everyday chatDeepSeek flash (cloud)βœ”βœ”
TerminexNot an agent β€” DisPatch's Reasonix coding terminal, a real PTY in the browserDeepSeek pro (cloud)β€”βœ”

How they perform, honestly:

  • The DeepSeek-backed bots are the daily drivers. Flash-tier agents (Bits, Flash, Alpha, Beta) answer in seconds, cost fractions of a cent per message, and never need warming up. Brains, on the pro tier, takes noticeably longer per answer β€” that’s the point, it’s the one you hand problems worth thinking about β€” and still costs pennies per session. The whole stack’s measured bill is in the cost article.
  • The local bots are slower and dumber, but private and free. Doxy runs a 35B mixture-of-experts model on this box, and it is the one agent whose work never leaves the building. It takes a while to load into memory and its long turns can run for minutes; the gateway’s timeouts had to be raised specifically to accommodate it. That model size is the ceiling here β€” the CHUWI’s 30GB of RAM handles small to mid-size models fine, but the 120B I used to run on the old machine is well beyond it. Charley’s vision model runs on a second PC on the network, which keeps image work from evicting Doxy from memory mid-job.
  • The safe set is deliberately boring. Alpha, Beta, and Charley are the locked-mode roster: general info and vision, no tools that touch files or systems. That’s what makes handing a phone to a kid a non-event.

Step 2: a static site plus a gated deploy script

Agents and WordPress mix badly β€” a database, a login, plugins, and PHP are all attack surface and all failure modes. A static site generator is the natural partner for an agent: the entire site is plain text files in a folder, the build is one command, and “publish” is a file copy. My generator is a few hundred lines of Python (Jinja2 + Markdown + YAML); Hugo, Eleventy, or Zola work the same way. What matters is the contract:

  • Content is Markdown files with frontmatter. An LLM edits these natively and a git diff shows exactly what it did.
  • The build is deterministic. Same content in, byte-identical site out β€” so any output change is traceable to a content change.
  • There is exactly one deploy path, and it is a script with the safety built in, not a set of instructions the agent is trusted to follow.

That last one is the heart of this whole setup. Here’s the shape of mine:

./deploy.sh preflight   # checks SSH, target webroot, size budget
./deploy.sh             # build + DRY-RUN rsync β€” prints what would change
./deploy.sh --go        # build + server-side backup + publish + verify

And what the script guarantees, in order of importance:

  1. Dry-run by default. Running it with no arguments can never modify the server. An agent can run it freely to show me a pending deploy.
  2. Never deletes. rsync runs without --delete. A confused agent can overwrite a page with a newer version; it cannot erase the site.
  3. Backs up first. With --go, the script takes a server-side tarball of the webroot before any file is overwritten (keeping the last few). If the backup fails, the deploy aborts.
  4. Fail-closed. A missing manifest, an unreachable host, or a placeholder config value aborts before rsync runs. Source files (templates, scripts, configs) are hard-excluded so they can never ship to the public webroot.
  5. Verifies after. It fetches the live homepage and greps for a generator marker the build embeds β€” a bare HTTP 200 can be a false positive if old hosting still answers for the domain.

The agent is allowed to run the dry run and preflight whenever it likes. The --go flag is reserved for me. That single asymmetry converts “an AI has SSH-adjacent access to my production site” from terrifying to boring.

Step 3: putting the refresh on a timer (and why mine is off)

The last piece is the one that makes a site self-maintaining, and it is also the piece I am currently running by hand. The shape is simple: a systemd timer fires on a schedule and hands the agent a standing brief β€” review the site, refresh anything stale, tighten weak posts, verify internal links, and stage, not publish, the result.

Mine ran that way until a restore from backup at the end of July quietly took the job with it. I noticed weeks later, from a stale date on this very page. That is worth saying out loud, because it is the failure mode of every scheduled job: a timer that stops firing looks exactly like a timer with nothing to do. If you build this, have the job report something every run β€” even “nothing changed” β€” so silence means broken rather than idle.

The sweep still happens here; it happens in a session I start, and the updated: stamps on this site are written by the agent doing that work.

# ~/.config/systemd/user/site-refresh.timer
[Unit]
Description=Weekly website content refresh
[Timer]
OnCalendar=Sun 06:00
Persistent=true
[Install]
WantedBy=timers.target

The service it triggers calls the gateway’s API (or CLI) with the brief, then runs the guard checks on whatever the agent staged:

  • Secret scan. A pattern scan over the diff for anything shaped like an API key, token, password, or private hostname. Tools like gitleaks do this off the shelf. Any hit aborts the run β€” fail closed, no exceptions.
  • Professional-content check. A second LLM pass, with a different prompt than the writer, reviews the staged changes against one question: “would a business be comfortable publishing this?” Tone, claims, unfinished TODOs, placeholder text. A fail leaves the changes staged for human review instead of deploying.
  • Build must pass, including the link/asset checker β€” a broken image or dead internal link fails the run before it ever reaches the deploy script.

Only when all three pass does the job even dry-run a deploy β€” and on this site, actual publishing still waits for my --go. If you eventually trust the pipeline enough to let a scheduled job publish autonomously, the guards plus the never-delete, backup-first deploy script cap the blast radius at “a bad article went live and I restored from backup.”

Replicating this

Everything above is buildable from this description β€” there’s nothing proprietary in it. The recipe, condensed:

  1. Pick a Linux box and install a local model server (LM Studio, Ollama, llama.cpp) as a systemd service.
  2. Install an agent gateway (OpenClaw or similar) bound to loopback; create one webmaster agent with file/shell tools scoped to the site directory.
  3. Move the site to a static generator if it isn’t already β€” Markdown content in, one build command out.
  4. Write the gated deploy script with the five guarantees above: dry-run default, no deletions, backup-first, fail-closed, post-deploy verification. This is the piece worth the most care; it’s maybe 150 lines of bash.
  5. Add the two guard checks (secret scan + LLM content review), fail-closed β€” and a timer for the sweep if you want one, with a report every run so a dead timer is visible.
  6. Keep --go human until the pipeline has earned otherwise.

Per this site’s convention, the “implement this yourself” box at the top of the article means exactly what it says: this write-up is deliberately detailed enough that you can hand the whole article to your own LLM and ask it to build each piece for your site. The deploy script’s guarantees list is effectively its spec.

Gotchas

  • The deploy script must be the only path. The first time you (or an agent) hand-rolls an rsync “just this once,” you’ve created the incident the script existed to prevent. Mine hard-excludes source files and refuses placeholder configs precisely because a one-off command wouldn’t.
  • A bare HTTP 200 is not a successful deploy. During my WordPress migration, the old hosting kept answering for the domain β€” every “verification” passed while the new files sat unused. Embed a generator marker in your build and grep the live page for it.
  • Agents follow the schema you show them, including the TODOs. If your post scaffold has placeholder text, a lazy model will publish TODO: excerpt here verbatim. That’s half of why the professional-content check exists.
  • Don’t let the writer review itself. Same model, same prompt, same blind spots. The reviewer needs a different prompt at minimum, ideally a different model.
  • Scheduled LLM jobs need timeouts and a “staged, not published” default. A weekly job that can publish unsupervised will eventually publish something weird at 6am on a Sunday. Staging costs you nothing but a review.
  • A scheduled job that disappears is silent. Mine went missing in a restore and nothing complained; the only symptom was a date on this page that stopped moving. Make the job say something every time it runs.
  • Secret scans belong in the pipeline, not in the agent’s instructions. “Never include secrets” in a prompt is a wish; a fail-closed scan on the diff is a control.

← More AI & Local LLM