Run Conductor in the cloud — a guide
One Ironway project, one machine in the cloud running Conductor, set up from your own computer with one command. Written for someone who can use GitHub and a terminal and has never run a server. The reference behind each step is a dedicated cloud host and
deploy/opentofu/; this page is the walk through, in order, with what went wrong on the first real host and how each problem now shows up.
conductor host new widget-1
It asks for what it cannot find: which of your Ironway projects the machine works, and a Hetzner API token. That makes the machine, waits while it installs itself, and ends with the machine's setup questions in the same terminal. The rest of this page is what happens in between and what those questions are.
What you get, and why a separate machine
Conductor runs Claude sessions that write code, run tests and open pull requests, and it runs them with permission prompts turned off so nobody has to sit and click "allow". That is safe on a machine that holds one project's repository and nothing else, and it is not safe on the laptop that holds your documents. So the machine is a small cloud server, one per project, made and destroyed with one command, and it never sees your files.
| on the machine | what it is | needed for the first test |
|---|---|---|
| Conductor | claims work from Ironway, runs a session per piece, opens the pull request | yes |
| TeamClaude | pools several Claude Max accounts and moves to the next when one runs low | on by default, recommended |
| Tower | lets several sessions work on one PRD at once (a "swarm") | later (--tower) |
| Roundhouse agent | runs CI for the project on the same machine | later (--roundhouse <url>) |
Before you start
- An Ironway project with a connected GitHub repository and at least one approved PRD. Note its workspace and project slugs, the two words in its dashboard address
/dashboard/t/<workspace>/projects/<project>. They are not the GitHub owner and repository name. A Hetzner Cloud account and an API token: the Hetzner console, your project, Security → API tokens, Read & Write. Use a Hetzner project that holds only these machines, because a token reaches every server in its project.
conductor host newlooks in this order and never saves it, because agent sessions run as your own user and could read any file or unlocked keychain you could:- An exported
TF_VAR_hcloud_tokenorHCLOUD_TOKEN. Do not export either in the shell that startsconductor serve. hetzner_token_commandin the config, any command that prints it.- 1Password, if
opis installed: see below. - A prompt with the input hidden. A script with no terminal must use 1 or 2.
- An exported
Optional: tokens in 1Password. Make a vault called
Ironway, or name another withonepassword_vaultin the config (noneturns this off). If you are signed in to several 1Password accounts, setonepassword_accountto the one holding the vault, using a user ID fromop account list. Add any of these as API Credential items, with the token in thecredentialfield:item used for Hetznermaking and destroying the machine Claudethe machine's CLAUDE_CODE_OAUTH_TOKENGitHubthe machine's GH_TOKEN; use a token for its own GitHub accountGitLabthe machine's GITLAB_TOKEN, for a project on GitLabIronwaythe machine's IRONWAY_API_TOKENEach item can also belong to one machine:
GitHub tower-1is tried beforeGitHub, and the same goes for the others. Give each machine its ownGitHuborGitLabitem when it has its own account, and its ownHetzneritem when it lives in a different Hetzner project. If only a sharedGitHubitem exists, the command shows which account that is and asks before using it. A forge token you paste is saved under the machine's name.You can skip making the items. Before it creates anything, the command checks the vault, asks on your Mac for whatever is missing, shows which GitHub account a pasted token belongs to, and offers to save each one. Enter skips a token, and the machine's setup asks for it instead. It never replaces an item that exists, and the vault must exist first (
op vault create Ironway).--envreplaces the last three. A token in the vault only stays out of an agent's reach if 1Password asks you to approve the read. Turn on Settings → Developer → Integrate with 1Password CLI, then check thatop readfrom a new terminal prompts you.- An SSH key on your Mac.
conductor host newuses the agent your~/.ssh/confignames (a 1PasswordIdentityAgentis found with no setup), then the default agent, then~/.ssh/*.pub. It takes the key namedid_ed25519, else the first ed25519 key, prints which, and--ssh-keynames another. If you use 1Password, turn on Settings → Developer → Use the SSH agent and point~/.ssh/configat the socket path it shows you. There is no password for the server, ever; the key is how you get in. - A Claude token: on your Mac,
claude setup-token, and keep what it prints. It is a subscription token, not an API key. - OpenTofu and Conductor on your Mac:
brew install opentofu, andcurl -fsSL https://ironway.work/install/conductor.sh | sh.
1. Make the machine
conductor host new widget-1
widget-1 is the server's name. Add --project my-workspace/my-project to skip the question. What the command does, in order:
- Lists the projects your Ironway login reaches and asks which, unless
--projectnamed them. Refuses straight away iftofuis not installed, then asks for the Hetzner token if it has none. - Finds your ssh key and asks Hetzner whether your project already holds it, so it is never registered twice (Hetzner refuses a second copy).
- Writes an OpenTofu setup for this one machine under
~/.switchyard-conductor/hosts/widget-1/and runstofu apply. The 8-core machine (ccx33) is billed while it exists. Hetzner repriced this line in 2026, so check the console's current price before you make one, or choose a size with--server-type. - Waits while the machine installs itself, printing its install log as it goes. The machine installs the published Conductor binary (checked against its published checksum) and runs
conductor host bootstrap, which adds the GitHub CLI, the agent CLIs and TeamClaude. Nothing is cloned and nothing is compiled. - Opens the machine's setup questions in your terminal (step 2 below).
The machine keeps itself current. Its conductor follows the published release (--auto-update=false turns that off, and --binary implies it, because an update would replace the build you uploaded), and Ubuntu installs its own security updates.
Useful flags: --project again for a second project on the same machine, --tower, --roundhouse https://rails.switchyard.work, --teamclaude=false, --server-type, --location, --no-setup to stop before the questions, and --env FILE (step 2). conductor host new -h lists them all.
2. Answer the setup questions
The questions come from conductor host setup, running on the machine. It asks only for what it cannot find, and can be run again at any point:
- The Claude token you made on your Mac. Paste it.
- GitHub. It prints a code. Open
https://github.com/login/deviceon your Mac and enter it; the server cannot open a browser itself. - Tower, if you asked for it: built now that GitHub is signed in.
- Ironway. It prints a link; open it and approve the machine. It then registers every project and clones its repository.
- The verify command for each project, the check a build must pass before its pull request opens. When the project declares one on Ironway (its settings' verify command) setup just shows it: every lane runs Ironway's, so there is nothing to answer.
conductor project commands <workspace/project>prints what Ironway declares. Only for a project that declares none does setup suggest one from the repository; for a large Go project the short form (go build ./... && go vet ./... && go test -short -timeout 30m ./...) keeps each build to minutes. - TeamClaude accounts.
teamclaude loginonce per Claude Max account; use a private browser window for each extra account. Your Claude token from question 1 is still needed: theclaudeprogram checks its own login before the pool ever sees a request. - Roundhouse, if you asked for it: a code to approve on its Agents page.
- Doctor, which lists anything left, then start.
Fewer questions. The three tokens can be written ahead of time into a file on your Mac, one NAME=value per line, and handed over with --env:
cat > ~/widget-1.env <<'TOKENS'
CLAUDE_CODE_OAUTH_TOKEN=... # from claude setup-token
IRONWAY_API_TOKEN=... # optional; else the browser approval in question 4
GH_TOKEN=... # optional; a token for the machine's own GitHub account
TOKENS
chmod 600 ~/widget-1.env
conductor host new widget-1 --project my-workspace/my-project --env ~/widget-1.env
The file travels only over the ssh connection, into the machine's own protected settings; it is never put in the machine's definition, in OpenTofu's state or in a log. Questions 1, 2 and 4 are then answered from it and the first pass runs without you. What no file can answer is a browser approval: one teamclaude login per Claude Max account, and whatever token you left out. Those are asked next, in the same terminal.
Give the machine its own GitHub account
Do this before the setup questions if you can. With your own account, the machine's pull requests look like yours, it shares your 5,000 requests an hour, and its credential can reach everything you can.
- Create a GitHub account for this machine, such as
conductor-widget-1. - Invite it as a collaborator with Write on each repository the machine works, and accept the invitation as that account.
- Make a token on that account. If an organization owns the repositories, use a fine-grained token scoped to them. If a personal account owns them, use a classic token with
repoandworkflow: GitHub does not let a fine-grained token reach a repository as a collaborator. Set an expiry you will renew. Give it to the machine. Answer the GitHub question with
GH_TOKENin--env, or put it in 1Password asGitHub. On a machine already running:printf '%s' "$TOKEN" | conductor github loginThen restart
serve. It checks the token with GitHub before saving it.
conductor doctor then shows github acts as conductor-widget-1, and a github-push line per project. The full permission list is the conductor's own account.
A project on GitLab
A project on GitLab, gitlab.com or your own server, works the same way. Setup asks Ironway which forge each project is on, signs in to that instance with glab, and points git at that login. It skips the GitHub question unless a project is on GitHub or you asked for --tower, because Tower's source is on GitHub.
Give the machine its own account on that GitLab, and make a personal access token with the api scope. Put it in --env as GITLAB_TOKEN, or in the vault as an item named GitLab. Without one, setup runs glab auth login in your terminal. One token covers one GitLab server, so a machine on a second server asks again.
Before you make the machine, check two things about a self-hosted GitLab: that it accepts connections from a cloud server, and that your organization allows its code on one.
A machine that only runs CI
conductor host new conductor-ci-1 --role ci --ci-pipeline conductor
That makes a host with the Go toolchain, Node, Python and a Roundhouse agent, and no conductor service, sessions, Tower or TeamClaude. Setup asks only for GitHub and the Roundhouse approval. It runs two jobs at once, and --roundhouse-spawn changes that.
--ci-pipeline is what keeps the machine to that one pipeline's jobs. Roundhouse agents share a queue, and a step's queue: "*" reaches all of them, so an agent with no pipeline would take other projects' jobs and fail every clone its GitHub account cannot make. A conductor host with --roundhouse can use it too. The tag needs a Roundhouse server new enough to honour it, so check that first: an older server ignores it.
Give the machine its own GitHub account with Read access to that one repository, and put its token in the vault as GitHub conductor-ci-1. The Roundhouse server posts the statuses, so it needs no write access. A shared GitHub item is never used on a CI machine.
Treat everything on this machine as readable by CI. Jobs run as the same user as the agent, so a job can read that token and the machine's Roundhouse credential, and a job comes from whatever branch or pull request starts it. The account's narrow access is what limits the damage: the token reaches one repository, read-only, and the server refuses the credential any tag it was not approved with. Keep nothing else on the machine, and run only pipelines you trust equally on it. If a credential may have leaked, revoke the host on the Agents page and revoke the token on GitHub.
3. See it working
From your Mac:
conductor tunnel ops@<the address>
conductor host list prints the address. It opens the machine's dashboard at http://127.0.0.1:18095, and TeamClaude's account pool is at http://127.0.0.1:13456/teamclaude/dashboard. Ctrl-C closes it. Without the conductor command, the same thing by hand:
ssh -N -L 18095:127.0.0.1:18095 -L 13456:127.0.0.1:13456 ops@<the address>
The machine uses 18095 and 13456 on purpose, so a Conductor and TeamClaude on your own Mac (8095 and 3456) keep running alongside.
The machine also shows up on Ironway under Account → Conductors, where it can be paused and resumed. The finish line for a first test is a pull request on the project's repository opened from a conductor/<install-id>/… branch.
4. When you are done
conductor host destroy widget-1 removes the machine, and then its OpenTofu setup on your Mac. Nothing on Ironway is lost; a new machine signs in and picks up where the old one stopped.
How long it takes
The figure this section records is the time from tofu apply starting to bootstrap done in /var/log/conductor-bootstrap.log, on a ccx33 host running Conductor and TeamClaude (--teamclaude, the default; no --tower, no --roundhouse), made from PRD #1081's branch against the private conductor repository, with zero operator commands typed between the apply and conductor host setup.
| measured | value |
|---|---|
tofu apply → bootstrap done | TODO(prd-1081): not yet measured — a person must make the host and fill this in |
| operator commands between apply and setup | TODO(prd-1081): expected 0, to be confirmed on the live host |
| host, location, date | TODO(prd-1081) |
No number is written here until a live host has produced one. To measure it: run conductor host new <name> --project <ws/proj> --no-setup from this branch, note when tofu apply starts printing, and read the timestamp of the bootstrap done line (conductor host new also prints a ready line with the time since it started, which includes tofu init and is therefore a little longer). The step the binary boot removed, a Go install and a source build, took ten to fifteen minutes on the first host, which itself never reached bootstrap done unattended: its clone of the private repository failed.
Troubleshooting
Every one of these happened on a real host, and each now either cannot happen or says so plainly.
| what you see | why | what to do |
|---|---|---|
HCLOUD_TOKEN is not set | no token was found and there is no terminal to ask on | run it from a terminal, or set hetzner_token_command |
Hetzner refused | the token is wrong or revoked | make a new one in the Hetzner console |
tofu is not on PATH | OpenTofu is not installed | brew install opentofu |
no ssh public key found | no key in the agent your ~/.ssh/config names, the default agent or ~/.ssh | ssh-keygen -t ed25519, or --ssh-key path/to/key.pub |
| ssh asks for a password | your Mac did not offer the key | turn on the 1Password agent (above), or --ssh-key the key ssh offers |
bootstrap NOT run: no conductor binary was installed | the download origin did not answer for the whole retry window | ssh ops@<the address> sudo sh /var/lib/cloud/instance/scripts/runcmd |
bootstrap NOT run: the installed conductor has no conductor host command | the published release predates the one-command host | wait for the next release, or try your build with --binary (OpenTofu README) |
no bootstrap done ... after 30m0s | the install is slow or stuck | it printed the last 40 log lines; --timeout 45m waits longer, and running conductor host new again resumes |
switchyard named no repository | the project was given as GitHub owner/repo | the Ironway workspace/project; setup asks again |
Failed to connect to bus | sudo -iu gives a shell without its systemd session | fixed in setup; by hand, export XDG_RUNTIME_DIR=/run/user/$(id -u) first |
| Roundhouse: "no place to enter a code" | approving needs the Roundhouse admin token | leave --roundhouse off until you have it |
| the swarm is refused, "does not run inside Forge" | swarms need Tower | expected without --tower; single beads still build |
By hand
conductor host new is three steps an operator can still take one at a time, for a provider without a module or a setup it does not cover.
- The machine. From a checkout,
deploy/opentofu/examples/hetzner:cp terraform.tfvars.example terraform.tfvars, put your ssh key inssh_public_keys(or its Hetzner name inexisting_ssh_keys) and your project's slugs in aprojectsentry, thentofu init && tofu applyandtofu output hosts. Or pastescripts/systemd/cloud-init.yamlas any provider's user data, with your key and project filled in. - The wait.
ssh ops@<the address>, thentail -f /var/log/conductor-bootstrap.loguntilbootstrap done. - The questions.
sudo -iu conductor conductor host setup(add--yesto take every detected default,--env FILEfor the tokens). Paste multi-line blocks one at a time: a command that opens a new shell or asks a question swallows the lines pasted after it.