Back to Phantom Notes
Automation

Running Claude Code Remotely: Telegram Bot + systemd + Auto-Permission Watcher (The Setup Guide)

April 18, 202610 min readBy T.W. Ghost
Claude CodeTelegramsystemdVPSRemote AccessLinuxAutomationAnthropic

The Problem

Claude Code is a phenomenal agentic coding tool, but the default installation assumes a desktop-first workflow. You open a terminal, you start Claude Code, you type prompts, you close the terminal when you're done.

What if you want the agent to run 24/7? What if you want to poke it from your phone at the airport? What if you want to fire off a long-running refactor, go to bed, and check results in the morning?

Three problems stand in the way:

  • Persistence — Claude Code dies when you close the terminal. You need it running as a service.
  • Remote access — SSH works, but typing prompts over SSH from a phone is miserable. Telegram DMs feel native.
  • Permission prompts — Claude Code asks for confirmation on tool calls. Those prompts are fine at your desk, brutal when the agent is running unattended.

This is the architecture we use to solve all three.


What We Built

A Claude Code instance running 24/7 on a Linux VPS, accessible via a private Telegram bot, with a background watcher that auto-approves all permission prompts. The whole stack costs $7/month on a cheap VPS and takes about 30 minutes to set up from scratch.

The components:

  • Claude Code CLI installed on the VPS as a non-root user
  • systemd service keeping Claude Code alive across reboots
  • Telegram Channels plugin (official Claude plugin) bridging Telegram to Claude Code
  • Auto-permission watcher (a small bash script) approving permission prompts in the background
  • tmux session holding the interactive Claude Code session the watcher attaches to

When we message our Telegram bot, the message flows into Claude Code. Claude works, calls tools, gets permission prompts that the watcher auto-approves, completes the task, and replies in the Telegram chat. We can be on the train, in bed, or out running errands.


Why Not Just SSH In?

SSH works, but the ergonomics are wrong for a phone-first workflow.

SSH from a phone requires:

  • An SSH client app (Termius, Blink, or similar)
  • A stable keyboard on mobile (painful)
  • A tmux session you manually attach to
  • Visual parsing of terminal output on a small screen

Telegram bot access gives you:

  • Native iOS and Android keyboards with dictation
  • Message history with formatting
  • Push notifications when Claude replies
  • Conversation context that reads naturally
  • Inline keyboard buttons for confirmations (when needed)

For quick "what was that thing I did last week" queries or "fix the CI failure and push" requests, Telegram is dramatically better than SSH. For heavy debugging sessions, SSH still wins.


Why Telegram Specifically

The official Claude Channels plugin supports multiple messaging platforms: Telegram, Discord, Slack, iMessage, Signal. We chose Telegram because:

  • Private by default — no public servers or channels to configure. A bot DM is end-to-end between you and the bot.
  • Cross-platform — same Telegram client on iOS, Android, desktop, web. Context syncs.
  • Bot API is battle-tested — Telegram's Bot API has been stable for years. Rate limits are generous.
  • Allowlist control — you can restrict the bot to respond only to your user ID. No risk of strangers messaging the bot and triggering Claude on your infrastructure.
  • Inline buttons — Telegram's inline keyboard buttons work as permission prompt UI when the watcher isn't enough.

If Telegram is blocked in your country or you're deep in the Discord ecosystem, the same architecture works on Discord. The Claude Channels plugin supports both.


The Architecture

At a high level:

  • Your phone runs the Telegram app
  • Telegram servers route messages to/from your private bot
  • Your VPS runs Claude Code under a non-root user, wrapped in a systemd service
  • The systemd service launches tmux + Claude Code + the Telegram Channels plugin
  • A background watcher script monitors the tmux session for permission prompts and auto-approves them
  • Claude's tools (Read, Write, Bash, Edit, etc.) all operate within the user's home directory and permitted paths

Every incoming Telegram message becomes a new Claude Code turn. Every Claude reply comes back as a Telegram message. Permission prompts get handled silently in the background.


The systemd Service Pattern

We run Claude Code under a non-root user (we'll call it claude-user) because Anthropic recommends against running Claude Code as root, and the VS Code extension explicitly refuses to.

The three non-obvious directives that make the service work reliably:

ini
Type=forking
Environment="TERM=xterm-256color"
ExecStart=/usr/bin/tmux new-session -d -s claude-session

Why these specifically matter:

  • Type=forking — the tmux command forks a background process and exits. systemd needs to know the parent will not stay in the foreground, or it kills the service instantly.
  • Environment="TERM=xterm-256color" — systemd strips most environment variables. Claude Code's TUI needs TERM to render arrow-key menus, bold text, and the input box correctly. Miss this and permission prompts display as garbled escape sequences.
  • The tmux nesting — Claude Code needs a PTY (pseudo-terminal). Running it as a bare systemd process fails with "no TTY" warnings. Wrapping it in a detached tmux session gives it the PTY it needs AND lets you attach to watch it work.

Why nested tmux + systemd is the right architecture (not just systemd alone, not just tmux alone) is the kind of question that sends people down 4-hour debugging spirals. The complete service file with restart policies, logging config, and reboot-recovery behavior is documented in the Pro Track.

Once running, you attach to watch Claude work:

bash
sudo -u claude-user tmux attach -t claude-session

Press Ctrl+B then D to detach without killing the session.


The Telegram Channels Plugin

Claude Code plugins install via a single slash command inside an active Claude session. Once attached to the tmux session:

/plugin install telegram@claude-plugins-official

The plugin asks for three things:

  • Bot token — create a bot with @BotFather on Telegram. Free, 30 seconds.
  • Allowlist — a list of Telegram user IDs permitted to message the bot. Start with just yours.
  • Mode — "allowlist" (only listed users) vs "public" (anyone can message). Always use allowlist.

Get the Weekly IT + AI Roundup

What changed this week in NinjaOne, ServiceNow, CrowdStrike, and AI. One email, every Monday.

No spam, unsubscribe anytime. Privacy Policy

The plugin stores config in ~/.claude/channels/telegram/.env. The bot token goes there, file-permissioned to the claude-user only.

Once configured, Telegram messages to your bot flow into Claude Code automatically. No webhooks, no reverse proxy, no public endpoints required. The plugin uses Telegram's long-polling API by default.


The Auto-Permission Watcher

This is the piece no one documents clearly, and the biggest unlock for truly unattended operation.

Claude Code prompts for permission whenever a tool call requires it (Bash commands, file writes, certain MCP operations). Those prompts are inline in the terminal as arrow-key-navigable menus. In an unattended session, they block indefinitely until someone presses a key.

The pattern that solves it:

A small background script runs as its own systemd service, captures the tmux pane content every few seconds, matches against known permission prompt patterns, and sends approval keystrokes back into the tmux session.

In pseudocode:

loop forever:
  output = capture tmux pane
  if output matches a known permission prompt pattern:
    send approval keystroke to tmux session
  sleep 2 seconds

That's the core idea. In practice, three patterns need to be matched (the [y/n] menu, the "Do you want to proceed" prompt, and MCP-style tool call approvals), each with different keystrokes to send, plus logging, error handling, and graceful recovery when the tmux session is missing.

The hard parts not shown here:

  • Which regex patterns to match reliably without false positives
  • How to avoid approving the same prompt twice (race conditions between captures)
  • How to handle MCP-specific permission flows that use a different UI
  • How to log approvals for audit trails without filling the disk
  • How to fail safely when the tmux session dies mid-loop

This is risky by design. You're removing the human-in-the-loop checkpoint Anthropic built into Claude Code. Claude could run any tool it decides to run (subject to your settings.local.json deny list). Use allowlists aggressively, keep the deny list tight, never add untrusted Telegram users.

The production-ready watcher script, the systemd unit that supervises it, the settings.local.json deny list we use, and the incident response playbook for "the watcher approved something it shouldn't have" are all in the Pro Track.


Three Gotchas Worth Mentioning

1. Claude OAuth tokens expire silently

OAuth tokens on the Claude side currently expire after ~18 months. When they expire, the bot goes silent with no error in Telegram. Schedule a calendar reminder for rotation. Full rotation procedure and monitoring hooks are in the Pro Track.

2. `systemctl enable` is not automatic

systemctl start runs the service NOW. systemctl enable makes it start on boot. You need both. Miss the enable step and the service dies on first reboot. This is the single most common operational failure we've seen.

3. Message length limits

Telegram messages cap at ~4096 characters. Claude's longer responses get auto-split by the plugin. Threaded conversations with long replies can feel fragmented. Instructing Claude in a CLAUDE.md to keep Telegram replies under 3,500 characters helps.

The full operational gotcha catalog (stale tmux sockets after forced reboots, snapshot restore recovery, MCP permission persistence quirks, and the OAuth renewal monitoring playbook) is covered across Modules 3 and 6 of the Pro Track.


Security Considerations

Running Claude Code with auto-permission approval is powerful. It's also a delegated-access pattern with real risks. We mitigate with:

  • Allowlist mode only — only your own Telegram user ID can trigger the bot
  • Non-root user — Claude Code runs as claude-user, not root, so worst-case damage is confined to the user's home directory
  • No sudo access for claude-user — claude-user is not in the sudoers file
  • Dedicated VPS — not your daily-driver server. If the agent does something destructive, the blast radius is contained.
  • Regular audit of .claude/settings.local.json — verify no new unwanted MCP servers have been added
  • Rotate the bot token every 6-12 months
  • Monitor outbound traffic — alerts for unusual external API calls

Never run this setup with sudo privileges, never enable it on your personal workstation, and never add untrusted users to the allowlist.


Who This Is For

Developers who want to fire off coding tasks from their phone without opening a laptop.

Infrastructure engineers running Claude Code as an always-on automation agent responding to webhooks and alerts.

Content teams piping CMS events or analytics into Claude Code for async processing.

Anyone who's thought "I wish I could tell Claude to check on that deploy while I'm at lunch."

If you don't have a VPS, aren't comfortable with systemd, or don't want to manage Linux security, this setup isn't for you. Use Claude Code in its intended desktop form until you're ready for the operational overhead.


The Bigger Picture

Claude Code was designed for interactive use at a developer's workstation. We're using it as a 24/7 autonomous agent accessible from anywhere. That's a different operating mode from the intended one, and it carries different risks and rewards.

The reward: an AI agent that answers to your phone, works on your codebase, handles your webhooks, and never needs you to open your laptop.

The risk: auto-approval disables the human-in-the-loop. One bad prompt, one compromised allowlist, one misconfigured MCP server, and the agent does something you don't want.

We run this setup because the productivity gain is worth it to us. Your calculation may differ.


Get Started

Claude Code is free with an Anthropic API key or Claude Pro subscription. The Telegram Channels plugin is free and bundled with Claude plugins.

Want the full deployment playbook? Our Remote Claude Code Pro Track covers VPS hardening, systemd service orchestration, Telegram Channels plugin setup, the auto-permission watcher architecture, and operational security across 6 modules. Everything in this post is surface-level compared to the track.

Running n8n, Claude Code, and other services on the same VPS? Our n8n Administration Pro Track covers the multi-service deployment pattern end-to-end.

Not sure which AI stack fits your workflow? Take the free quiz and get matched in 2 minutes.