Deploy NanoClaw (OpenClaw Lightweight Alternative)
OpenClaw alternative with multi-channel, memory & Agents SDK built in
NanoClaw
Just deployed
/data
Deploy and Host NanoClaw on Railway
NanoClaw is a personal AI assistant powered by Claude that connects to messaging channels (Slack, Telegram, Discord, WhatsApp, Gmail). It runs Claude Agent SDK in isolated processes, giving each group its own memory, skills, and tools - including web browsing, file management, and scheduled tasks.
About Hosting NanoClaw
Deploying NanoClaw on Railway involves running a single Node.js service that spawns Claude Agent SDK processes for each incoming message. A persistent volume stores authentication state, SQLite databases, group memory, and conversation history. The service uses a multi-stage Docker build that bundles Chromium (for web browsing), the Claude Code CLI, and the agent-runner into one image.
All five channels (Slack, Telegram, Discord, WhatsApp, Gmail) are pre-installed. Set the env vars for the channels you want - channels without tokens are silently skipped at startup. No post-deploy setup commands needed.
Common Use Cases
- Personal AI assistant accessible from your phone or desktop: ask questions, search the web, browse pages, and manage tasks
- Group-aware assistant that maintains separate memory and context per chat group, with customizable triggers and behavior
- Scheduled task automation with recurring prompts (daily summaries, reminders, monitoring) running on cron schedules
Dependencies
- An Anthropic API key (
ANTHROPIC_API_KEY) - At least one channel's credentials (see Available Channels below)
Deployment Dependencies
- NanoClaw GitHub Repository
- Anthropic API for Claude access
Core Environment Variables
| Variable | Required | Description |
|---|---|---|
ANTHROPIC_API_KEY | Yes | API key for Claude model access |
ASSISTANT_NAME | No | Bot display name (default: Andy). Also used to auto-register the main group: on first startup, the bot looks for a chat matching this name and registers it as the main group. |
TZ | No | Timezone for scheduled tasks and log timestamps (default: UTC) |
GITHUB_TOKEN | No | GitHub personal access token for installing skills from private repos |
A Railway volume must be mounted at /data for persistent storage (auth state, SQLite, group files).
Auto-registration of the main group
On first startup, if no groups are registered yet, the bot syncs its chat list and looks for a group whose name matches ASSISTANT_NAME (case-insensitive). If found, it auto-registers that group as the main group (no trigger required, elevated privileges).
To use this:
- Set
ASSISTANT_NAMEto your bot's name (e.g.Andy) - Create a group in your messaging app with that exact name (e.g. a WhatsApp group called "Andy")
- Send at least one message in the group so the bot knows about it
- Deploy (or restart) - the bot finds the group and registers it automatically
Once registered, the group persists in SQLite across restarts. This only runs when there are zero registered groups.
Available Channels
All channels are pre-installed. Set the relevant env vars in Railway and restart - the bot auto-connects. Channels without credentials are silently skipped.
Slack
| Variable | Description |
|---|---|
SLACK_BOT_TOKEN | Bot User OAuth Token (starts with xoxb-) |
SLACK_APP_TOKEN | App-Level Token for Socket Mode (starts with xapp-) |
SLACK_MAIN_CHANNEL_ID | (Optional) Channel ID to auto-register as the main group |
How to get the tokens:
- Go to api.slack.com/apps and create a new app (from scratch)
- Socket Mode - enable it, generate an App-Level Token (
xapp-...) with scopeconnections:write - Event Subscriptions - enable and subscribe to bot events:
message.channels,message.groups,message.im - OAuth & Permissions - add scopes:
chat:write,channels:history,groups:history,im:history,channels:read,groups:read,users:read - Install to Workspace - copy the Bot User OAuth Token (
xoxb-...)
Setting the main channel:
Set SLACK_MAIN_CHANNEL_ID to the Slack channel ID where you want to manage your bot (your private admin channel). To find the channel ID, right-click the channel in Slack > "View channel details" > the ID is at the bottom of the modal (starts with C). When set, this channel is auto-registered as the main group on first startup — no need to match ASSISTANT_NAME.
Telegram
| Variable | Description |
|---|---|
TELEGRAM_BOT_TOKEN | Bot token from BotFather |
How to get the token:
- Open Telegram and message @BotFather
- Send
/newbot, follow the prompts to name your bot - Copy the token BotFather gives you
Discord
| Variable | Description |
|---|---|
DISCORD_BOT_TOKEN | Bot token from Discord Developer Portal |
How to get the token:
- Go to discord.com/developers/applications and create a new application
- Go to Bot - click "Reset Token" and copy it
- Enable Message Content Intent under Privileged Gateway Intents
- Go to OAuth2 > URL Generator - select scopes
bot, permissionsSend Messages+Read Message History- open the generated URL to invite the bot to your server
| Variable | Description |
|---|---|
WHATSAPP_PHONE | Bot's phone number with country code (e.g. +33612345678) |
Setup:
The bot needs its own WhatsApp account - use a secondary phone number, not the one you want to message the bot from. A cheap prepaid SIM or a virtual number works fine.
- Install WhatsApp Business on your phone and create an account with the secondary number
- Set
WHATSAPP_PHONEin Railway to that number (with country code, e.g.+33612345678) - Deploy on Railway - WhatsApp Business should send a notification to link a new device. Tap it to confirm
- If you don't get the notification, check
railway logsand search for "code" - you'll find an 8-digit pairing code. Enter it manually in WhatsApp Business > Settings > Linked Devices > Link a Device > Link with phone number
Auth state persists on the Railway volume. After initial pairing, the bot stays connected. WHATSAPP_PHONE is only needed again if the session expires.
Gmail
| Variable | Description |
|---|---|
GMAIL_CLIENT_ID | OAuth 2.0 Client ID from Google Cloud Console |
GMAIL_CLIENT_SECRET | OAuth 2.0 Client Secret |
GMAIL_REFRESH_TOKEN | OAuth refresh token |
How to get the credentials:
- Open Google Cloud Console - create a new project or select an existing one
- Go to APIs & Services > Library, search "Gmail API", click Enable
- Go to APIs & Services > Credentials, click + CREATE CREDENTIALS > OAuth client ID
- If prompted for consent screen: choose "External", fill in app name and email, add your email as a test user, save
- Application type: Desktop app
- Copy the Client ID and Client Secret
- Go to Google OAuth Playground
- Click the gear icon (Settings) > check Use your own OAuth credentials > enter your Client ID and Client Secret
- In Step 1: find Gmail API v1 > select
https://mail.google.com/> click Authorize APIs - Sign in and grant access (if you see "app isn't verified", click Advanced > Go to app)
- In Step 2: click Exchange authorization code for tokens
- Copy the
refresh_tokenfrom the response
Note: If your GCP app is in "Testing" mode, refresh tokens expire after 7 days. To avoid this, go to OAuth consent screen and click Publish App (no Google review needed for personal use - only test users you added can authorize).
Skills
Skills are reusable capabilities that extend what the agent can do — outbound email, signal detection, web scraping, etc. They're defined in GitHub repos as SKILL.md files and installed via skills.sh.
Adding skills
Tell your bot in chat:
add skills from arnaudjnn/gtm-skills
The agent calls the install_skills MCP tool, which runs npx skills add behind the scenes, copies the skill files into the container, and reports back with any required credentials. It accepts owner/repo shorthand or a full GitHub URL.
Required credentials
Some skills declare required environment variables (API keys, endpoints) in their SKILL.md frontmatter under inputs. After installing, the agent will ask you for any missing credentials and store them using set_env_var. Values are written to /data/.env on the persistent volume, so they survive deploys.
Auto-update on deploy
Every time the Railway service restarts (deploy, crash recovery, manual restart), NanoClaw reads data/skills-lock.json from the persistent volume and re-installs all registered skill repos via npx skills add. This means:
- Skills always pull the latest version from their source repo on every deploy
- No manual update step needed — push changes to the skills repo, redeploy NanoClaw, done
- Skills survive deploys — the lock file persists on the volume even though the app directory is ephemeral
Removing skills
To remove a skill repo, delete its entry from data/skills-lock.json on the persistent volume and redeploy. The skill files won't be re-installed on next startup.
How it works under the hood
install_skillsIPC task runsnpx skills add {repo} --all --copy --agent claude-codein a temp directory- Installed skills are copied from the temp
.claude/skills/intocontainer/skills/ - The repo is recorded in
data/skills-lock.json(persistent volume) - At startup,
syncSkillsOnStartup()loops through the lock file and re-runsnpx skills addfor each repo container/skills/is synced into each group's.claude/skills/before every agent invocation- Skill
inputs(env vars) are parsed from SKILL.md frontmatter and forwarded to agents as secrets
Differences from the Local (Docker) Setup
The upstream NanoClaw repository runs each agent invocation inside a Docker container, providing OS-level isolation between the host and the agent's filesystem. On Railway, Docker-in-Docker is not available, so agents run as child Node.js processes instead. Here's what changes:
| Feature | Local (Docker) | Railway |
|---|---|---|
| Agent isolation | Each agent runs in its own container with separate filesystem | Agents run as child processes sharing the host filesystem |
| Filesystem sandboxing | Container mounts restrict what the agent can read/write | Directory-based separation (no OS-level enforcement) |
| Resource limits | Docker CPU/memory limits per container | Railway service-level resource limits |
| Availability | Depends on your machine being on and connected | Always on - Railway keeps the service running 24/7 |
| Network | Requires stable home internet and open ports | Railway handles networking, SSL, and uptime |
Why This Is Fine for Personal Use
NanoClaw is designed as a personal assistant - you control who has access and what groups are registered. The agent already runs with --dangerously-skip-permissions (bypassing Claude Code's permission prompts), so container isolation is a defense-in-depth layer, not the primary security boundary. For a single-user deployment:
- You are the only one sending prompts - there's no untrusted input that could exploit the lack of sandboxing
- The agent only writes to its group folder - the Claude SDK's working directory is scoped to
/data/groups/{group}/ - Secrets are protected - API keys are passed via stdin and stripped from Bash subprocesses by a PreToolUse hook, same as Docker mode
- The real advantage is uptime - Railway keeps your assistant available 24/7 without needing an always-on home machine
If you need multi-tenant isolation or expose the bot to untrusted users, consider the local Docker setup instead.
Why Deploy NanoClaw on Railway?
Railway is a singular platform to deploy your infrastructure stack. Railway will host your infrastructure so you don't have to deal with configuration, while allowing you to vertically and horizontally scale it.
By deploying NanoClaw on Railway, you are one step closer to supporting a complete full-stack application with minimal burden. Host your servers, databases, AI agents, and more on Railway.
Template Content
NanoClaw
arnaudjnn/nanoclaw-railwayANTHROPIC_API_KEY
API key for Claude model access

