Untether your coding from your Laptop
My "coding with AI" progression is likely similar to your own:
- (2022) "legacy coding" =]
- (2023) ChatGPT assisted "copy-paste" coding
- (2024) GitHub Copilot tab-completion coding
- (2025) Coding agents in the terminal: Claude Code and OpenAI Codex
- (2026) YOU ARE HERE
We've all walked around holding our laptop with the lid half-closed, to give our coding agent just a few more minutes
When I found myself trying to type in a prompt at a stop-light while driving, I knew I had to upgrade to a better approach.
Here's a fast way to build a simple, always-on, coding agent in the cloud: 1 Dockerfile + 1 startup script + 2 Environment variables.
With this one step, human attention becomes the bottleneck
High Level Overview
Hosting environment + Coding Agent + API Keys
A coding agent is just a software program that repeatedly calls an LLM, has a "harness" to use the command line and tools, and writes/edits (text) code files.
https://blog.john-pfeiffer.com/ai-with-agents-aka-llms-with-tools/
Pre-requisite - a separate GitHub account
For the security best practice of "least privilege" and isolation I created a separate GitHub account for my coding agent(s).
The agent's github account is invited as a Collaborator to GitHub Repositories, and contributes via Pull Requests.
This creates clarity and audit trails of code authorship - including commit messages.
The human reviews, approves, and merges Pull Requests
Agent Access with Personal Access Tokens
The most painful part of many projects can be getting permissions correct. =|
When logged into your Agent's GitHub account:
GitHub -> Settings -> Developer settings (all the way on the bottom left) -> Personal access tokens -> Tokens (classic)
Permissions:
- repo (the token has full read/write to every repo the account can see)
- admin:org -> read:org (gh CLI expects it for org membership lookups)
Save the key in a password manager - it may look like ghp_...
Gotcha: do not use the newer tokens with fine grained access control because of a limitation for outside collaborators on a repo.
Or create an Organization, add your agents to your organization, manage their access to repositories that way.
Factory Droid as the Coding Agent
Factory is a startup that built their own agent and "cloud factory", and I'm leveraging their off-the-shelf capabilities that handle:
- the coding agent (harness)
- subscription plan for a LLM
- a dashboard UI to organize the prompts and history
- a relay/tunnel to stay connected to a container containing the coding agent
- manage multiple agents across multiple computers, including the option for a long running "mission" - out of scope for this post
After creating the account, the next step is to create an API key.
optionally you can install the droid coding agent locally onto your computer and experiment with it
Use https://app.factory.ai/settings/api-keys or in the Factory UI, bottom left corner, click on YOUR USERNAME -> Settings -> API Keys -> CREATE KEY
Key Name: droid-registration
Expiration: SELECT-A-DATE
Via the CLI https://docs.factory.ai/api-reference/service-accounts/create-a-service-account-api-key
Save the key in a password manager - it looks like fk-...
Rather than pay for Factory's managed compute/machine, and for more visibility and control, you can bring your own hosting: https://docs.factory.ai/cli/features/droid-computers-byom
I received a year's worth of credits for Railway and Factory as part of my subscription to the podcast and substack Lenny's Product Pass, my documented approach would cost $25 a month
Hosting with Railway
A newish hosting company is Railway who offer integration from GitHub commit to live deployment in production.
The perfect place to provide an isolated environment with CPU and RAM, local disk, and network.
Use the simplest infrastructure-as-code: a tiny GitHub repo with a Dockerfile, https://github.com/johnpfeiffer/railway-factory-agent
FROM debian:trixie-slim
RUN apt-get update && apt-get install -y \
bash curl wget git ca-certificates jq procps \
python3 python3-pip python3-venv \
nodejs npm \
build-essential \
ripgrep fd-find tree unzip zip sqlite3 \
&& rm -rf /var/lib/apt/lists/*
# Droid CLI https://docs.factory.ai/reference/cli-reference
RUN curl -fsSL https://app.factory.ai/cli | sh \
&& /root/.local/bin/droid --version
ENV PATH="/root/.local/bin:${PATH}"
# GitHub CLI https://github.com/cli/cli/blob/trunk/docs/install_linux.md#debian
RUN mkdir -p -m 755 /etc/apt/keyrings \
&& wget -nv -O /etc/apt/keyrings/githubcli-archive-keyring.gpg https://cli.github.com/packages/githubcli-archive-keyring.gpg \
&& chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" > /etc/apt/sources.list.d/github-cli.list \
&& apt-get update \
&& apt-get install -y gh \
&& rm -rf /var/lib/apt/lists/* \
&& gh --version
WORKDIR /workspace
COPY connect-droid.sh /usr/local/bin/connect-droid.sh
RUN chmod +x /usr/local/bin/connect-droid.sh
ENV DROID_COMPUTER_NAME=railway-agent
CMD ["bash", "-c", "echo \"debian trixie started $(date -Is)\"; exec connect-droid.sh"]
New Project and Service
Assuming you have created a Railway account etc.
https://railway.com/new/github , or use the UI with "Start a New project" -> GitHub -> Configure GitHub App
A popup: Install Railway App
Where do you want to install Railway App?
(Login if necessary to GitHub) Your-GitHub-Username "Configure"
GitHub.com "Confirm access" - Signed in as Your-GitHub-Username
The initial Integration (Applications) into GitHub is one step.
The principle of "least privilege" says to only give access to repos that are triggering Railway build and deploy...
Repository access -> Only select repositories
I chose my dedicated repo... click on "johnpfeiffer/railway-factory-agent"
The UI shows you: "Building (00:01)" , clicking on it gives you details and the ability to "View logs" (Build Logs and Deploy Logs)
https://docs.railway.com/services
Environment Variables
As it builds use the UI to manually set the following Environment variables:
FACTORY_API_KEY
AGENT_GH_TOKEN
Set AGENT_GH_TOKEN to be the github classic PAT for your Agent's GitHub account.
Railway has a default GH_TOKEN environment variable in the container that assumes from the github repository owner. I do not want my agent using that GitHub, so the script overrides it.
Press the big purple "DEPLOY" button to apply changes (it will terminate any previous container and re-build and re-deploy).
Railway CLI
If you prefer using the CLI to manage everything - especially once you have the github integration and repo permissions configured...
railway login --browserless
railway whoami
railway list
railway init
Select a workspace
Project Name MYEXAMPLE
Created project MYEXAMPLE on My Projects
railway link
> Select a workspace My Projects
> Select a project MYEXAMPLE
> Select an environment production
Project factory-example linked successfully! ๐
railway add --repo johnpfeiffer/railway-factory-agent
> What do you need? GitHub Repo
> Enter a repo johnpfeiffer/railway-factory-agent
> Enter a variable <esc to skip> FACTORY_API_KEY=fk-...
> Enter a variable <esc to skip> AGENT_GH_TOKEN=...
> Enter a variable <esc to skip> DROID_COMPUTER_NAME=railway-agent
? Enter a variable <esc to skip> <cancelled>
Enter a service name factory-agent
Add a Volume
In the UI right click near your service to "Add New Service" -> Volume
clearly a lot of future interesting options here with Database, Function, etc.
The Railway Volume to the service with mount path /root/.factory.
This persists the machine's identity and registration across resuming connection and redeploys; re-registration a harmless no-op.
Without it, every fresh container has an empty ~/.factory and tries to re-claim the same name, producing the error:
Computer with name "MYAGENT" already exists
Click on the Purple button "Deploy" - it will
Ephemeral by default
A key concept is that this is ephemeral and uses infrastructure as code to deploy.
So there's no "pause" - you "remove" a container and it is gone. Only what was in a mounted volume is persisted.
Sometimes you have to use the Factory UI to also delete the Droid Computer to reset state and re-sync things.
The shim script to make it work
A few things have to happen to make everything work, so on deploy and start the container runs connect-droid.sh:
- Ensures the droid binary is in the PATH
- Ensures the Factory API key is set
- Ensures the Railway default
GH_TOKENenvironment variable is overridden with the Agent's GitHub (PAT) Token. - Sets the computer name (which propagates to Factory)
- some debugging commands for the logs ;)
droid computer register "$COMPUTER_NAME" -y || true- the droid CLI connects with the API key to Factory for "bring your own machine"droid daemon --remote-accessruns in the foreground as the container's main process - and connects to Factory's central relay
Note that the GH_TOKEN which is used by the gh command line tool should be the agent's token, AGENT_GH_TOKEN
#!/bin/bash
set -uo pipefail
# droid is installed here; ensure it's on PATH regardless of how we're invoked
export PATH="/root/.local/bin:${PATH}"
if [ -z "${FACTORY_API_KEY:-}" ]; then
echo "ERROR: FACTORY_API_KEY is not set (generate at https://app.factory.ai/settings/api-keys)" >&2
exit 1
fi
if [ -n "${AGENT_GH_TOKEN:-}" ]; then
export GH_TOKEN="$AGENT_GH_TOKEN"
gh auth status || true
fi
# name this Droid Computer (defaults to the container hostname)
COMPUTER_NAME="${DROID_COMPUTER_NAME:-$(hostname)}"
echo $COMPUTER_NAME
which droid
droid computer register "$COMPUTER_NAME" -y || true
# Connect to Factory's relay so this machine is reachable as a Droid Computer
droid daemon --remote-access
Under the hood
The container running in Railway (aka Factory BYOM) connects out to Factory's relay (relay.factory.ai) when you run droid daemon --remote-access, so no inbound ports are required.
โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ โญโโโโโโโโโโโโโโโโโโโฎ
โ Railway container (Debian, headless, no TTY) โ โ Factory โ
โ โ โ โ
โ FACTORY_API_KEY โโโถ authenticate to Factory โ โ โ
โ droid computer register factory-droid-agent โโโโโโโโถโ relay.factory.ai โ
โ droid daemon --remote-access โโโโโโโโโโโโโโโโโโโโโโโโถโ (outbound only) โ
โ โ โ โ
โ /root/.factory โโโ persisted on a Volume โ โฐโโโโโโโโโโโโโโโโโโโฏ
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
You can use Railway's SSH or Console to have a (root) shell into the container: ls -ahl /root/.factory
Factory UI
To double check that your deployment is working, log into Factory https://app.factory.ai/sessions
At the Bottom left corner, click on YOUR USERNAME -> Settings -> Droid Computers
YOUR COMPUTERS
Machines you have registered to use with Droid. See the BYOM guide to register another machine.
ACTIONS -> Go to Dashboard
Daemon
Version: 0.162.1
Note that instead of updating the agent and modifying state, I would recommend one-click and redeploying the Railway container
You can use the Railway mobile app to redeploy the container - there can be a "is this a clean state" question hopefully resolved by the Railway volume, but it should "just work".
Prompt to Code
It is a simple chat web UI - in the center you can select a computer (i.e. railway-agent)
Select your model. I have found using the open weight (only uses .55x quota) (Droid Core) GLM 5.2 at reasoning level "max" and "Auto High" autonomy to be very effective.
Give it a prompt:
Clone the repo https://github.com/johnpfeiffer/railway-factory-agent and tell me what it does
The droid coding agent will use the gh command line tool and take care of the rest.
Use the browser on your phone to login to https://app.factory.ai/ - continue the conversation and have it work for a few minutes or an hour.
Specifically ask it for Pull Requests - then use the GitHub mobile app to Review and Approve. =)
Creativity
More than coding, you can ask the agent to summarize a websitedownload things, install packages, analyze data.
Usage
You can see your Factory usage limits, the now-familiar rolling "5 hour" window, weekly limit, and Monthly.
Factory -> Settings -> Usage
References
My previous posts on the topic of agents in the cloud:
- https://blog.john-pfeiffer.com/what-a-security-hackathon-taught-me-about-agents-in-the-cloud/
- https://blog.john-pfeiffer.com/maximum-leverage-and-minimum-ops-with-google-cloud-run-and-the-jules-coding-agent/
Factory's older documentation on an even more granular do-it-yourself approach https://docs.factory.ai/guides/building/droid-vps-setup
As a further aside, you can Bring Your Own Key (for your preferred LLM vendor/provider) https://docs.factory.ai/cli/byok/