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

Environments

C3 runs your script inside a container built from the Docker image named in .c3, delivered through Nydus, with a persistent JuiceFS workspace. Pick a public image that already contains everything your script needs:

Your projectImage
Python with GPU librariesA framework image such as pytorch/pytorch:2.4.0-cuda12.4-cudnn9-runtime
System packages, other languages, custom CUDAAny public Docker Hub or GitHub Container Registry image that contains them
Self-contained binaries or Bash scriptsA base image such as ubuntu:24.04

Jobs run without outbound network access, so the script cannot install packages. Anything the image lacks has to be built into a custom image.

Docker​

project: docker-example
script: run.sh
hardware: l40
time: "02:00:00"

docker:
image: pytorch/pytorch:2.4.0-cuda12.4-cudnn9-runtime
requires_accelerator: cuda

output:
- ./results
# run.sh
#!/bin/bash
set -euo pipefail
python3 train.py --output "$C3_ARTIFACTS_DIR"

C3 prepares the image once per immutable image/platform and reuses its Nydus representation. It runs bash /workspace/run.sh with your persistent workspace, attached data resources and $C3_ARTIFACTS_DIR mounted. Select --environment-delivery=oci to use ordinary container delivery instead.

Images on the GitHub Container Registry work the same way:

docker:
image: ghcr.io/my-lab/trainer:1.4
requires_accelerator: cuda

A CPU job that only needs a shell and the tools in a base image:

project: bash-example
script: run.sh
hardware: cpu-d3-4vcpu-16gb
time: "00:30:00"

docker:
image: ubuntu:24.04
requires_accelerator: none

output:
- ./results
# run.sh
#!/bin/bash
set -euo pipefail
mkdir -p results
./bin/my-simulation --output results/output.dat

Requirements:

  • Without a registered credential reference, use a public image on Docker Hub or ghcr.io. ubuntu:24.04, user/image:tag, docker.io/... and ghcr.io/<owner>/<image>:<tag> are accepted, with a tag or a digest. Other registries are rejected before upload. Anonymous pulls require a public package; GHCR reports private or missing packages as denied.
  • C3 reuses an image already present on the machine. Pin a digest if you need a specific build rather than a mutable tag such as latest.
  • The image must contain bash. Alpine and distroless images often do not.
  • The image must already contain every dependency. Jobs run in a private network namespace with no outbound access, so pip install or apt-get in the script fails.
  • Set requires_accelerator: cuda for GPU images and none for CPU images. C3 stops before running the script if the machine does not match.
  • C3 preserves the verified image environment for the payload, while fixing workspace, home and cache destinations for isolation.

If C3 has registered a private registry credential for your organization, pass its reference alongside the image:

docker:
image: registry.example.com/team/trainer:1.4
credential_id: my-registered-reference

credential_id is a nonsecret reference, not a password or token. The server checks its organization, registry and repository before preparing the image. A refusal fails the submission; C3 never retries it anonymously. This requires JuiceFS storage and works with Nydus or ordinary OCI delivery. It cannot be used with legacy storage or c3 ar deploy. MCP deploy accepts the same reference as docker_credential_id. Public images need no new setting.

For a CPU offering without the persistent runtime, use --storage=legacy and set requires_accelerator: none. See CPU jobs.

Container contents and persistence​

The image supplies Python, system libraries and command-line tools; C3 adds nothing to it except GPU access. GPU jobs receive the assigned GPU devices, the driver libraries and nvidia-smi through the isolated runtime. LD_LIBRARY_PATH and TRITON_LIBCUDA_PATH point at the mounted driver, so Triton and other tools that read the linker cache find libcuda.so.1. An LD_LIBRARY_PATH set by your image is kept and the driver directories are appended to it.

Files under /workspace and attached writable data resources persist between jobs. Changes to the container root, /tmp or the job home are temporary.

Every job runs with a memory, CPU and process limit set for its hardware offering. /tmp is a small memory-backed filesystem (256 MiB on current offerings) and anything written there counts against the job's memory limit. Keep downloads, model caches and scratch files under the job home or /workspace; TMPDIR and the cache variables below already point there. Model weights that every run needs belong in a dataset (c3 data upload), so they are mounted instead of downloaded on each job.

--storage=legacy explicitly selects the older execution/storage pathway for compatible offerings. It is never selected automatically after a runtime failure.

Variables available to your script​

VariableValue
Working directory/workspace
C3_ARTIFACTS_DIR/artifacts
HOME/home/job (temporary, disk-backed)
TMPDIR/home/job/tmp (temporary, disk-backed)
HF_HOME, TORCH_HOME/home/job/.cache/huggingface, /home/job/.cache/torch (temporary)
UV_HTTP_TIMEOUT300 (seconds per package request during dependency install)

The environment is sanitised: no C3 credentials or storage keys are visible to your script. .env files in your workspace are uploaded but not loaded; load them yourself if you need them.