Skip to main content
Are you a large language model? This page is available as raw markdown at /environment.md. The full docset is at /llms-full.md and the index is at /llms.md.

Environment

Every C3 job runs the script: from your .c3 project configuration as a Bash script on a C3-managed machine. The environment mode controls what C3 prepares before that script starts:

  • Python: C3 builds and caches a uv environment from pyproject.toml + uv.lock.
  • Docker: C3 pulls the Docker Hub image in docker.image and runs your script inside it.
  • Bash: C3 runs your script directly on the VM. This can launch anything, but any setup you do in the script runs from scratch on every job.

Use Python or Docker when dependency setup is expensive or needs to be reproducible across jobs. Use Bash when your workload is already self-contained, has trivial setup, or you intentionally want to manage everything inside the script.

Python

Use python.project for Python projects with a pyproject.toml and uv.lock:

# .c3
project: python-example
script: run.sh
time: "02:00:00"

python:
project: ./

output:
- ./results
# run.sh
#!/bin/bash
set -euo pipefail

python3 train.py --output "$C3_ARTIFACTS_DIR"
# pyproject.toml
[project]
name = "python-example"
requires-python = ">=3.11"
dependencies = [
"jax[cuda12]",
"numpy",
]

C3 uses uv to run uv sync before your script starts. The resulting environment is cached from the lock file, so repeat jobs with the same uv.lock avoid rebuilding dependencies. If your project is in a subdirectory, set python.project to that path.

Generate or refresh the lock file locally with:

uv lock
Prefer python.project to pip install in run.sh

If you install Python packages inside the Bash script, those installs happen again for every job. Declaring the project with python.project lets C3 cache the environment between jobs.

Docker

Use docker: for non-Python workloads, mixed-language projects, custom CUDA/system libraries, CPU images with MPI/system packages, or Python projects that need OS-level dependencies. docker: and python: are mutually exclusive.

Reference a Docker Hub image from .c3:

# .c3
project: docker-example
script: run.sh
time: "02:00:00"

docker:
image: rust:1.95-slim-bookworm
requires_accelerator: none

output:
- ./results
# run.sh
#!/bin/bash
set -euo pipefail

cargo run --release -- --output "$C3_ARTIFACTS_DIR"
# Cargo.toml
[package]
name = "docker-example"
version = "0.1.0"
edition = "2021"
// src/main.rs
use std::{env, fs, path::PathBuf};

fn main() -> std::io::Result<()> {
let mut output = None;
let mut args = env::args().skip(1);
while let Some(arg) = args.next() {
if arg == "--output" {
output = args.next();
break;
}
}

let output = output.unwrap_or_else(|| {
env::var("C3_ARTIFACTS_DIR").unwrap_or_else(|_| ".".to_string())
});
let path = PathBuf::from(output).join("result.txt");
fs::create_dir_all(path.parent().unwrap())?;
fs::write(path, "Rust job ran on C3\n")
}
Docker image requirements

Use a public Docker Hub image that C3 can pull without authentication; bare names (ubuntu:22.04), namespaced names (user/image:tag), and explicit docker.io/... hosts are accepted, but private repositories and external registries are not. Keep secrets out of image layers: do not publish .c3.local, .env, SSH keys, cloud credentials, service tokens, or build-argument secrets.

C3 validates that docker.image points to Docker Hub, uploads your workspace, and pulls the image on the job machine before running bash /workspace/<script> inside the container. Docker images must include bash; minimal images such as Alpine or distroless may not have it by default.

Set docker.requires_accelerator when the container needs a specific runtime. Use cuda for NVIDIA/CUDA images that require GPU access, or none for CPU images. If the selected hardware does not match the requirement, C3 stops during Docker setup before your script runs; for example, requires_accelerator: cuda is rejected on CPU hardware.

On CPU profiles such as cpu-d3-96vcpu-384gb, Docker runs without CUDA, NVIDIA drivers, or Docker --gpus flags. This is the right path for single-node CPU scientific jobs, including containers that install tools such as OpenMPI, compilers, Julia, R, or domain-specific simulation packages. C3 does not currently provide a multi-node MPI launcher; if you use MPI on a CPU profile, run it within the single allocated machine.

CPU availability is currently experimental. CPU containers are usable and billed normally, but capacity and the C3-managed CPU images may change while the service matures.

Docker images keep their own PATH, HOME, and other ENV defaults. C3 only injects the job-specific variables needed to find the workspace, artifact directory, and selected hardware.

Bash

Use Bash-only mode by omitting both python: and docker:. C3 still runs your script: on the job machine, and that script can execute shell commands, compiled binaries, Julia, R, Rust, Fortran, MPI launchers, or anything else available in the workspace or installed by the script.

# .c3
project: bash-example
script: run.sh
time: "00:30:00"

datasets:
- ref: /datasets/example-inputs
mount: /data

output:
- ./results
# run.sh
#!/bin/bash
set -euo pipefail

mkdir -p results

# Any dependency setup done here runs again on every job.
chmod +x ./bin/my-simulation
./bin/my-simulation --input /data/input.dat --output results/output.dat

Bash-only jobs are the most flexible, but C3 does not cache an environment for them. If your script downloads packages, compiles dependencies, or creates virtual environments, that setup cost is paid on every run. Move that setup into python.project or docker: when repeat-job startup time matters.

Script environment variables

Your script runs with a sanitized environment. C3 forwards the runtime variables needed by the selected hardware and software stack but does not expose agent credentials, storage keys, or arbitrary host secrets.

VariableDescription
C3_JOB_WORKDIRAbsolute path to the job workspace. Available in all modes
C3_ARTIFACTS_DIRDirectory for files you want uploaded as job artifacts. Available in all modes
C3_HARDWARE_PROFILEConcrete C3 hardware profile selected for the job, such as l40, h100-80gb, or cpu-d3-96vcpu-384gb
C3_HARDWARE_KINDSelected hardware kind, currently gpu or cpu
C3_ACCELERATOR_KINDSelected accelerator kind, currently cuda or none
C3_GPU_PROFILELegacy compatibility alias for the selected hardware profile
PYTHONUNBUFFEREDSet to 1 so Python output streams promptly. Available in all modes
TERMTerminal type, defaulting to dumb if unset. Available in all modes
HOMESet to the job workspace for Bash/Python jobs. In Docker jobs, the image controls HOME
PATHRuntime PATH for Bash/Python jobs, with the Python virtualenv prepended when configured. In Docker jobs, the image controls PATH
TMPDIRPer-job temporary directory under the job workspace for Bash/Python jobs
VIRTUAL_ENVSet when python.project creates a virtualenv

Write persistent results to $C3_ARTIFACTS_DIR or a path listed in output:; files outside the configured artifact outputs are discarded when the job ends. Use data mounting for inputs that should live in C3 storage rather than in the project bundle.