Canadian KVM VPS in Montréal from 8.50 CAD/month

Codex Cloud, with any AI agent

A fresh machine per task, a ready environment, secrets kept in place, a PR to review: Codex Cloud's building blocks, assembled with the agent and model of your choice, over MCP or with a fifty-line script.

Three AI agents of different shapes each start a task on its own machine, which ends in a pull request

OpenAI has launched Codex Cloud: you describe a task, Codex works on it on its own machine while your computer sleeps, and you come back to review a pull request. The idea is good. It is also tied to one agent (Codex), one account (ChatGPT) and one code host (GitHub).

The building blocks themselves are simple: a fresh machine per task, a ready environment, secrets kept in their place, a result you review. This article puts them together with any command-line agent and any model, on an FFxF machine rented by the hour in Montréal. Two routes: your agent delegates on its own over MCP, or a fifty-line script starts the task without you.

1. What Codex Cloud does

According to its documentation, Codex Cloud rests on five ideas:

  • A published environment: repositories, dependencies and tools, prepared once, with an install script.
  • An isolated workspace per task, drawn from that environment: two tasks never step on each other.
  • Work goes on without you: the computer can sleep.
  • Network secrets: a proxy substitutes the real value for allowed domains, the process only sees a placeholder.
  • Review: you read the changes, ask for a follow-up, open the PR.

2. The same building blocks, unbundled

None of these ideas needs a closed product. Here is the equivalent of each one with an FFxF machine.

  • Published environment → the coding-agent image (Ubuntu, Docker, git, gh, Node.js, Python and uv, Claude Code, Codex CLI) plus a .agent/setup.sh in the repository.
  • Isolated workspace per task → one KVM machine per task, with root and Docker, destroyed at the end.
  • Goes on without you → the machine runs in Montréal; the task starts from a script, a CI job or your agent.
  • Network secrets → narrowly scoped keys, sent over standard input, erased with the machine.
  • Review and PR → a pushed branch and a draft PR, opened by gh.
  • The agent, Codex → Codex, Claude Code, or any agent that installs from the command line.

A Nano (1 vCPU, 2 GB) costs 0.018 CAD an hour, and a machine created and destroyed within the hour costs exactly that. For a heavy build, pick a bigger plan: prices are in Plans and regions.

3. First route: your agent delegates

If you already work with an agent on your own computer, the shortest route is to give it the tools to rent its own machine. The FFxF MCP server (https://ffxf.net/mcp) installs in Claude Code, Codex, Cursor and the other MCP clients; this sentence is enough for the agent to install it itself:

prompt
Install the FFxF MCP server by following https://ffxf.net/agents.md. My API key is in the FFXF_API_KEY environment variable.

Then add the block from Remote mode to the project's AGENTS.md or CLAUDE.md: when a task needs a real machine, the agent prices it, orders it, works on it over SSH and destroys it. The call-by-call demo shows a real run: delivered in 42 seconds, destroyed in 10, 0.018 CAD.

This route keeps a human in the loop, but it needs an open session. To hand off a task and close the laptop, you need the second one.

4. Second route: one task, one machine, one PR

The script below does for one task what Codex Cloud does: it orders a machine, clones the repository on it, runs the agent unattended, pushes a branch, opens a draft PR, and destroys the machine, whether the task succeeded or not.

It needs, in the environment:

  • FFXF_API_KEY: an FFxF key limited to vms.read, vms.create and vms.destroy, created under Account → API keys.
  • GH_TOKEN: a fine-grained GitHub token limited to the repository, with Contents and Pull requests write access.
  • ANTHROPIC_API_KEY or OPENAI_API_KEY, depending on the agent.
  • Your public key ~/.ssh/id_ed25519.pub, already added to the FFxF account.
bash
#!/usr/bin/env bash
# ffxf-task: one agent task on a fresh machine, returned as a PR, then the machine is destroyed.
# Usage: ffxf-task owner/repo "the task" [claude|codex]
set -euo pipefail
REPO=$1 TASK=$2 AGENT=${3:-claude}
API=https://api.ffxf.net/v1
AUTH="Authorization: Bearer $FFXF_API_KEY"
NAME="task-$(date +%s)"
KEY=$(ssh-keygen -lf ~/.ssh/id_ed25519.pub | awk '{print $2}')

# 1. Order: Nano, coding-agent image, hourly, SSH key only.
BODY=$(jq -n --arg h "$NAME" --arg k "$KEY" '{plan: "nano", region: "montreal",
  image: "coding-agent", hostname: $h, billing: "hourly",
  password_delivery: "none", ssh_keys: [$k]}')
VM=$(curl -sf "$API/vms" -H "$AUTH" -H "Content-Type: application/json" \
     -H "Idempotency-Key: $NAME" -d "$BODY" | jq -r .data.vm.id)

# Whatever happens next, the machine is destroyed when the script exits.
trap 'curl -sf -X DELETE "$API/vms/$VM?confirm=$NAME" -H "$AUTH" >/dev/null && echo "$NAME destroyed"' EXIT

# 2. Wait for delivery, then for SSH.
until [ "$(curl -sf "$API/vms/$VM" -H "$AUTH" | jq -r .data.status)" = running ]; do sleep 5; done
IP=$(curl -sf "$API/vms/$VM" -H "$AUTH" | jq -r .data.ipv4)
SSH=(ssh -o StrictHostKeyChecking=accept-new "ubuntu@$IP")
until "${SSH[@]}" true 2>/dev/null; do sleep 3; done

# 3. Secrets go through stdin: not on a command line, not in a history.
"${SSH[@]}" 'umask 077; cat > ~/.agent.env' <<EOF
GH_TOKEN=$GH_TOKEN
ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
OPENAI_API_KEY=${OPENAI_API_KEY:-}
EOF

# 4. The work, on the machine.
"${SSH[@]}" "bash -s -- $(printf '%q ' "$REPO" "$NAME" "$AGENT" "$TASK")" <<'EOF'
set -euo pipefail
set -a; . ~/.agent.env; set +a
gh auth setup-git
git config --global user.name "Agent FFxF"
git config --global user.email "agent@users.noreply.github.com"
gh repo clone "$1" ~/work/repo && cd ~/work/repo
git switch -c "agent/$2"
[ -x .agent/setup.sh ] && .agent/setup.sh
case "$3" in
  claude) claude -p "$4" --dangerously-skip-permissions ;;
  codex)  printenv OPENAI_API_KEY | codex login --with-api-key >/dev/null
          codex exec --dangerously-bypass-approvals-and-sandbox "$4" ;;
esac
git add -A
git diff --cached --quiet && { echo "No changes."; exit 0; }
git commit -qm "$4"
git push -qu origin HEAD
gh pr create --draft --fill
EOF

Run it like this, and run several at once if you like:

bash
chmod +x ffxf-task
./ffxf-task my-org/my-repo "test_invoice_rounding fails: find the cause and fix it" &
./ffxf-task my-org/my-repo "Add pagination to GET /customers, with tests" codex &
wait

Each call gets its own machine, just as each Codex Cloud task gets its own workspace. The default quota is five machines per account.

5. What matters in this script

  • The trap is set as soon as the machine exists. Agent failure, SSH drop, Ctrl+C: the machine is destroyed when the script exits. A forgotten machine is the only real source of spending.
  • The FFxF key never leaves your computer. The machine gets a GitHub token limited to one repository and the model key, nothing that lets it order or destroy anything.
  • Secrets go through standard input, into a 600 file: they show up neither in the process list nor in a history, and they vanish with the disk. Codex Cloud does better here (the process never sees the real value); in this setup, cap spending at the model provider.
  • --dangerously-skip-permissions and its Codex equivalent are reasonable here, and only here: the machine is disposable, reaches nothing else, and will be erased within the hour.
  • .agent/setup.sh, if the repository has one, plays the part of Codex Cloud's install script: dependencies, test database, variables. The agent also reads the repository's AGENTS.md or CLAUDE.md, as it does locally.
  • The idempotency key: if the order is resent after a network drop, the API returns the first response instead of creating a second machine.

One difference remains: Codex Cloud keeps the environment ready between tasks, while here each task starts from a clean image and reinstalls its dependencies. For an ordinary Node or Python project that is a minute; for a project that needs ten, keep a monthly machine as the agent's workstation and run the tasks on it.

6. Any agent, any model

The script's case is the only place that depends on the agent. A few variations:

  • Claude Code on a subscription rather than an API key: claude setup-token on your computer gives a long-lived token, to write as CLAUDE_CODE_OAUTH_TOKEN in ~/.agent.env instead of ANTHROPIC_API_KEY.
  • Codex with an open model: codex exec --oss --local-provider ollama uses an Ollama server instead of the OpenAI API; its address is set in the Codex configuration.
  • Another agent: install it in .agent/setup.sh and add a line to the case. It needs a non-interactive mode (instructions as an argument, an exit code); most have one.
  • Your own model: a Nano will not run an LLM. Host it on a bigger machine that stays up (see Self-host Hermes and Ollama), and point the disposable agents at it. Code and model then stay in Canada.

7. Starting a task from a GitHub issue

Codex Cloud starts from ChatGPT, a phone or Slack. The script starts from anywhere a command can run: a cron job for a nightly task, or GitHub Actions when someone puts the agent label on an issue, with the script committed as tools/ffxf-task.

bash
# .github/workflows/agent.yml
name: agent
on:
  issues:
    types: [labeled]
jobs:
  task:
    if: github.event.label.name == 'agent'
    runs-on: ubuntu-latest
    timeout-minutes: 120
    steps:
      - uses: actions/checkout@v4
      - name: SSH key
        env:
          SSH_KEY: ${{ secrets.FFXF_SSH_KEY }}
        run: |
          install -d -m 700 ~/.ssh
          printf '%s\n' "$SSH_KEY" > ~/.ssh/id_ed25519 && chmod 600 ~/.ssh/id_ed25519
          ssh-keygen -y -f ~/.ssh/id_ed25519 > ~/.ssh/id_ed25519.pub
      - name: Task
        env:
          FFXF_API_KEY: ${{ secrets.FFXF_API_KEY }}
          GH_TOKEN: ${{ secrets.AGENT_GH_TOKEN }}
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
          NUMBER: ${{ github.event.issue.number }}
        run: ./tools/ffxf-task "$GITHUB_REPOSITORY" "Handle issue #$NUMBER (gh issue view $NUMBER) and open a PR."

Two precautions. The issue text never enters the command: only its number does, and the agent reads the issue itself with gh issue view. And only repository members can add a label, but the issue text comes from whoever wrote it: to the agent it is an instruction like any other, hence the token limited to this one repository and the draft PR, reviewed before merging.

If the runner is killed without warning, the trap has no time to run. The console's monthly budget bounds the spending, and the GET /v1/usage?group_by=vm report from Recipes shows any task-… machine that survived.

8. What each does better

Codex Cloud needs no assembly: the environment is prepared in conversation, review happens in the app, on the phone too, and its network secrets are tighter than a file on the machine. If you already live in ChatGPT, Codex and GitHub, it is the shortest path.

The FFxF assembly is the better fit when you want to choose:

  • the agent and the model, including a model you host;
  • a real VM: root, Docker, public IPv4 and IPv6, a service that stays reachable while you test it;
  • an hourly bill, on prepaid credit, with a monthly budget that stops machines past 120 %;
  • where the code runs: Montréal, Canada;
  • the trigger: a script, a cron job, any CI, any code host.

The full API is covered in the documentation, and the basic scripts in Recipes.

An AI agent conversation linked through an MCP plug to a machine that starts, then is handed back

An AI agent that rents its own VPS: an MCP demo

A real run, call by call: the agent estimates the price, orders a Nano, gets it in 42 seconds, works on it over SSH and destroys it. The limits that held, and the bill: 0.018 CAD.

Read the article 7 min read

OpenClaw agent on a server linked to messaging apps

Install OpenClaw on a VPS: your own 24/7 AI agent

The open source personal AI agent that answers on WhatsApp or Telegram deserves better than a laptop left open. Here is the clean VPS install, from empty server to a daemon that survives reboots.

Read the article 9 min read

Support & discussions

Technical questions, incident reports, or infrastructure discussions, the team is reachable on Discord, Telegram, X, Instagram, Reddit, and IRC.