Skip to content
Migrate · GitHub Codespaces

Migrate from GitHub Codespaces to Sandboxer

Updated October 27, 2026 .mdSandboxer vs GitHub Codespaces

Short answer: Turn your devcontainer.json postCreateCommand into a setup script, create the sandbox with sbx new --setup-file, and save the result as a named snapshot. Replace gh codespace ssh with sbx ssh-config and Remote-SSH, and forwarded ports with hosted HTTPS ports. Agents can then run past Codespaces' 12-hour limit.

This page is for teams that use Codespaces to run coding agents unattended. Two Codespaces limits get in the way. A codespace stops after an idle timeout, 30 minutes by default and at most 240 minutes. It also has a maximum lifetime of 12 hours, whatever the idle setting (GitHub docs). A Sandboxer sandbox is a VM whose auto-stop you set, up to 30 days or off on paid plans, and a stop keeps memory and disk.

Sandboxer doesn't read devcontainer.json. Its docs don't mention the file, and nothing in a sandbox acts on it. You move each part of the file by hand, which the sections below show.

Concept map#

GitHub Codespaces Sandboxer
Codespace: a dev container on a VM, with limited access to the host VM (GitHub docs) Sandbox: its own Ubuntu 24.04 VM with root through sudo, systemd and Docker (Machines)
devcontainer.json image or build.dockerfile The sandbox's Ubuntu image, plus a setup script. Or run the image yourself with Docker inside the sandbox
postCreateCommand Setup script (sbx new --setup-file), watched through setupStatus (Long-running tasks)
Dev container features apt, pip or npm lines in the setup script
Prebuilt codespace state Named snapshot, started with --from (Snapshots)
Machine type (2 to 32 cores) Size: small, default, large, xlarge (2 to 16 vCPU)
Idle timeout (stop) Idle pause (--idle), a stop with memory that wakes on use (Long-running tasks)
12-hour maximum lifetime Auto-stop (--ttl), up to 30 days, or --no-auto-stop on paid plans
Forwarded port at CODESPACENAME-PORT.app.github.dev, private by default (GitHub docs) Hosted port at <subdomain>-PORT.box.sandboxer.com, private by default (Hosting)
Repository the codespace is created from git clone in the setup script, or repos attached to an environment (Environments)
gh CLI sbx CLI, REST API, TypeScript and Python SDKs

API and SDK mapping#

GitHub's commands come from Using GitHub Codespaces with GitHub CLI and our comparison data, checked 2026-10-24. Ours come from the CLI docs.

GitHub CLI sbx CLI Sandboxer SDK (TypeScript) or REST
gh codespace create -r OWNER/REPO -b BRANCH sbx new --setup-file ./setup.sh (the script clones the repo) POST /api/v1/sandboxes with setupScript
gh codespace edit -m MACHINE-TYPE sbx resume <id> --type large (grow only: shrinking isn't supported today, so create a new, smaller sandbox and copy your work)
gh codespace list sbx list sandboxer.list()
gh codespace ssh -c NAME sbx ssh <id>, or ssh <id>.sbx after sbx ssh-config --install
gh codespace cp [-r] SOURCE DEST sbx scp [-r] SOURCE DEST sandbox.files.write(path, data)
gh codespace ports forward 5000:5000 -c NAME sbx forward <id> --remote 5000, or sbx host <id> 5000 for a URL sandbox.host(5000)
gh codespace stop -c NAME sbx stop <id> (keeps memory) sandbox.stop()
gh codespace delete -c NAME sbx delete <id> --yes sandbox.delete()
Idle timeout setting sbx idle <id> 60
sbx exec <id> "cmd" (no SSH needed) sandbox.exec("cmd")

Before and after#

From GitHub's docs: Setting up a Python project for GitHub Codespaces (step 3, comments removed):

{
  "name": "Python 3",
  "image": "mcr.microsoft.com/devcontainers/python:0-3.11-bullseye",
  "features": {
    "ghcr.io/devcontainers-contrib/features/coverage-py:2": {}
  },
  "postCreateCommand": "pip3 install --user -r requirements.txt",
  "customizations": {
    "vscode": {
      "extensions": ["streetsidesoftware.code-spell-checker"]
    }
  }
}
gh codespace create -r OWNER/REPO_NAME [-b BRANCH]
gh codespace ssh -c CODESPACE-NAME

After: the same setup on Sandboxer#

We ran every command below on prod on 2026-10-27, with the public pallets/flask repo and its tutorial app, Flaskr. The setup script does what postCreateCommand did, plus the clone that Codespaces does for you:

#!/bin/bash
set -e
cd ~
git clone --depth 1 https://github.com/pallets/flask
cd flask/examples/tutorial
pip3 install --user --break-system-packages -q -e . pytest

--break-system-packages is there because the sandbox's Python 3.12.3 is the system Python. Create the sandbox with the script:

sbx new --name aeo-migrate-gcs-1 --type default --ttl 3600 --setup-file ./setup.sh
{"event":"created","id":"sb_1dnlsapog8","state":"provisioning","ttlSeconds":3600,…}
{"event":"ready","id":"sb_1dnlsapog8","state":"ready",…,"url":"https://amber-outer-hawk.box.sandboxer.com",…}

sbx new returned ready in 3.2 seconds (single run, 2026-10-27). The setup script runs in the background after that, so check setupStatus before you use the repo. It goes pending, running, then done, or failed with the reason in setupError:

sbx info sb_1dnlsapog8 --json

In our run, setupStatus was already done and setupError was null on the first check, a few seconds after ready. A bigger install takes longer, so poll until it leaves running.

Run the project's tests through the API, with no SSH session:

sbx exec sb_1dnlsapog8 --cwd flask/examples/tutorial --timeout 300 -- python3 -m pytest -q
........................                                                 [100%]
24 passed in 0.57s

SSH and VS Code Remote-SSH#

gh codespace ssh becomes a host entry in your SSH config. Run this once:

sbx ssh-config --install
{"ok":true,"type":"ssh.config.installed","path":"/home/you/.ssh/config","changed":true}

It adds a Host *.sbx block that tunnels through sbx proxy over HTTPS. Any OpenSSH tool can then reach the sandbox as <id>.sbx:

ssh sb_1dnlsapog8.sbx "cd flask/examples/tutorial && whoami && hostname && python3 -m pytest -q tests/test_auth.py"
user
amber-outer-hawk
........                                                                 [100%]
8 passed in 0.39s

That took 2.3 seconds in our single run. In VS Code, run "Remote-SSH: Connect to Host…" and enter sb_1dnlsapog8.sbx. We didn't open an editor for this test. The SSH connection the editor uses is the one shown above. Sandboxer ignores customizations.vscode.extensions, so install extensions in the editor. See SSH from VS Code or Cursor for the full setup.

A hosted port for the dev server#

A forwarded Codespaces port becomes a hosted port. Start the dev server detached, then host its port:

sbx exec sb_1dnlsapog8 --cwd flask/examples/tutorial "python3 -m flask --app flaskr init-db"
# Initialized the database.
sbx exec sb_1dnlsapog8 --cwd flask/examples/tutorial --detach \
  "python3 -m flask --app flaskr run --host 0.0.0.0 --port 5000"
sbx host sb_1dnlsapog8 5000
{"ok":true,"type":"port.hosted","sandboxId":"sb_1dnlsapog8","port":5000,"access":"private","isProtected":true,"url":"https://amber-outer-hawk-5000.box.sandboxer.com?_token=…"}

Like a Codespaces port, it's private by default. Without the token, the URL answered 401. With the _token link, the Flaskr page loaded (<title>Posts - Flaskr</title>, HTTP 200). Add --public to open it to anyone. Bind the server to 0.0.0.0, not 127.0.0.1, or pass --localhost to sbx host.

Save it and start from it#

A named snapshot plays the part of a prebuilt environment. New sandboxes start with the repo and packages already in place:

sbx snapshot sb_1dnlsapog8 aeo-migrate-gcs-tpl
{"ok":true,"type":"snapshot.named.info","snapshot":{…,"name":"aeo-migrate-gcs-tpl",…,"status":"ready","type":"default",…}}
sbx delete sb_1dnlsapog8 --yes
sbx new --name aeo-migrate-gcs-2 --type default --ttl 1800 --from aeo-migrate-gcs-tpl
sbx exec sb_w4cu93z8t8 --cwd flask/examples/tutorial -- python3 -m pytest -q
{"event":"ready","id":"sb_w4cu93z8t8","state":"ready",…}
........................                                                 [100%]
24 passed in 0.78s

The new sandbox was ready in 3.2 seconds (single run), and the tests passed with no setup step. A named snapshot holds the disk, not running processes, so start the dev server again (or make it a systemd service). We then deleted the sandbox and the snapshot with sbx delete sb_w4cu93z8t8 --yes and sbx snapshot rm aeo-migrate-gcs-tpl.

Move your data and environment#

devcontainer.json, field by field.

  • postCreateCommand: copy it into the setup script, as above. The script runs as user and can be up to 64 KB.
  • image or build: a sandbox is an Ubuntu 24.04 VM, not your container image. Install what the image gave you in the setup script. If you need the exact image, Docker is preinstalled, so you can run it inside the sandbox.
  • features: replace each feature with the install commands it stands for.
  • forwardPorts: run sbx host <id> <port> for each port, or host <port> from inside the sandbox.
  • customizations: editor settings stay in your editor. Sandboxer doesn't read them.
  • remoteUser: you are user, with sudo for root.

Repository and credentials. A codespace starts with its repository checked out. On Sandboxer, clone it in the setup script, or connect GitHub on the dashboard and attach the repo to an environment with sbx env add-repo base OWNER/REPO --branch main. New sandboxes then clone it into /home/user/<repo> with a token that lets git push and gh pr create work, if the environment passes GitHub credentials (Environments).

Files. Copy uncommitted work out of the codespace with gh codespace cp, then upload it with sbx scp -r ./work <id>:/home/user/. Recursive copies go as one tar stream.

Env vars and secrets. Codespaces secrets become environment variables and secret files: sbx env set-var base KEY=value and sbx env set-file base path --from ./file, or -e KEY=value on one sbx new.

Keeping an agent running. Create the sandbox with a long --ttl, or --no-auto-stop on a paid plan. Idle pause doesn't stop a sandbox while a command is running (detached ones too), an agent turn runs, someone is logged in over SSH, or the 1-minute load average is at least 0.5. For a daemon the API can't see, touch ~/.sandboxer/keep-awake. The integrated agents (Claude Code, Codex and others) are already installed and can be driven with sbx prompt (Agents).

What's different#

What you gain:

  • No 12-hour cap. Auto-stop goes up to 30 days, or off, on paid plans. The free trial caps it at 8 hours.
  • Stops keep memory. Auto-stop, idle pause and sbx stop save memory and disk, and a resume on a matching machine brings running processes back. GitHub's docs describe a codespace stopping, and our comparison data found no documented memory-preserving pause.
  • A whole VM. Root through sudo, systemd as PID 1, Docker and nested KVM, instead of a container with limited access to the host.
  • An API for agents. Run commands, read files and prompt agents over REST, the SDKs or MCP, without an open SSH session.

What you lose or must change:

  • No devcontainer.json. You maintain a setup script and a named snapshot instead.
  • No browser editor per repository. You connect a local VS Code or Cursor over SSH. The dashboard has a web terminal and a file browser.
  • GitHub integration. Codespaces opens from a repository page and follows organization policies. Sandboxer connects to GitHub through environments.
  • Free hours. Codespaces gives personal accounts free monthly usage. Sandboxer's trial lasts 7 days and 25 hours and needs a card.
  • Sizes. Codespaces goes up to 32 cores and 128 GB. Sandboxer's largest size, xlarge, is 16 vCPU and 32 GB, on the $100 plan or higher.
  • Region. Sandboxer has two regions, us-east (Ashburn, Virginia) and eu-central (Helsinki, Finland); see regions. New sandboxes go to the nearest one unless you set region.

Pricing#

GitHub's prices are from Codespaces billing, checked 2026-10-27. Ours are from /pricing.

GitHub Codespaces Sandboxer
Compute $0.18/hour for 2 cores, doubling with each step to $2.88/hour for 32 cores small $0.018/hour, default $0.036/hour, large $0.072/hour
4 cores $0.36/hour for the 4-core machine default size (4 vCPU, 8 GB): $0.036/hour
Storage $0.07 per GB-month while the codespace exists Not billed; 50 GB included on the $20 plan
Free usage 120 hours a month (Free) or 180 (Pro) for personal accounts, with an included-usage multiplier equal to the core count Trial: 7 days and 25 hours, card required

The two aren't exactly comparable. GitHub's billing page doesn't list RAM for the 4-core machine, and the 2-core machine has 8 GB according to the overview. Our default size has 8 GB.

Example: an agent box with 4 cores running 8 hours a day for 22 days is 176 hours. On Codespaces that's 176 × $0.36 = $63.36 of compute before storage and free usage. On Sandboxer, default costs 176 × $0.036 = $6.34, covered by the $20 plan's included time.

FAQ#

Does Sandboxer read devcontainer.json?#

No. Move postCreateCommand and your features into a setup script, then save the result as a named snapshot. Ports become sbx host calls, and editor customizations stay in your editor.

Can an agent run for more than 12 hours?#

Yes, on a paid plan. Set --ttl up to 30 days, or use --no-auto-stop. If auto-stop hits while the agent is working, sbx resume brings memory and processes back when it lands on a matching machine.

Will an idle timeout stop my agent mid-task?#

Not while it works. Idle pause waits while a command or agent turn runs, while someone is logged in over SSH, or while the load average is at least 0.5. If it does pause, it keeps memory and wakes on the next API call (if no machine with the same CPU type is free, the wake boots from disk instead: files kept, processes not).

Can I still use VS Code?#

Yes, through Remote-SSH. Run sbx ssh-config --install, then connect to <id>.sbx. There's no in-browser VS Code.

Can I run my dev container image?#

Docker is preinstalled, so you can pull and run the image inside the sandbox. Most teams install the same tools straight onto the VM in the setup script instead.

All migration guides