Blog/How to Run OpenClaw on Mac Mini and fly.io in 2026
·9 min read

How to Run OpenClaw on Mac Mini and fly.io in 2026 (Complete Setup Guide)

OpenClaw runs well on both a Mac mini and fly.io — but each platform has its own quirks. This guide covers the setup process for both, plus the three gotchas that silently break most deployments.

Want the full step-by-step walkthrough?

The OpenClaw Setup Guide covers Telegram integration, model config, and a complete troubleshooting reference. 17 pages, $19.

Get the guide — $19 →

What is OpenClaw?

OpenClaw is a self-hosted automation agent that connects to AI models, executes scheduled tasks, and integrates with messaging platforms like Telegram. Unlike cloud-based automation tools, you run it on your own infrastructure — which means no per-seat pricing, no vendor lock-in, and full control over what data leaves your environment.

The tradeoff is setup complexity. OpenClaw is not a one-click install. It requires Node.js, a process manager, and some configuration work before it runs reliably. This guide covers the two deployment targets that work best in practice: a Mac mini for always-on local hosting, and fly.io for cloud-based deployment without managing a full VPS.

Why Mac mini and fly.io are the best deployment targets

OpenClaw needs to run continuously. It checks queues, responds to triggers, and — if you have Telegram integration enabled — needs to be reachable at any time. That requirement rules out standard developer laptops (which sleep) and eliminates most cheap VPS options (which have unreliable uptime or throttle sustained CPU usage).

Mac mini: best for local or home-office deployments

The M-series Mac mini is near-silent, draws under 10W at idle, and runs macOS — which means launchd, Apple's service manager, for automatic startup and restart. A Mac mini M2 costs around $600 and will run OpenClaw indefinitely without thermal issues. It also gives you direct access to the filesystem for logs and model caches, which simplifies debugging.

The main limitation is that your Mac mini needs to be powered on and connected. If you want OpenClaw accessible from outside your home network, you'll need to configure port forwarding or use a tunnel service.

fly.io: best for cloud deployments without DevOps overhead

fly.io runs containerised applications close to your users across a global network. Unlike AWS or GCP, it doesn't require you to manage VMs, networking rules, or load balancers. You define your app in a fly.toml file, attach a persistent volume for state, and deploy with a single command.

The free tier is limited — OpenClaw needs at least a shared-cpu-1x instance with 512MB RAM to run without OOM errors — but the paid tier starts at $1.94/month per machine. For a lightweight agent that isn't under constant load, this is cost-effective.


Mac mini setup overview

This section gives you the high-level steps. The full guide includes exact commands, expected output at each stage, and a checklist.

Step 1: Install Node.js 24

OpenClaw requires Node.js 24 or later. The recommended installation method is via nvm (Node Version Manager), which lets you switch Node versions without affecting system tools:

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
nvm install 24
nvm use 24
node -v  # should output v24.x.x

Do not install Node.js via Homebrew for this use case. Homebrew-managed Node can cause path issues when launchd launches OpenClaw at boot, because launchd does not inherit your shell's PATH.

Step 2: Clone and install OpenClaw

Clone the repository into a stable location (not your Downloads folder) and run the install:

git clone https://github.com/openclaw/openclaw ~/openclaw
cd ~/openclaw
npm install
cp .env.example .env
# Edit .env with your API keys and config

Step 3: Configure launchd for automatic startup

On macOS, launchd is the recommended way to run OpenClaw as a background service that starts on login and restarts on crash. You create a .plist file in ~/Library/LaunchAgents/:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>com.openclaw.agent</string>
  <key>ProgramArguments</key>
  <array>
    <string>/Users/YOUR_USERNAME/.nvm/versions/node/v24.0.0/bin/node</string>
    <string>/Users/YOUR_USERNAME/openclaw/index.js</string>
  </array>
  <key>RunAtLoad</key>
  <true/>
  <key>KeepAlive</key>
  <true/>
  <key>StandardOutPath</key>
  <string>/Users/YOUR_USERNAME/openclaw/logs/stdout.log</string>
  <key>StandardErrorPath</key>
  <string>/Users/YOUR_USERNAME/openclaw/logs/stderr.log</string>
  <key>EnvironmentVariables</key>
  <dict>
    <key>HOME</key>
    <string>/Users/YOUR_USERNAME</string>
    <key>PATH</key>
    <string>/Users/YOUR_USERNAME/.nvm/versions/node/v24.0.0/bin:/usr/local/bin:/usr/bin:/bin</string>
  </dict>
</dict>
</plist>

Load it with launchctl load ~/Library/LaunchAgents/com.openclaw.agent.plist. After a reboot, OpenClaw will start automatically.


fly.io setup overview

The fly.io deployment requires a Dockerfile, a fly.toml config file, a persistent volume for OpenClaw's state directory, and secrets for your API keys.

fly.toml configuration

A minimal fly.toml for OpenClaw looks like this:

app = "my-openclaw"
primary_region = "lhr"

[build]
  dockerfile = "Dockerfile"

[env]
  NODE_ENV = "production"

[[mounts]]
  source = "openclaw_data"
  destination = "/app/data"

[http_service]
  internal_port = 3000
  force_https = true
  auto_stop_machines = false
  auto_start_machines = true
  min_machines_running = 1

[[vm]]
  memory = "512mb"
  cpu_kind = "shared"
  cpus = 1

Creating the volume and setting secrets

Create the persistent volume before first deployment. This is where OpenClaw stores its queue state and gateway lock files:

# Create the volume (do this once)
fly volumes create openclaw_data --size 1 --region lhr

# Set secrets (these become environment variables in your container)
fly secrets set OPENAI_API_KEY=sk-...
fly secrets set TELEGRAM_BOT_TOKEN=...
fly secrets set OPENCLAW_SECRET=...

# Deploy
fly deploy

The 3 most common OpenClaw setup gotchas

These three issues account for the vast majority of broken OpenClaw deployments. If your instance starts but behaves unexpectedly, check these first.

Gotcha 1: auto_stop_machines stopping your fly.io instance

fly.io's default configuration sets auto_stop_machines = true, which stops your machine when it receives no HTTP traffic for a period. For a web app that only runs on request, this is a sensible cost-saving default. For OpenClaw — which runs scheduled background tasks and needs to receive Telegram webhooks at any time — it is catastrophic.

The fix: set auto_stop_machines = false and min_machines_running = 1 in your fly.toml. Without this, your OpenClaw instance will silently go offline after a few minutes of inactivity and miss scheduled tasks.

Gotcha 2: launchd not inheriting environment variables on Mac mini

When you run OpenClaw manually in your terminal, your shell loads ~/.zshrc or ~/.bash_profile, which sets environment variables like PATH, HOME, and any API keys you've exported. When launchd starts OpenClaw at boot, it does not load your shell config. It starts with a minimal environment.

The result: OpenClaw either fails to start (because node is not found) or starts but fails silently (because API key environment variables are missing). The fix is to declare all required environment variables explicitly in the EnvironmentVariables dict in your .plist file, and to use the absolute path to your nvm-managed Node binary (not just node).

Gotcha 3: Stale gateway lock file blocking startup

OpenClaw creates a lock file at startup to prevent multiple instances from running simultaneously. If the process exits uncleanly — due to a crash, a forced kill, or a fly.io machine restart — the lock file is not deleted. On the next startup, OpenClaw sees the lock file, assumes another instance is already running, and exits immediately.

On Mac mini, the lock file lives at ~/openclaw/.gateway.lock by default. On fly.io, it lives in your mounted volume at /app/data/.gateway.lock.

If OpenClaw is not running but shows no errors in the logs, check for a stale lock file. Delete it and restart the service. For fly.io, you can do this via fly ssh console and removing the file manually, or by configuring OpenClaw to delete a stale lock file on startup (available in the config options documented in the full guide).


Verifying your OpenClaw deployment

Once deployed, run a quick health check to confirm OpenClaw is running and connected:

  • Mac mini: Check ~/openclaw/logs/stdout.log for startup messages and the first scheduled task run
  • fly.io: Run fly logs to tail live output from your machine
  • Both: If Telegram is configured, send a /status command to your bot — a response confirms the agent is running and the Telegram webhook is working

For fly.io, also verify that your machine is not in stopped state by running fly machines list. The status column should show started.


What this guide does not cover

This overview covers the core setup path and the three deployment-breaking gotchas. It intentionally omits:

  • Telegram bot creation and webhook registration
  • Model configuration (switching between OpenAI, Anthropic, and local models)
  • Scheduling syntax and task config
  • Multi-region fly.io deployments
  • Log rotation on Mac mini
  • Full troubleshooting reference (10+ additional error states)

Get the complete walkthrough

For the complete step-by-step walkthrough including Telegram integration, model config, and a full troubleshooting guide, get the OpenClaw Setup Guide — 17 pages, $19.

The guide covers both Mac mini and fly.io deployments end-to-end, with exact commands at every step, annotated config files, and a troubleshooting section that addresses 12 known failure modes. It's written for developers who are comfortable with the terminal but have not deployed OpenClaw before.

Ready to run OpenClaw?

17-page setup guide. Mac mini + fly.io. Telegram integration. $19.

Get the OpenClaw Setup Guide →