Skip to main content

Environments

An Environment says what a Code Sandbox runs with: a Python version on an approved base, the packages to install, a few files to bake in, and the variants — Datalayer, E2B, Daytona, Modal — it must build for. You write it once. It is resolved once into a lock, and every variant builds from that same lock, so the four sandboxes install the same package versions.

Preview

code_sandboxes.environments holds what every party to an Environment shares: the specification, the sandbox contract, the version lifecycle, the error codes, the builder interface, the doctor and the conformance suite.

On the Datalayer platform, the registry, the pages and the datalayer envs CLI are in place: an environment is written, versioned, validated, promoted, rolled back and launched by version. The resolver and the Datalayer builder are implemented and have each resolved and built a real environment end to end against a real registry — but the production BuildKit pool they need is not deployed on the runtimes plane yet, so no version reaches ready there yet. The states below are the ones it will move through, and a step whose infrastructure is not deployed refuses with DL_ENV_CAPABILITY_UNSUPPORTED, naming what is missing rather than failing obscurely.

Three objects​

  • An Environment is the stable object you name. It points at one promoted version.
  • An Environment Version is immutable once it leaves draft: its specification, the lock resolved from it, and the outcome of its builds. Editing a ready version makes a new draft.
  • An Environment Artifact is what one version builds into on one variant. A sandbox is always launched from the artifact's immutable reference, never from a name or a tag, which every provider lets move:
VariantImmutable reference
DatalayerThe OCI image digest, ...@sha256:...
E2BThe template build id
DaytonaThe snapshot id, once the snapshot is active
ModalThe image id, im-...

A specification​

apiVersion: environments.datalayer.io/v1alpha1
kind: Environment
metadata:
name: geospatial-analysis
title: Geospatial analysis
spec:
language:
name: python
version: "3.13"
base:
ref: datalayer/python-cpu
channel: "2026.09"
packages:
python:
manager: uv
dependencies:
- geopandas==1.1.1
- rasterio==1.4.3
system:
apt:
- gdal-bin
env:
GDAL_DATA: /usr/share/gdal
resources:
sizeClass: medium
compatibility:
variants:
required: [datalayer]
optional: [e2b, daytona, modal]
from code_sandboxes.environments import spec_digest, validate_environment

environment = validate_environment(open("geospatial.yaml").read())
print(spec_digest(environment)) # sha256:...

validate_environment returns the environment, or raises EnvironmentsError with every finding listed. The specification reference lists each field and its rules.

Lifecycle​

A version moves through these states, and only along these edges:

FromToWhen
draftresolvingIt is submitted
resolvingresolved or failedThe lock is produced, or the dependencies conflict
resolvedbuildingA build is requested
buildingvalidating or failedEvery build has ended, or a required variant failed
validatingready, partially_ready or failedThe smoke tests pass everywhere, fail only on an optional variant, or fail on a required one
partially_readyreadyA backfill build brings the missing variant
ready or partially_readydeprecatedIt is deprecated

Only a draft is edited. Only ready and partially_ready versions are promoted, and a partially_ready one only with its missing variants acknowledged. A deprecated version keeps its running sandboxes and takes no new ones.

from code_sandboxes.environments import build_outcome, transition

transition("draft", "resolving") # "resolving"
build_outcome(required={"datalayer": True}, optional={"modal": False}) # partially_ready

Errors​

Every failure is reported under one code, whichever provider it came from:

CodeRetryableWhat to do
DL_ENV_SPEC_INVALIDNoFix the highlighted field
DL_ENV_CAPABILITY_UNSUPPORTEDNoRemove the instruction or drop the variant
DL_ENV_RESOLVE_CONFLICTNoRelax a pin; the conflicting pair is shown
DL_ENV_PACKAGE_NOT_FOUNDSometimesCheck the name, the version, or the index allowlist
DL_ENV_PROTECTED_PACKAGENoRemove the pin; the supported range is shown
DL_ENV_BUILD_FAILEDNoRead the build log
DL_ENV_BUILD_TIMEOUTYesRetry or simplify
DL_ENV_QUOTA_EXCEEDEDLaterWait or raise the quota
DL_ENV_POLICY_DENIEDNoContact the organization admin
DL_ENV_SCAN_BLOCKEDNoUpgrade the offending package
DL_ENV_SMOKE_TEST_FAILEDNoThe contract violation is named
DL_ENV_ARTIFACT_MISSINGYesBuild it or pick another variant
DL_ENV_PROVIDER_ERRORYesRetry; Datalayer is alerted
DL_ENV_BUILD_SECRET_UNAVAILABLESometimesRetry once IAM is reachable, or check the secret still exists
DL_ENV_PUBLICATION_BLOCKEDNoRemove the private input (a build secret), or keep the version private

Checking an artifact​

Every artifact must satisfy the sandbox contract. Two tools check it:

  • The doctor, datalayer-sandbox doctor --json, runs inside the sandbox using nothing but the standard library and reports each row of the contract. Build it with make doctor-zipapp; images carry it at /opt/datalayer/bin/datalayer-sandbox.
  • The conformance suite, code_sandboxes.environments.conformance, runs against a started sandbox. Its core tier — the doctor, identity, Python, the kernel, the locked imports, the filesystem, a kernel restart, shutdown and build secrets — must pass on every required variant before a version is ready. Its extended tier — egress, GPU, disk throughput, cold start and concurrent kernels — is recorded per variant.
from code_sandboxes import Sandbox
from code_sandboxes.environments.conformance import run_core_tier

with Sandbox.create(variant="datalayer", environment="acme/geospatial-analysis") as sandbox:
result = run_core_tier(
sandbox,
python_version="3.13",
expected_packages={"geopandas": "1.1.1", "rasterio": "1.4.3"},
)
for check in result.failures:
print(check.id, check.detail)

From the Datalayer platform​

The same objects, through the platform that builds and runs them. An environment is named account/name and a version account/name@<number>:

datalayer envs create --file geospatial.yaml
datalayer envs validate acme/geospatial-analysis@1
datalayer envs resolve acme/geospatial-analysis@1
datalayer envs build acme/geospatial-analysis@1 --follow
datalayer envs try acme/geospatial-analysis@1
datalayer envs promote acme/geospatial-analysis@1

A launch names a version, or leaves it out for the promoted one:

from code_sandboxes import CodeSandboxClient

sandbox = CodeSandboxClient.create(
variant="datalayer",
environment="acme/geospatial-analysis",
environment_version=1,
)

Only the Datalayer variant has versions: naming one for E2B, Daytona or Modal is refused rather than quietly run on that provider's latest image. The whole flow, with what each step costs and what a refusal means, is in the User Environments guide, and every field of the document is in the Environment specification.