conda.toml specification#

This page is the normative reference for the conda.toml workspace manifest format read by conda-workspaces. It is the human-readable companion to schema/conda-toml-1.schema.json and the source material for the future Conda Enhancement Proposal that will standardise the format across the conda ecosystem.

For tutorials and examples see Configuration and Quickstart. For plugin name aliases see Plugin format names and aliases.

Status#

Field

Value

Format name

conda.toml

Schema version

1

Schema URL

https://schemas.conda.org/conda-toml-1.schema.json

Canonical plugin name

conda-workspaces (see format-aliases.md)

Reference implementation

conda_workspaces.manifests.toml.CondaTomlParser

Standardisation track

Pre-CEP — see the CEP tracker issue

Scope#

conda.toml describes a workspace: a project root that may declare one or more conda environments composed from reusable features, plus a set of tasks that run inside those environments. It is the conda-native sibling of pixi.toml. The core workspace, dependency, feature, environment, and task tables deliberately overlap with pixi, while conda-workspaces also owns conda-specific extensions such as default-environment and [workspace.archive]. The reverse direction (pixi.toml → conda.toml) holds only when the pixi.toml keeps to the fields described here. See Pixi compatibility ladder below for the precise asymmetry.

Out of scope: package build recipes (use recipe.yaml), package distribution metadata (pyproject.toml [project]), and lockfile contents (use the companion conda.lock, see Lockfile relationship below).

File detection#

A conda-workspaces tool MUST detect a workspace using one of these two forms, in order:

  1. A file named conda.toml at the workspace root that contains a top-level [workspace] table.

  2. A file named pyproject.toml at the workspace root that contains a [tool.conda.workspace] table.

A conda.toml without a [workspace] table is permitted: it is treated as a tasks-only manifest (see [tasks] below) and does not constitute a workspace on its own.

For compatibility, conda-workspaces also reads pixi.toml (top-level [workspace] / [project]) and pyproject.toml [tool.pixi.workspace]. That compatibility surface is not part of this specification — it is documented in Configuration.

Search order#

Tools that search a directory tree for the manifest MUST consult these filenames in this order, returning the first hit:

  1. conda.toml

  2. pixi.toml

  3. pyproject.toml

When more than one form is present in the same directory, conda.toml wins. When pyproject.toml contains both [tool.conda] and [tool.pixi] tables, [tool.conda] wins (see Configuration).

Top-level tables#

All tables are optional unless marked required. Field types follow TOML conventions: string, integer, boolean, array, table, inline-table.

[workspace] (required when used as a workspace manifest)#

Workspace metadata.

Field

Type

Required

Description

name

string

no

Workspace name. Defaults to the workspace directory name.

version

string

no

Workspace version.

description

string

no

Short prose description.

channels

array of channel

yes

Conda channels in priority order.

platforms

array of platform

yes

Platforms the workspace targets. Used to drive multi-platform solves.

channel-priority

string

no

One of "strict", "flexible", "disabled". Maps to conda’s solver setting.

envs-dir

string

no

Where per-env prefixes live, relative to and contained by the workspace root. Symbolic-link directories are rejected. Default: .conda/envs.

dependencies

conda deps

no

Root-level dependency pool used by { workspace = true } entries in dependency tables.

archive

table

no

Archive filters and compression settings. See [workspace.archive].

A channel is either:

  • a string containing a channel name ("conda-forge") or URL ("https://my.channel/label/dev"), or

  • an inline table { channel = "<name-or-url>" }. Other keys (e.g. priority) are reserved and currently ignored by this version.

Channel URLs should not contain credentials, Anaconda /t/<token>/ path segments, queries, or fragments. Generated manifests remove those values from inherited channels while preserving an absolute URL’s origin and path. Configure channel authentication through Conda outside the workspace manifest. A relative t/<token>/<channel> value is serialized as a credential-free absolute channel URL. A scheme-relative //host/path value is normalized to an explicit HTTPS URL.

A platform is either a conda subdir string from the closed enum defined in the JSON schema under $defs.platform (e.g. linux-64, osx-arm64, win-64, noarch) or a Pixi-compatible rich-platform inline table. Rich-platform tables may set platform = "<subdir>", an optional workspace-scoped name, and virtual package constraints such as cuda, archspec, glibc / libc, linux, macos / osx, windows / win, or raw __<virtual-package> keys.

[workspace]
platforms = [
  "linux-64",
  { name = "linux-64-cuda", platform = "linux-64", cuda = "12.0" },
]

When name is omitted, conda-workspaces follows Pixi’s generated-name style, for example { platform = "linux-64", cuda = "12.0" } becomes linux-64-cuda-12-0. The declared name is used by feature platforms restrictions and as the conda.lock platform key. The backing platform subdir is used for conda solves and package URL validation.

[workspace.archive]#

Archive settings used by conda workspace archive.

Field

Type

Description

include

array of string

Glob patterns for files to include. When set, only matching files are archived.

exclude

array of string

Glob patterns for files to exclude.

compression

string

One of "zst", "gz", or "bz2".

compression-level

integer

Compression level passed to the selected compressor.

[workspace.dependencies]#

Reusable conda package specs keyed by package name. Regular dependency tables can opt into a root spec with { workspace = true }, which keeps shared versions in one place while preserving explicit package membership in each feature or target table. This follows the workspace dependency inheritance syntax that pixi added in 0.70.0.

[workspace.dependencies]
numpy = "1.*"
cmake = { version = ">=3.28", channel = "conda-forge" }

[dependencies]
python = ">=3.12"
numpy = { workspace = true }

[feature.build.dependencies]
cmake = { workspace = true, build = "h*" }

The inherited entry uses the root spec as a base. Non-version fields such as build, channel, subdir, md5, sha256, url, file-name, license, license-family, features, and track-features may be set on the consuming dependency to layer on top. version is owned by the workspace entry. Setting both workspace = true and version is an error. workspace = false is also rejected.

[dependencies]#

Conda packages that belong to the default feature. Keys are package names. Values are either a version constraint string or a detailed dependency spec. Detailed specs may also inherit from [workspace.dependencies] with { workspace = true }.

[dependencies]
python = ">=3.10"
numpy = ">=1.24,<2"
scipy = "*"
cuda-toolkit = { version = ">=12", build = "*cuda*" }

A detailed conda dependency accepts:

Field

Type

Description

version

string

Version constraint (e.g. ">=12").

build

string

Build-string glob (e.g. "*cuda*").

build-number

string

Build number constraint.

channel

string

Channel name or URL for this package.

subdir

platform

Platform/subdir constraint.

md5 / sha256

string

Exact package hash.

url

string

Exact credential-free package URL. Mutation and import commands reject embedded authentication, Anaconda token paths, queries, and fragments.

file-name

string

Exact package filename.

license / license-family

string

License constraints.

features / track-features

array of strings

Feature constraints.

workspace

boolean

Must be true. Inherits from [workspace.dependencies]. Mutually exclusive with version.

[pypi-dependencies]#

PyPI packages that belong to the default feature. Keys are package names. Values are either a PEP 508 version-constraint string or a detailed PyPI dependency spec:

Field

Type

Description

version

string

PEP 508 constraint.

extras

array of string

Extras to request, e.g. ["http2"].

path

string

Local path to the package.

editable

boolean

Install in editable mode.

git

string

Git repository URL.

branch

string

Branch (with git).

tag

string

Tag (with git).

rev

string

Revision (with git).

url

string

Direct package URL.

[activation]#

Default-feature activation settings.

Field

Type

Description

scripts

array of string

Shell scripts sourced on environment activation.

env

table of string -> string

Environment variables set on activation.

[system-requirements]#

Default-feature system-level constraints (e.g. minimum glibc, macOS version). Recognised keys mirror conda’s virtual-package names (__glibc, __osx, __cuda, …). Values are stringified version constraints, except libc may also use pixi’s inline table form { family = "glibc", version = "2.28" }.

[target.<platform>]#

Per-platform overrides for the default feature. <platform> may be a declared platform name or the backing conda subdir of a declared rich platform. When both match, the subdir target is merged first and the declared rich-platform name can override it.

Field

Type

Description

dependencies

conda deps

Platform-specific conda overrides.

pypi-dependencies

PyPI deps

Platform-specific PyPI overrides.

[feature.<name>]#

A reusable group of dependencies and settings. <name> is an arbitrary identifier referenced from [environments].

Field

Type

Description

dependencies

conda deps

Conda dependencies for this feature.

pypi-dependencies

PyPI deps

PyPI dependencies for this feature.

channels

array of channel

Additional channels.

platforms

array of string

Restrict the feature to declared platform names or subdirs.

system-requirements

table

Per-feature system requirements.

activation

table

Per-feature activation.

target

table of [target.<platform>]

Per-platform dep overrides for the feature.

tasks

table

Tasks contributed by this feature and merged into the workspace task set.

[environments]#

A named environment is a composition of one or more features, private dependencies declared directly on that environment, plus (by default) the default feature defined by the top-level [dependencies], [pypi-dependencies], [activation] and [system-requirements] tables.

Two forms are accepted:

[environments]
lint = ["lint"]                                     # shorthand
docs = { features = ["docs"], no-default-feature = false }

[environments.test]
features = ["test"]

[environments.test.dependencies]
coverage = "*"

[environments.test.pypi-dependencies]
pytest-plugin = ">=1"

[environments.test.target.win-64.dependencies]
pywin32 = "*"

[environments.test.target.win-64.pypi-dependencies]
colorama = ">=0.4"

Field

Type

Description

features

array of string

Feature names to include in addition to the default feature.

solve-group

string

Accepted for pixi compatibility. Currently ignored. Environments are solved independently.

no-default-feature

boolean

If true, exclude the default feature from this environment. Default: false.

dependencies

conda deps

Conda dependencies private to this environment.

pypi-dependencies

PyPI deps

PyPI dependencies private to this environment.

target

table of [target.<platform>]

Per-platform private dependency overrides for this environment.

When [environments] is omitted entirely, a single implicit environment named default is used, composed from the default feature only.

Environment names must be one portable path segment. Windows device aliases, alternate data stream syntax, trailing dots or spaces, and path separators are rejected on every host so a workspace remains safe to move between platforms.

Environment-local dependencies are merged after shared features. They therefore affect only that environment and override a same-named dependency contributed by a feature. An environment’s matching target dependencies are merged after its unqualified private dependencies.

[tasks]#

Named tasks runnable via conda task run <name>. Each value is either a command string or a task table.

Field

Type

Description

cmd

string or array of string

Command to execute. Omit to define an alias whose only purpose is depends-on.

args

array of task arg

Named arguments with optional defaults.

depends-on

array of task dep

Tasks to run before this one.

cwd

string

Working directory for the task.

env

table of string -> string

Environment variables to set.

description

string

Human-readable description.

inputs

array of string

Glob patterns for cache inputs.

outputs

array of string

Glob patterns for cache outputs.

clean-env

boolean

Run with a minimal environment.

default-environment

string

Conda environment to activate by default for this task.

target

table of [target.<platform>.tasks]

Per-platform overrides.

A task arg is an inline table such as { arg = "path", default = "tests/" }.

A task dep is one of:

  • a string naming another task: "build", or

  • an inline table with extra fields: { task = "test", args = ["tests/unit/"], environment = "py311" }.

[target.<platform>.tasks]#

Platform-specific task overrides. Each entry follows the same shape as a [tasks] entry.

Composition rules#

When a tool resolves an environment named <env>:

  1. Unless the environment sets no-default-feature = true, merge the default feature’s top-level declarations and then its matching [target.<platform>] overrides.

  2. Merge each named feature listed in features, in order. For each feature, merge its unqualified declarations and then its matching [feature.<name>.target.<platform>] overrides. Later features override earlier ones for conflicting dependency keys.

  3. Merge dependencies declared directly on the environment.

  4. Merge matching [environments.<env>.target.<platform>] dependencies.

Conda and PyPI dependency maps follow these ordered last-wins rules. Channel order is preserved and duplicate channels are removed. Activation scripts are concatenated in feature order without implicit de-duplication.

Dependency mutation rules#

conda workspace add, conda workspace update, and conda workspace remove address one declaration location. They MUST NOT search the composed environment and choose the effective winning declaration.

Selectors

Conda declaration table

none

[dependencies]

--platform P

[target.P.dependencies]

--feature F

[feature.F.dependencies]

--feature F --platform P

[feature.F.target.P.dependencies]

--environment E

[environments.E.dependencies]

--environment E --platform P

[environments.E.target.P.dependencies]

--feature default is equivalent to using no location selector. Version 1 continues to read a legacy [feature.default] table with its established replacement semantics, where it replaces rather than merges top-level default feature content. New manifests MUST put default feature content in the corresponding top-level tables. add, update, and remove require manual migration before mutating a manifest that contains the legacy table. Move its dependencies and settings with the mappings below. Review feature-specific channels and platforms manually because they have no exact top-level equivalent.

Legacy table

Top-level destination

[feature.default.dependencies]

[dependencies]

[feature.default.pypi-dependencies]

[pypi-dependencies]

[feature.default.activation]

[activation]

[feature.default.system-requirements]

[system-requirements]

[feature.default.target.P.dependencies]

[target.P.dependencies]

[feature.default.target.P.pypi-dependencies]

[target.P.pypi-dependencies]

For dependencies and settings, preserve the legacy values when both locations exist because the legacy feature replaces the top-level default feature in 0.7. Legacy [feature.default.tasks] and [feature.default.target.P.tasks] remain ignored in conda.toml, matching 0.7. Do not move them unless you intend to activate those commands. Remove the legacy table after preserving or deliberately discarding its content.

--pypi selects the corresponding pypi-dependencies table. --feature and --environment are mutually exclusive. --platform composes with either selector. P MUST identify a declared platform name or the backing conda subdir of a declared rich platform when adding. Remove also accepts the exact key of an existing target table so stale declarations can be deleted after a platform is removed.

A bare add MUST preserve an existing { workspace = true } marker at the selected location. An explicit spec replaces only that selected marker. Removing a marker removes membership from the selected table. Neither operation changes the shared [workspace.dependencies] entry. Target declarations change only when --platform selects them.

A bare update MUST leave the selected declaration unchanged. An explicit update spec replaces only that membership declaration. Update does not support --pypi. It selects every environment and platform where the chosen declaration is effective, updates installed host prefixes with conda’s frozen-installed solve, and selectively solves the same roots against a complete canonical lockfile baseline. Every required solve MUST complete before update publishes the desired manifest and lock. Prefix transactions start only after publication so the reported conda workspace install -e NAME commands can reconcile a failed multi-prefix update without creating previously uninstalled environments.

After an add, the command reports any later declaration that still overrides the added value and gives the selector for that location. A remove where a requested package is absent from the selected location but declared elsewhere MUST fail before writing and list exact commands for all matching locations. The complete removal request is preflighted as one operation, so one wrong-location package prevents every removal in that request.

Lockfile relationship#

conda workspace lock and conda workspace install produce and consume conda.lock at the workspace root. The lockfile schema is derived from rattler-lock v6 (the same schema pixi.lock uses) with one on-disk difference: conda.lock writes version: 1 instead of version: 6 so tools can identify the file as conda-workspaces-owned at a glance. The remainder of the document — environments, packages, channels, platform package lists — is structure-compatible with rattler-lock v6.

See Plugin format names and aliases for the canonical and alias strings under which conda.lock is registered.

Lockfile satisfiability check#

conda workspace install and conda workspace info evaluate the lockfile against the manifest to determine whether a re-solve is needed. The check is purely structural (no file timestamps) and runs in this order:

  1. Schema version – the lockfile version field must match the expected version (1).

  2. Environments – every environment declared in the manifest must have a corresponding entry in the lockfile.

  3. Channels – the channel list for each environment in the lockfile must match the manifest (order-sensitive, URLs normalized).

  4. Platforms – every platform declared in the manifest must be present in each lockfile environment’s packages section.

  5. Dependencies – for each dependency in the manifest (on the current platform), a locked package must exist whose version satisfies the manifest’s version spec.

The check returns the first failing condition it finds. The result is one of three states:

Status

Constant

Meaning

up-to-date

LockfileStatus.UP_TO_DATE

All checks pass

out-of-date

LockfileStatus.OUT_OF_DATE

At least one check fails

missing

LockfileStatus.MISSING

No conda.lock file found

JSON output fields#

conda workspace info --json includes the following lockfile fields:

Field

Type

Description

lockfile_status

string

One of up-to-date, out-of-date, missing

lockfile_reason

string

Human-readable reason (only present when out-of-date)

Embedded form (pyproject.toml)#

The same tables are accepted under [tool.conda.<name>] inside a pyproject.toml:

Top-level table

Embedded equivalent

[workspace]

[tool.conda.workspace]

[dependencies]

[tool.conda.dependencies]

[pypi-dependencies]

[tool.conda.pypi-dependencies]

[activation]

[tool.conda.activation]

[system-requirements]

[tool.conda.system-requirements]

[target.<platform>]

[tool.conda.target.<platform>]

[feature.<name>]

[tool.conda.feature.<name>]

[environments]

[tool.conda.environments]

[environments.<name>.target.<platform>]

[tool.conda.environments.<name>.target.<platform>]

[tasks]

[tool.conda.tasks]

When both conda.toml and pyproject.toml exist in the same directory, conda.toml wins. When a pyproject.toml contains both [tool.conda] and [tool.pixi] tables, [tool.conda] wins.

Pixi compatibility ladder#

conda.toml and pixi.toml share a deliberately overlapping table layout, but they are not byte-for-byte interchangeable. The relationship is:

Direction

Holds?

Why

Every valid conda.toml is a valid pixi.toml (modulo filename).

No

The core workspace/dependency/task fields overlap intentionally, but conda-workspaces extensions such as default-environment and [workspace.archive] are not pixi schema guarantees.

Every valid pixi.toml is a valid conda.toml.

No

Pixi’s [workspace] accepts additional fields (authors, license, readme, homepage, repository, documentation, requires-pixi, preview, build-variants, conda-pypi-map, …) that this v1 schema does not list. The schema is a strict validation target and rejects unknown keys (additionalProperties: false).

pixi reads our default-environment task field.

No

default-environment (in [tasks] and [feature.*.tasks]) is a conda-workspaces extension. Pixi may ignore it.

conda-workspaces reads pixi.toml.

Yes

conda-workspaces ships a separate compatibility reader for pixi.toml and [tool.pixi.*]. That reader is out of scope for this spec and is documented in Configuration.

Two intentional differences from pixi.toml:

  • conda.toml accepts only [workspace]. The legacy pixi [project] table is not part of this specification.

  • conda-workspaces resolves dependencies with conda’s configured solver backend and installs them as conventional conda prefixes. Pixi’s build-process options (e.g. [package]-style build recipes) are not part of this specification.

If you need pixi-only fields, write a pixi.toml and let conda-workspaces’ compatibility reader handle it. Do not extend conda.toml with them.

Versioning policy#

The schema is versioned with a single integer (1). A future backwards-incompatible change to required fields, semantics, or defaults bumps the version to 2 and gets a sibling schema URL (https://schemas.conda.org/conda-toml-2.schema.json) and plugin canonical name (conda-workspaces-v2). Aliases follow the rules in format-aliases.md.

Backwards-compatible additions (new optional fields, new platforms in the platform enum) do not bump the version. The JSON schema is the strict validation target for this version. The conda-workspaces runtime parser is intentionally more permissive for pixi and forward compatibility and may ignore unknown fields when reading manifests.

Open questions (non-normative)#

These are tracked for the CEP draft and are not part of v1:

  • Whether [workspace] should be required to carry version and name (currently both optional).

  • Whether solve-group should ever gain conda-workspaces semantics, or remain an accepted no-op compatibility field.

  • Whether to standardise an [envs] shorthand that maps a feature one-to-one to an environment of the same name.

  • Discovery rules for nested workspaces (currently parent-directory search stops at the first match).

  • Whether [tool.pixi.*] compatibility belongs in this specification or in a separate “compatibility” CEP.

  • Whether conda.toml should exist as a separate filename at all, or whether the spec should describe a “conda-workspaces dialect of pixi.toml” — i.e. a pixi.toml plus conda-workspaces extensions such as default-environment and [workspace.archive], without a parallel filename or schema URL. Trade-off: governance ownership (conda community vs. Prefix.dev) versus format proliferation. See the CEP tracker for discussion.