Workspace Automation
A Workspace is the primary Linux environment for development. It typically contains:
- A full Linux root filesystem
- Custom development tools
- Source code
- Background processes and cron jobs at runtime
- Remote IDEs
- Other components involved in development
Crafting provides highly customizable automation when a workspace starts, as well as automated management of background processes and cron jobs.
Start Sequence
When a workspace starts — either when newly created or restarted for any reason — the following Start Sequence (also called Workspace Setup and Automation or Setup Tasks) is executed in order:
- Pull images needed for the workspace, including base snapshots and Web IDE images.
- Essential System Config: A non-customizable step where Crafting ensures its own functionality is properly configured.
- Apply home and personal snapshots (if specified and available).
- Inject files defined in the workspace definition.
- Run
/etc/sandbox.d/setupscript (if present and executable). - Run
~/.sandbox/setupscript (if present and executable). - Launch global (system-level) daemons.
- Check out source code from all defined Checkouts.
- Run
post-checkouthooks from all Checkouts. - Run
buildhooks from all Checkouts. - Launch daemons and activate jobs from all Checkouts.
- Run
/etc/sandbox.d/post-setupscript (if present and executable).
Some tasks only run once when the workspace is first created (e.g. applying snapshots), while others run every time the workspace starts (e.g. file injections, setup, and post-setup scripts).
Checkout tasks and hooks run once by default, unless the workspace is in Auto Mode. Daemons and jobs are always launched regardless of whether the build hook succeeds or fails, because they are automatically managed and restarted on failure.
Checkouts
A workspace can automatically check out defined code repositories, build the code, and launch daemons, so a sandbox runs a fully functioning application end-to-end when it becomes ready — with no manual steps required.
One or more Checkout entries can be added to the checkouts list of a workspace definition. For example:
workspaces:
- name: example
checkouts:
- path: foo
repo:
git: git@github.com:example-org/foo
manifest:
overlays:
- inline:
hooks:
post-checkout:
cmd: |
npm install
build:
cmd: |
npm run build
daemons:
server:
run:
cmd: npm start
Field reference:
path: the local path for the working copy (must be a relative path, always under$HOME).repo: the remote repository definition (see Repository).manifest: additional automation details for this checkout (see Manifest).
Repository
Only git repositories are currently supported. The repository can be defined in the following formats:
SSH protocol (recommended for pull and push access):
repo:
git: git@github.com:example-org/foo
This uses the workspace's Managed SSH Keypair for authentication.
HTTPS protocol:
repo:
git: https://github.com/example-org/foo
Always works with public repositories, but does not allow pushing changes and will fail for private repositories.
GitHub App (when the org has the GitHub App enabled):
repo:
github:
org: example-org
repo: foo
The workspace automatically constructs the origin git remote as https://github.com/example-org/foo and uses the GitHub App to authenticate. See GitHub App for details.
Checkout Alternative Versions
By default, code is checked out from the default branch. To specify a different version, use version_spec:
checkouts:
- path: foo
repo:
git: git@github.com:example-org/foo
version_spec: next
The value of version_spec can be a branch name, a tag name, or a commit ID.
Controlling Checkout History Depth
For large repositories, pulling the full history is often unnecessary and slow. Use the history field to limit what is fetched:
# By depth
checkouts:
- path: foo
repo:
git: git@github.com:example-org/foo
history:
depth: 1
# By date
checkouts:
- path: foo
repo:
git: git@github.com:example-org/foo
history:
since: '2025-10-12'
The depth field maps to git's --depth flag, and since maps to --shallow-since.
Recursive Checkout
Unlike the default git clone behavior, submodules are checked out recursively by default, because workspaces automate end-to-end builds and typically require the full source tree. To disable this:
checkouts:
- path: foo
repo:
git: git@github.com:example-org/foo
disable_recursive_checkout: true
Manifest
The Manifest (specified by manifest) defines the automation details for a particular checkout, including:
- An optional
post-checkouthook script to run after the code is checked out (or when new commits are pulled in Auto Mode). - An optional
buildhook script to run afterpost-checkoutcompletes. - Background daemons to launch from this checkout.
- Cron jobs to activate from this checkout.
Loading the Manifest
The manifest is defined using one or more overlays. If no overlays are specified, the workspace tries to load the manifest from .sandbox/manifest.yaml at the root of the source tree. When overlays are specified, .sandbox/manifest.yaml is skipped, and all items under overlays are merged in the order they are defined.
Full Manifest Example
env:
- FOO=BAR
hooks:
post-checkout:
cmd: ./post-checkout.sh
dir: scripts
env:
- FOO=my${BAR}
build:
cmd: ./build.sh
dir: scripts
env:
- FOO=my${BAR}
daemons:
server:
run:
cmd: ./server
dir: out
env:
- NODE_ENV=dev
monitor:
run:
cmd: ./monitor
dir: out
jobs:
housekeeper:
run:
cmd: ./housekeep
dir: out
schedule: '* * * * *'
The top-level env defines environment variables shared by all commands in the manifest. When multiple overlays are used, their env lists are merged in order. Each entry supports environment variable expansion in the value:
env:
- FOO=bar
- FOO=prefix$FOO
- FOO=${FOO}suffix
# Result: FOO=prefixbarsuffix
The run schema applies to hooks, daemons, and jobs:
cmd: the shell command passed to$SHELL -c.dir: optional working directory, must be a relative path under the checkout folder and must exist when the command runs.env: additional environment variables appended after the top-levelenvfor this specific command.
Daemons and Jobs
Daemons are background processes assumed to run continuously. The workspace automatically restarts them if they stop.
Jobs run on the cron schedule defined in the schedule field.
All commands run as the owner user. Use sudo for commands requiring root privileges.
Commands run in a headless, non-interactive shell. The default ~/.bashrc in most Debian-based distributions skips setup when not running interactively, so environment variables added to ~/.bashrc may not be available. To force interactive mode for a specific command:
cmd: exec bash -i -c COMMAND
To prevent a daemon or job from launching automatically when the workspace starts:
daemons:
server:
run:
cmd: ./server
disable_on_start: true
jobs:
housekeeper:
run:
cmd: ./housekeep
schedule: '* * * * *'
disable_on_start: true
Managing Daemons and Jobs via CLI
cs start # Start a daemon
cs stop # Stop a daemon
cs restart # Restart a daemon
cs ps # List daemons and jobs with their status
cs sandbox job enable # Enable a job
cs sandbox job disable # Disable a job
Logs from daemons and jobs are stored under /var/log/sandbox and automatically rotated. They can also be viewed from the Web UI. See Log View.
Auto Mode
A workspace can be placed in Auto Mode, which automatically detects new commits from the remote repository, pulls them, and then re-runs the post-checkout and build hooks and relaunches all daemons.
This mode is useful for sandboxes running autonomous tasks where no manual changes to the working copy are expected. Auto Mode automatically discards any outstanding local changes before pulling new commits. A warning is displayed when the workspace is accessed in this state.
Toggle Auto Mode from the Web UI or using the CLI:
cs sandbox update my-sandbox -m workspace1=AUTO -m workspace2=MANUAL
Setup Scripts
A workspace runs the following setup scripts if they are present with executable permissions:
/etc/sandbox.d/setup: Executed asrootfor system-level customizations.~/.sandbox/setup: Executed asownerin the home directory for user-level customizations./etc/sandbox.d/post-setup: Executed asrootas the final step of the start sequence.
All setup scripts run after all snapshots are applied and file injections are complete, and before checkout tasks begin.
File Injection
File injection is a complementary mechanism that provides flexibility beyond snapshots. Files can be declared in the workspace definition and injected into the filesystem after snapshots are applied. This allows setup scripts themselves to be injected rather than baked into snapshots.
Example:
workspaces:
- name: example
system:
files:
- path: /etc/sandbox.d/setup
mode: '0755'
overwrite: true
content: |
#!/bin/bash
echo "Performing system-level customization"
- path: ~/.sandbox/setup
mode: '0700'
template: |
#!/bin/bash
echo "Performing user-level customization for {{env "$SANDBOX_OWNER_EMAIL"}}"
echo "{{secret "some-token"}}" >~/.my-token
Field reference:
path: Must be an absolute path (starting with/or~/). Intermediate directories are created automatically.owner: File ownership asuid:gid. Defaults are derived from the path:0:0for paths outside$HOME,1000:1000for paths inside$HOME.mode: File permission mode. Default is0644.content: Raw content written to the file as-is.template: A Go template for rendering the file content. Supported functions:env EXPAND_EXP: performs environment variable expansion.secret NAME: replaced with the content of the named secret.overwrite: Iftrue, the file is always overwritten on startup. Iffalse(default), the file is skipped if it already exists.
Global Daemons
Global daemons are launched at the system level under root during startup, before any checkout tasks run. They can be defined in two ways:
In the workspace definition:
workspaces:
- name: example
system:
daemons:
- name: foo
run:
cmd: foo args
In the workspace filesystem, under /etc/sandbox.d/daemons, as files with .json, .yaml, or .yml suffixes:
name: foo
run:
cmd: foo args
Global daemons are controlled with the same CLI commands as checkout daemons: cs start/stop/restart and cs ps.
Impact of Failures
The workspace checks exit codes to determine success (zero) or failure (non-zero).
Failures in setup scripts and checkout hooks block the start sequence and put the workspace into a Problematic state. By default, the workspace retries indefinitely. Auto Retry can be disabled if manual debugging access is needed.
Stuck scripts (those that never exit) will stall the workspace in the Setting Up or Launching state indefinitely. Ensure:
- Scripts do not block on any input (there is no stdin available).
- Scripts do not contain indefinite loops.
- Exit codes accurately reflect success or failure.
Failures in daemons also flag the workspace as Problematic, unless the daemon was explicitly stopped from the Web UI or CLI.
See Also
- Workspace Snapshots — base, home, and personal snapshots
- Workspace SSH Access — SSH access and remote IDEs
- Workspace Web Access — WebTerminal, WebIDE, and Remote Desktop
- Readiness and Wait For — coordinating startup across workloads
- Log View — viewing logs from daemons and jobs
- GitHub App — GitHub App for seamless repository access
- Managed SSH Keypair — SSH keypair used for code checkout