// workspaces

Configure with .runcode.yaml

Commit a .runcode.yaml to the root of your repository and RunCode runs your setup commands for you. A fresh workspace, or one waking from sleep, comes up with dependencies installed and services running. No manual steps, and it works the same way for everyone on the repo.

Where it lives

RunCode reads the file from /home/ubuntu/workspace/.runcode.yaml, which is the root of your cloned repository. Commit it like any other file and every workspace built from that repo picks it up. No file? Then there are no setup steps to run, and RunCode just skips it.

A complete example

Three optional blocks, each a plain list of shell commands run in order as the ubuntu user:

.runcode.yaml
# .runcode.yaml goes at the root of your repository

onCreate:
  - cd ~/workspace && pip install -r requirements.txt
  - cd ~/workspace && npm ci

onStart:
  - cd ~/workspace && docker compose up -d db

onRefresh:
  - git -C ~/workspace fetch --all --quiet

The three lifecycle blocks

onCreate runs once, ever

Runs a single time, right after the workspace is first built and your repo is cloned. This is the place for one-time setup: installing dependencies, building tools, seeding a database. It is only marked done if it succeeds, so a failed setup is retried on the next boot rather than left half-finished.

onStart runs every boot

Runs every time the workspace powers on, including after an idle auto-stop. Use it for anything that needs to be running but does not survive a shutdown: a database, a dev server, or background services.

onRefresh runs every event

Runs on create, on start, and whenever the workspace reconciles. This is the most frequently run block, so keep it lightweight, like a quick git fetch or a status check. Save heavy or slow work for elsewhere.

Each command is its own shell

Every item in a block runs as a separate shell, so a cd on one line does not carry over to the next. Chain dependent steps with && inside a single item, or use absolute paths. Each block has to be a list of strings. A bare string is rejected rather than run character by character.

.runcode.yaml
# each list item is its own shell. a cd does NOT carry to the next item:
onCreate:
  - cd ~/workspace/api      # this cd is forgotten on the next line
  - pip install -r requirements.txt   # runs in the default directory!

# chain with && inside ONE item instead:
onCreate:
  - cd ~/workspace/api && pip install -r requirements.txt

Good to know

  • onCreate retries on failure

    It is only recorded as complete once it exits cleanly, so a flaky install is retried on the next boot instead of leaving a half-provisioned box.

  • onStart runs after every wake

    Because a stopped workspace shuts its processes down, onStart is your hook to bring services back each time the box powers on, idle auto-stop included.

  • Keep onRefresh cheap

    It runs on every lifecycle event, so slow work here slows down routine reconnects. Reserve it for quick syncs and checks.

  • Commands run as ubuntu

    They run as the ubuntu user, not root. Prefix with sudo where you genuinely need elevated privileges.

Want the bigger picture on how create, start, and refresh fire? See Workspaces & lifecycle.