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 |
|
Schema version |
|
Schema URL |
|
Canonical plugin name |
|
Reference implementation |
|
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:
A file named
conda.tomlat the workspace root that contains a top-level[workspace]table.A file named
pyproject.tomlat 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:
conda.tomlpixi.tomlpyproject.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 |
|---|---|---|---|
|
string |
no |
Workspace name. Defaults to the workspace directory name. |
|
string |
no |
Workspace version. |
|
string |
no |
Short prose description. |
|
array of channel |
yes |
Conda channels in priority order. |
|
array of platform |
yes |
Platforms the workspace targets. Used to drive multi-platform solves. |
|
string |
no |
One of |
|
string |
no |
Where per-env prefixes live, relative to and contained by the workspace root. Symbolic-link directories are rejected. Default: |
|
conda deps |
no |
Root-level dependency pool used by |
|
table |
no |
Archive filters and compression settings. See |
A channel is either:
a string containing a channel name (
"conda-forge") or URL ("https://my.channel/label/dev"), oran 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 |
|---|---|---|
|
array of string |
Glob patterns for files to include. When set, only matching files are archived. |
|
array of string |
Glob patterns for files to exclude. |
|
string |
One of |
|
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 |
|---|---|---|
|
string |
Version constraint (e.g. |
|
string |
Build-string glob (e.g. |
|
string |
Build number constraint. |
|
string |
Channel name or URL for this package. |
|
platform |
Platform/subdir constraint. |
|
string |
Exact package hash. |
|
string |
Exact credential-free package URL. Mutation and import commands reject embedded authentication, Anaconda token paths, queries, and fragments. |
|
string |
Exact package filename. |
|
string |
License constraints. |
|
array of strings |
Feature constraints. |
|
boolean |
Must be |
[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 |
|---|---|---|
|
string |
PEP 508 constraint. |
|
array of string |
Extras to request, e.g. |
|
string |
Local path to the package. |
|
boolean |
Install in editable mode. |
|
string |
Git repository URL. |
|
string |
Branch (with |
|
string |
Tag (with |
|
string |
Revision (with |
|
string |
Direct package URL. |
[activation]#
Default-feature activation settings.
Field |
Type |
Description |
|---|---|---|
|
array of string |
Shell scripts sourced on environment activation. |
|
table of |
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 |
|---|---|---|
|
conda deps |
Platform-specific conda overrides. |
|
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 |
|---|---|---|
|
conda deps |
Conda dependencies for this feature. |
|
PyPI deps |
PyPI dependencies for this feature. |
|
array of channel |
Additional channels. |
|
array of string |
Restrict the feature to declared platform names or subdirs. |
|
table |
Per-feature system requirements. |
|
table |
Per-feature activation. |
|
table of |
Per-platform dep overrides for the feature. |
|
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 |
|---|---|---|
|
array of string |
Feature names to include in addition to the default feature. |
|
string |
Accepted for pixi compatibility. Currently ignored. Environments are solved independently. |
|
boolean |
If |
|
conda deps |
Conda dependencies private to this environment. |
|
PyPI deps |
PyPI dependencies private to this environment. |
|
table of |
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 |
|---|---|---|
|
string or array of string |
Command to execute. Omit to define an alias whose only purpose is |
|
array of task arg |
Named arguments with optional defaults. |
|
array of task dep |
Tasks to run before this one. |
|
string |
Working directory for the task. |
|
table of |
Environment variables to set. |
|
string |
Human-readable description. |
|
array of string |
Glob patterns for cache inputs. |
|
array of string |
Glob patterns for cache outputs. |
|
boolean |
Run with a minimal environment. |
|
string |
Conda environment to activate by default for this task. |
|
table of |
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", oran 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>:
Unless the environment sets
no-default-feature = true, merge the default feature’s top-level declarations and then its matching[target.<platform>]overrides.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.Merge dependencies declared directly on the environment.
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 |
|
|
|
|
|
|
|
|
|
|
|
--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 |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
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:
Schema version – the lockfile
versionfield must match the expected version (1).Environments – every environment declared in the manifest must have a corresponding entry in the lockfile.
Channels – the channel list for each environment in the lockfile must match the manifest (order-sensitive, URLs normalized).
Platforms – every platform declared in the manifest must be present in each lockfile environment’s
packagessection.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 |
|---|---|---|
|
|
All checks pass |
|
|
At least one check fails |
|
|
No |
JSON output fields#
conda workspace info --json includes the following lockfile fields:
Field |
Type |
Description |
|---|---|---|
|
|
One of |
|
|
Human-readable reason (only present when |
Embedded form (pyproject.toml)#
The same tables are accepted under [tool.conda.<name>] inside a
pyproject.toml:
Top-level table |
Embedded equivalent |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
No |
The core workspace/dependency/task fields overlap intentionally, but conda-workspaces extensions such as |
Every valid |
No |
Pixi’s |
pixi reads our |
No |
|
conda-workspaces reads |
Yes |
conda-workspaces ships a separate compatibility reader for |
Two intentional differences from pixi.toml:
conda.tomlaccepts 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 carryversionandname(currently both optional).Whether
solve-groupshould 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.tomlshould exist as a separate filename at all, or whether the spec should describe a “conda-workspaces dialect ofpixi.toml” — i.e. apixi.tomlplus conda-workspaces extensions such asdefault-environmentand[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.