Skip to content

docs: add one-click Railway deploy - #346

Open
will-bogusz wants to merge 1 commit into
CopilotKit:mainfrom
will-bogusz:docs/railway-deploy-button
Open

docs: add one-click Railway deploy#346
will-bogusz wants to merge 1 commit into
CopilotKit:mainfrom
will-bogusz:docs/railway-deploy-button

Conversation

@will-bogusz

Copy link
Copy Markdown

Summary

Adds a Deploy on Railway button under "Deploy it" (railway.com/deploy/openbot). The template runs the official ghcr.io/copilotkit/openbot:v0.0.5 image beside a Railway Postgres 18 (pgvector included), plus three services the single image does not carry on a PaaS: agent-langgraph as the managed Bot so New coworker works, a cron service running scripts/fire-routines.ts so routines fire, and a small Caddy in front of the public URL so a single-user deployment is not an open administrator with a shell. Everything is wired over the private network with every secret generated. The inputs are an OpenAI key and the two CopilotKit Intelligence credentials the README already tells people to get; everything else is generated or wired.

What the template does

  • EMBEDDED_POSTGRES=off, DATABASE_URL → the bundled Postgres; migrations run at container start via server/scripts/migrate.ts (single replica), and the API is only started after they succeed.
  • KEY_ENCRYPTION_KEY, COMPUTER_TOKEN, MANAGED_AGENT_TOKEN, AGENT_TOOL_TOKEN and WORKER_SHARED_SECRET generated per deployment; OPENBOT_PUBLIC_URL set from Railway's domain; health check on /health.
  • Agent: agent-langgraph built unchanged from this repo's v0.0.5 tag (agent-langgraph/Dockerfile, context .) because no image is published for it; MANAGED_AGENT_AG_UI_URL points at it over the private network and OPENBOT_TOOL_URL points back at /api/agent-tools/call. BOT_PROVIDER=openai by default, sharing OPENAI_API_KEY; the deploy form documents the anthropic/google path.
  • Routines: the same openbot image as a Railway cron service (*/5 * * * *) running timeout -s KILL 240 bun scripts/fire-routines.ts with the server's env referenced from the OpenBot service, matching charts/openbot/templates/routines/cronjob.yaml.
  • OPENAI_BASE_URL and the OAuth variables are optional and documented in the deploy form; one volume at /data holds both WORKSPACE_DIR and PROFILES_DIR, so Bot files and browser logins survive redeploys.
  • Gate: caddy:2-alpine holds the only public domain and basic_auths every request (generated GATE_PASSWORD, /health exempt) before reverse_proxy to OpenBot over the private network; OpenBot has no public address. Railway's generated domains are <service>-<env>-<4 hex>.up.railway.app, so an unauthenticated OPENBOT_SINGLE_USER=true deployment would be enumerable — the gate is the cheapest fix that works from a phone. OPENBOT_PUBLIC_URL and the OAuth redirect URIs use the Gate's domain.

Two platform notes you may want in docs/deployment.md

  1. Railway's runtime does not run the image entrypoint as PID 1 (containers start under /run/podman-init), so s6-overlay's /init exits with s6-overlay-suexec: fatal: can only run as pid 1 — the same failure reported on Fly Machines in NousResearch/hermes-agent#38349. The template sets a start command that runs the three s6 services directly (migrate.ts, agent-computer, api) with /command/s6-setuidgid pwuser, unchanged otherwise. A PID-aware /init shim upstream would let the template drop that override.
  2. Because Railway replaces the ENTRYPOINT with the start command, the same image runs cleanly as a one-shot cron container — no second server, no Chromium — which is a simpler shape than a long-lived worker/ for platforms without CronJobs.

Verified (2026-09-03, fresh deploy from the template)

  • railway deploy -t openbot with only OPENAI_API_KEY, INTELLIGENCE_API_KEY and COPILOTKIT_LICENSE_TOKEN → five services healthy in under a minute; GET /health 200 through the gate unauthenticated, / 401 without credentials and 200 with them; without the three inputs the deploy refuses.
  • Deploy log shows migrations-applied before agent-computer listening and OpenBot server listening.
  • General Assistant chat, browsing (navigate example.com — Allowed in the audit), shell as pwuser, and a coworker created with an empty endpoint answering through agent-langgraph; a governed tool call round-tripped through /api/agent-tools/call (mcp.call_succeeded — routines/list_routines — Scout).
  • A 15-minute routine fired within seconds of its schedule (routine-sweep … fired: […]) with zero routines.dispatch_refused; a deliberately wrong WORKER_SHARED_SECRET produced exactly one mismatch refusal.
  • General Assistant → Knowledge and → coworker handoffs via message_bot, answers landing in the addressed Bot's channel as documented.
  • Files under /data/workspace and the browser profile survived a redeploy; health probes stayed 200 across the cutover.

Disclosure

I maintain the template; the link carries my Railway referral code (the repo currently has no Railway link). Happy to drop it, or to move this to a docs/deployment.md platform note instead of the README if you prefer. I also build and host the agent-langgraph image (ghcr.io/will-bogusz/openbot-agent-langgraph, workflow in my fork) — if you would rather publish it from this repo I will switch the template to yours.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant