DEV Community

Cover image for Building Claudio: My Always-On Claude Code Box
Ben Utting
Ben Utting

Posted on

Building Claudio: My Always-On Claude Code Box

Building Claudio: My Always-On Claude Code Box

I have an always-on Debian VM that reads the AI news, checks in on my clients, and sends me everything over Telegram. No extra infrastructure cost, just the $20/month Claude plan and cron.

That was V1. It lasted about two weeks before the OAuth tokens started expiring and every cron job died silently. This is the story of both versions.

Why build it

I run an AI automation freelance business. I have active clients, a content pipeline, and a morning news habit that used to eat 30 minutes before breakfast. I wanted a system that handled the recurring operational work without me opening a terminal.

The requirements were simple: run Claude Code skills on a schedule, store outputs on Google Drive, and notify me via Telegram. No orchestration platform. No extra cost beyond the $20/month Claude plan I already use for client work.

V1: cron and Claude Code

The first version was minimal. Claudio is a Debian 13 VM on my home network running Claude Code headless via cron.

0  6 * * *   /usr/bin/claude -p "/morning-brief" --permission-mode bypassPermissions >> ~/morning-brief.log 2>&1
0 10 * * *   /usr/bin/claude -p "/client-pulse" --permission-mode bypassPermissions >> ~/client-pulse.log 2>&1
Enter fullscreen mode Exit fullscreen mode

That's the entire automation layer. Two lines in a crontab. It worked for about two weeks.

The first gotcha was the permission prompt. Headless cron jobs hung silently because Claude Code was waiting for an interactive permission check that nobody would ever see. The fix: --permission-mode bypassPermissions. Not prominently documented, and the single most important flag for running Claude Code unattended.

The second gotcha killed V1 entirely. Claude Code's OAuth tokens eventually expire. A 6am cron job doesn't care that your session died at midnight. No error, no Telegram alert, just silence. The skills stopped running and I didn't notice for two days.

OAuth is designed for interactive sessions. If you're running anything headless, you need auth that doesn't expire. Cron and OAuth are fundamentally incompatible.

V2: Claude Desktop and Cowork

V2 solves the auth problem by replacing cron entirely. The stack now:

  • Claude Desktop (unofficial Linux build via aaddrick/claude-desktop-debian) running persistently on the XFCE desktop
  • Cowork scheduled tasks replacing cron. Claude Desktop fires each skill on a schedule, no OAuth expiry, no hanging permission prompts
  • MCP servers wired into Claude Desktop:
    • rclone MCP: custom-built, ~50 lines of Node.js, exposes rclone_cat, rclone_lsf, rclone_copyto against the existing gdrive: remote
    • Perplexity MCP: official @perplexity-ai/mcp-server, replaces the built-in WebSearch with better recency filtering and citation quality
  • Google Drive still via rclone, no FUSE mount, same explicit command pattern
  • Dashboard on :8080 via FastAPI/uvicorn

No cron. No Docker. No orchestration platform.

What broke in V2 (and the fixes)

Cowork's bubblewrap sandbox can't shell out. The rclone CLI worked fine in V1 because cron has no sandbox. Cowork runs inside bubblewrap on Linux, which blocks arbitrary binary execution. The fix: a minimal MCP server wrapping the three rclone commands Claudio actually uses. About 50 lines of Node.js, hardcoded to the gdrive: remote.

The Telegram plugin path is fragile. The plugin lived in a versioned cache directory (telegram/0.0.1/). A plugin update would silently break the Desktop config by changing the path. The fix: copy anything you depend on out of versioned caches into a stable location you control.

Bubblewrap has opinions about your filesystem. The sandbox mounts home as read-only. Any MCP server that tries to write to its own directory on startup (like the Telegram plugin's chmodSync call) will silently fail and take the whole server down with it. Patch it or move it somewhere the sandbox can write.

Telegram bot token conflicts. Claude Desktop and the Claude Code CLI cannot both run the Telegram MCP simultaneously. They fight over the same bot token long-poll (Telegram 409 conflict). The solution: Desktop owns outbound scheduled tasks, the CLI owns inbound Telegram. They share MCP servers but can't share stateful connections.

What's still the same

Drive is not mounted. Every file operation is still an explicit rclone command. Still deliberate, still more reliable than a FUSE mount that can go stale.

Absolute dates only. Every log entry uses YYYY-MM-DD. No "yesterday", no "Thursday". Small discipline, big difference when you read logs a week later.

Stage before writing to Drive. Skills write to /tmp/ first, then rclone copyto the finished file. Half-written files don't land on Drive.

Telegram is still one-way. Cowork tasks send, they don't receive. The reply loop is V3 territory.

What I learned

The biggest lesson across both versions: the right auth model matters more than the right scheduler. V1's cron was fine as a scheduler. It was the OAuth dependency that killed it. V2's Cowork is a fine scheduler too, but the reason it works is that it's tied to the Claude Max account session, which doesn't expire as long as the Desktop app stays open.

The second lesson: MCP servers need stable paths and sandbox awareness. Versioned cache directories, read-only home mounts, and stateful connection conflicts are all things you hit only in production. None of this shows up when you test interactively.

Claudio runs my business operations while I sleep, commute, or focus on client work. V1 proved the concept. V2 made it reliable.

ctrlaltautomate.com

Top comments (1)

Collapse
 
jackgrazio profile image
Giacomo Grazioli

Hi Ben!

If you're running Claude Code in a remote or automated setup, token expiry is likely one of the most frustrating friction points you'll encounter.

There is a little-known feature in the official Claude Code documentation that addresses this directly: you can generate a long-lived token with a 1-year validity, eliminating the need for frequent re-authentication in headless or automated workflows.

The relevant section is documented here:
šŸ‘‰ code.claude.com/docs/en/authentica...

I have been leveraging this in production for remoteagent.chat — a product designed to run Claude Code remotely. It has proven essential for maintaining stable, uninterrupted sessions without any manual intervention.

If you are building on top of Claude Code, this is well worth adding to your setup.