OpenClaw
The openclaw template is the base Linux desktop with OpenClaw installed. A computer made from it opens OpenClaw's setup wizard in a terminal on login. Once set up, OpenClaw runs its gateway as a service on the computer and serves its Control UI to the desktop's browser. This guide covers what is on the image, how to set OpenClaw up with a model key you keep as a Mandala secret, and how to drive it from your own code.
What the template runs
- The base desktop: Debian with Xfce, Chromium and Firefox, git, gh, Node, Python, uv and ripgrep. The desktop user is
user. - OpenClaw, installed globally from npm, so
openclawis on every account's PATH.openclaw --versionnames the pinned release. The computer does not update it;sudo npm install -g openclaw@latestdoes, when you choose to. - A launcher that opens a terminal titled "OpenClaw" when the desktop starts, and again from the desktop icon. Before setup it runs
openclaw onboard --install-daemon, OpenClaw's own wizard. After setup it opens the Control UI in the browser (openclaw dashboard) and the terminal chat (openclaw tui). - 2 vCPU, 4 GB of memory and a 20 GB disk unless you ask for another size at create. OpenClaw recommends 4 vCPU and 8 GB for browser-heavy work, which is a resize away.
OpenClaw ships set up for nobody. Setup takes your model credential, and it also mints the gateway's own auth token, which should be unique to each computer. So setup runs once, on the computer, rather than in the image.
computer.ready fires when the desktop and the OpenClaw terminal are both up. It does not wait for the gateway, because on a fresh computer nothing has been set up yet.
First run on the desktop
Open the computer from the dashboard and follow the wizard in the terminal. It looks for credentials already on the machine, such as a Claude Code or Codex login or an API key in the environment, and offers the rest as a menu: sign in with a Claude or ChatGPT subscription in the desktop's browser, or paste an API key. It then installs the gateway as a systemd user service and opens the Control UI. The gateway comes back on its own after a restart, before anyone logs in to the desktop.
Set it up with a key from a secret
Store your model key once as a Mandala secret and bind it to the computer as an environment variable, for example ANTHROPIC_API_KEY. A bound value reaches the desktop session, every new shell and every exec, from a memory-backed file system in the guest.
Then run the wizard without prompts, as the desktop user:
openclaw onboard --non-interactive --accept-risk --auth-choice apiKey \
--secret-input-mode ref --install-daemon \
--skip-channels --skip-skills --skip-search --skip-ui--auth-choice apiKey sets up Anthropic from ANTHROPIC_API_KEY, and --secret-input-mode ref keeps the key out of OpenClaw's config file, ~/.openclaw/openclaw.json. openclaw onboard --help lists the other providers' choices. Setup without prompts requires --accept-risk, which acknowledges what the wizard's first screen says: OpenClaw is a personal, single-user agent, and its tools have full access to the computer.
The gateway service holds its own copy of the key. Installing the service writes the environment variables it needs, the key included, into ~/.config/systemd/user/openclaw-gateway.service, so from then on the value is on the computer's disk. When you replace the secret, reinstall the service so it takes the new value, and remove the backup it leaves:
openclaw gateway install --force
openclaw gateway restart
rm ~/.config/systemd/user/openclaw-gateway.service.bakOnly one setup runs at a time. The wizard in the welcome terminal holds a lock on the configuration, so close that terminal before running setup another way. The programs below do that first.
Drive it from code
The program below binds your Anthropic key, launches an OpenClaw computer, closes the welcome terminal, sets OpenClaw up without prompts, reopens the launcher so the Control UI appears, saves a screenshot, and sends the agent one message through the gateway with openclaw agent, which prints the reply.
Commands run with desktop=True (Python) or { desktop: true } (TypeScript) run as the desktop user, whose ~/.openclaw the setup writes and whose gateway service it installs.
pip install mandala-computer
# or
npm install mandala-computerPython
import os
import time
from mandala_computer import Client
client = Client() # reads MANDALA_API_KEY
key = client.secrets.set("ANTHROPIC_API_KEY", os.environ["ANTHROPIC_API_KEY"])
with client.computers.ephemeral(
template="openclaw",
name="openclaw",
secrets=[{"secret_id": key.id, "env": "ANTHROPIC_API_KEY"}],
) as computer:
computer.wait_for("computer.ready") # the desktop and the welcome terminal are up
computer.wait_for_secrets(expect_secrets=True) # the key has reached the guest
def run(command, timeout=120):
"""Run a command as the desktop user."""
res = computer.exec(command, desktop=True, timeout=timeout)
if not res.ok:
raise RuntimeError(f"{command} exited {res.exit_code}: {res.stdout_text[-500:]}{res.stderr_text[-500:]}")
return res.stdout_text
# Close the welcome terminal: its setup wizard holds a lock on the config.
run(
"pkill -f '^bash /usr/local/bin/openclaw-terminal';"
" pkill -f '^bash /usr/local/bin/openclaw-welcome';"
" pkill -f '^openclaw-onboard'; true"
)
# Set up without prompts, reading the key from ANTHROPIC_API_KEY.
run(
"openclaw onboard --non-interactive --accept-risk --auth-choice apiKey"
" --secret-input-mode ref --install-daemon"
" --skip-channels --skip-skills --skip-search --skip-ui",
timeout=300,
)
# Reopen the launcher: set up now, it opens the Control UI and the chat.
run("nohup /usr/local/bin/openclaw-terminal >/dev/null 2>&1 &")
time.sleep(20)
with open("openclaw.png", "wb") as f:
f.write(computer.screenshot(fresh=True))
print(run("openclaw agent --agent main -m 'Which Linux distribution is this?'", timeout=300))TypeScript
import { writeFile } from 'node:fs/promises';
import { Client } from 'mandala-computer';
const client = new Client(); // reads MANDALA_API_KEY
const key = await client.secrets.set({ name: 'ANTHROPIC_API_KEY', value: process.env.ANTHROPIC_API_KEY! });
await client.computers.ephemeral(
{ template: 'openclaw', name: 'openclaw', secrets: [{ secretId: key.id, env: 'ANTHROPIC_API_KEY' }] },
async (computer) => {
await computer.waitFor('computer.ready'); // the desktop and the welcome terminal are up
await computer.waitForSecrets({ expectSecrets: true }); // the key has reached the guest
/** Run a command as the desktop user. */
const run = async (command: string, timeoutS = 120) => {
const res = await computer.exec(command, { desktop: true, timeoutS });
if (res.exitCode !== 0) {
throw new Error(
`${command} exited ${res.exitCode}: ${res.stdoutText.slice(-500)}${res.stderrText.slice(-500)}`,
);
}
return res.stdoutText;
};
// Close the welcome terminal: its setup wizard holds a lock on the config.
await run(
"pkill -f '^bash /usr/local/bin/openclaw-terminal';" +
" pkill -f '^bash /usr/local/bin/openclaw-welcome';" +
" pkill -f '^openclaw-onboard'; true",
);
// Set up without prompts, reading the key from ANTHROPIC_API_KEY.
await run(
'openclaw onboard --non-interactive --accept-risk --auth-choice apiKey' +
' --secret-input-mode ref --install-daemon' +
' --skip-channels --skip-skills --skip-search --skip-ui',
300,
);
// Reopen the launcher: set up now, it opens the Control UI and the chat.
await run('nohup /usr/local/bin/openclaw-terminal >/dev/null 2>&1 &');
await new Promise((resolve) => setTimeout(resolve, 20_000));
await writeFile('openclaw.png', await computer.screenshot(undefined, { fresh: true }));
console.log(await run("openclaw agent --agent main -m 'Which Linux distribution is this?'", 300));
},
);The pkill patterns are anchored with ^ so they cannot match the shell running them. ephemeral() deletes the computer when the block or callback exits, even after an error, including a timeout while the computer comes up or its key is delivered. To keep the computer, use client.computers.launch(...) instead, which waits for the secrets itself; it is persistent and billed while it runs, and launch() leaves it in place when it fails, for you to inspect and delete.
A failed model call (a wrong key, say) makes openclaw agent exit non-zero, and the program raises with OpenClaw's own message.
Working with a running OpenClaw computer
- The Control UI is at
http://127.0.0.1:18789/inside the computer. The gateway listens on loopback only, so reach it through the desktop's browser; the launcher opens it with a one-time pairing link. - Check the gateway.
openclaw gateway statusandopenclaw doctor, run as the desktop user, report whether the service is up and what is wrong. - Reopen the window. The desktop icon runs the launcher again, as does
computer.exec("nohup /usr/local/bin/openclaw-terminal >/dev/null 2>&1 &", desktop=True). - Open a shell.
mandala terminal <id>opens an interactive shell on the computer as the desktop user.
See also: Troubleshooting and Run your agent next to a computer.