.cycleswap/
cycle project init creates a .cycleswap/ directory at the root of your project. It holds the configuration files read during upload and used inside the container.
Manifest
manifest.toml is the TOML file that configures your project.
# Example .cycleswap/manifest.toml
[main]
name = "my-experiment"
volumes = ["training-data", "validation-data"]
build_files = ["pyproject.toml"]
[ubuntu]
version = "24.04"
[job]
cuda = "13.0"
env_vars = { MAX_EPOCHS = "1000", LEARNING_RATE = "3e-4" }
email_on_success = true
email_on_failure = true
artifact_upload_order = "LIFO"[main]
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Project name. Must be unique within your organization. |
volumes | array of strings | no | Volume names to link to uploaded versions. Defaults to []. |
build_files | array of strings | no | Files (relative to the project root) whose content determines whether the build image is rebuilt. When any of these files change between uploads, the build image is rebuilt. They are also copied into the build context so the build script can reference them (e.g. pyproject.toml for dependency installation). Defaults to []. |
Volumes listed here are mounted inside the container at /volumes/<name> when a job runs. See Volumes.
Image section
Exactly one image section must be present. It determines the base container environment.
[ubuntu]
| Field | Type | Required | Description |
|---|---|---|---|
version | string | yes | Ubuntu version. One of: "22.04", "24.04", "25.04", "25.10", "26.04" |
[job]
Controls how the job runs.
| Field | Type | Required | Description |
|---|---|---|---|
cuda | string | no | Minimum CUDA version the host machine must have, e.g. "13.0" or "12.6". This does not install a driver inside the container; it tells the scheduler to assign the job only to machines whose GPU driver supports this CUDA version or newer. Omit this field if the job does not need a GPU. |
env_vars | table | no | Environment variables to set inside the container. Defaults to {}. |
email_on_success | bool | yes | Send an email when the job completes successfully. |
email_on_failure | bool | yes | Send an email when the job fails. |
artifact_upload_order | string | no | Order in which artifact files upload to storage. "FIFO" (default) uploads files in the order they were written. "LIFO" uploads the most recent file first. Set to "LIFO" when saving periodic checkpoints so the latest checkpoint is always available first. See Job Outputs. |
Built-in environment variables
These variables are set in every container automatically, on top of anything you define in env_vars:
| Variable | Value | Description |
|---|---|---|
INSIDE_CYCLESWAP_CONTAINER | 1 | Detect whether your code is running inside a container or locally. |
Run script
run.sh is the container's entry point. When a job starts, the container runs bash .cycleswap/run.sh from the /app working directory, where your project files are copied.
The default for Python projects:
export PATH="$HOME/.local/bin:$PATH"
uv run main.pyEdit this script to change what your job actually does. Any non-zero exit code marks the job as failed.
Build script
build.sh runs during the container image build, not at job time. Use it to install system packages, compile dependencies, or do any other setup that should be baked into the image rather than repeated on every job run.
At build time, the only files accessible are .cycleswap/ itself and the files listed in build_files. Your project's source code is not copied in until run time. If the build script needs a file outside .cycleswap/ (like pyproject.toml), add it to build_files in the manifest.
Incremental caching
Build images are cached and layered incrementally. Each build runs on top of the previous build image for the same project, so repeated installs of the same packages are near-instant. The content of the build script and the files listed in build_files are hashed together, and the build only re-runs when that hash changes.
You can force a rebuild with --rebuild (re-runs the build script on top of the previous build image) or --clean (starts fresh from the base Ubuntu image). These flags work on both cycle project upload and cycle project test.
The default for Python projects installs uv and syncs dependencies from your pyproject.toml:
apt-get update -qq && apt-get install -y -qq curl && rm -rf /var/lib/apt/lists/*
curl -LsSf https://astral.sh/uv/install.sh | sh
export PATH="$HOME/.local/bin:$PATH"
uv sync