CEP 32 - Management and structure of conda environments
| Title | Management and structure of conda environments |
| Status | Accepted |
| Author(s) | Jaime Rodríguez-Guerra <jaime.rogue@gmail.com> |
| Created | May 9, 2025 |
| Updated | Mar 4, 2026 |
| Discussion | https://github.com/conda/ceps/pull/124 |
| Implementation | NA |
| Requires | CEP 29, CEP 33, CEP 34 |
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC2119 when, and only when, they appear in all capitals, as shown here.
Abstract
This CEP describes the lifecycle of conda environments and their structure.
Specification
A conda environment is defined as a directory that contains, at least, a ./conda-meta/history file.
All relative paths in this specification MUST be understood as relative to the path of a given target environment, referred to as $CONDA_PREFIX. So ./conda-meta/ is equivalent to $CONDA_PREFIX/conda-meta. Directory paths end with a trailing slash /.
Internal metadata: ./conda-meta/
This directory stores metadata about the environment and installed packages. It MUST be considered protected and MUST NOT be populated directly by package contents.
The following files MUST be recognized by conda clients:
./conda-meta/history
Required.
Plain text file. Its existence MUST mark its parent directory as a valid conda environment.
It SHOULD record the operations performed during the lifetime of the environment, but MAY be empty. If populated, a history file MUST be composed of one or more action blocks. Each action block MUST follow this syntax:
==> YYYY-MM-DD HH:MM:SS <==
# cmd: /path/to/conda/executable subcommand arguments ...
# name-of-tool version: MAJOR.MINOR.PATCH
+channel/subdir::name_of_linked_package1-version-build_string
+channel/subdir::name_of_linked_package2-version-build_string
+channel/subdir::name_of_linked_package3-version-build_string
+channel/subdir::name_of_linked_package4-version-build_string
-channel/subdir::name_of_unlinked_package1-version-build_string
-channel/subdir::name_of_unlinked_package2-version-build_string
# (update|remove|neutered) specs: ['spec1', 'spec2', 'spec3', 'spec4']
./conda-meta/{name}-{version}-{build-string}.json
Required.
This document serves as a manifest of all the files that each installed package brought into the environment, plus some additional metadata to handle its behavior during the environment lifecycle.
It MUST be a JSON document that ships a dictionary conforming to this schema:
arch: str | None. Deprecated, as defined in CEP 34.build: str. Build string, as defined in CEP 34.build_number: int. Build number, as defined in CEP 34.channel: str: URL to source channel, without subdir, as defined in CEP 26.constrains: list[str]. Runtime constraints, as defined in CEP 34.depends: list[str]. Runtime requirements, as defined in CEP 34.extracted_package_dir: str: Absolute path to extracted contents of the artifact.files: list[str]: list of installed paths that are owned by this artifact, relative to$CONDA_PREFIX, forward-slash normalized. It MUST include files that were not initially part of the package but were generated during the installation process (e.g.*.pycbytecode files).fn: str. Filename of compressed artifact, as defined in CEP 26.license: str. SPDX license expression, as defined in CEP 34.link: dict[str, Any]: How the package was linked into the prefix. It MUST only allow two keys:source: str: The path of the extracted package directory.type: Literal[1, 2, 3, 4]: Type of linkage (1 = hardlink, 2 = softlink, 3 = copy, 4 = directory).
md5: str: Hexadecimal string of MD5 hash, as defined in CEP 34.name: str: Lowercase name of the package, as defined in CEP 26.noarch: Literal['generic', 'python']: Optional. Noarch type, as defined in CEP 34.package_tarball_full_path: str: Absolute path to downloaded artifact (compressed).paths_data: dict[str, Any]: Metadata about the artifact installed contents, which includes the artifact distributed files, and the generated files at install time. It MUST be a mapping with two keys:paths: list[dict[str, Any]]: Information about installed files. Extends CEP 34'spaths.jsonwith some extra details:_path: str: Relative path of file within$CONDA_PREFIX, forward-slash normalized.file_mode: Literal['text', 'binary']: Optional, defaults totext. How to perform prefix replacement.no_link: bool: Optional, defaults tofalse. Whether to force copy or allow link.path_type: Literal['softlink', 'hardlink', 'directory', 'pyc_file', 'unix_python_entry_point', 'windows_python_entry_point_script', 'windows_python_entry_point_exe', 'linked_package_record']: Optional, defaults tohardlink. How the file was written to$CONDA_PREFIX, which includes what type of generated file it is, if applicable.prefix_placeholder: str: Optional. String that MUST be replaced with the target location at$CONDA_PREFIX.sha256: str: Optional if the file is generated. 64-char hex string corresponding to the SHA256 checksum of the original file in cache.sha256_in_prefix: str: Optional if generated. 64-char hex string corresponding to the SHA256 checksum of the file as installed in the target prefix. This MAY be different fromsha256due to prefix replacement.size_in_bytes: int: Optional if generated. Size of file, in bytes.
paths_version: int: Version of this schema. Currently,1.
platform: str | None. Deprecated, as defined in CEP 34.requested_spec: str: Deprecated, userequested_specsinstead.MatchSpecstring (as defined in CEP 29) that led to choosing this package.requested_specs: list[str]: List ofMatchSpecstrings (as defined in CEP 29) that led to choosing this package.sha256: str: Hexadecimal string of SHA256 hash, as defined in CEP 34.size: int: Size, in bytes, of compressed artifact, as defined in CEP 34.subdir: str: Subdir string, as defined in CEP 34.timestamp: int: Moment the package build started, as defined in CEP 34.url: str: Direct URL to download the artifact. It SHOULD be the result of joining thechannelURL plussubdir(or itsbase_urlfield as defined in CEP 15) plus thefnfield.version: str: Package version, as defined in CEP 33.
The fields above that also appear in CEP 34's info/index.json MUST match the relevant fields in the most up-to-date repodata information available for the package at install time (depends and constrains are of particular importance due to repodata patching).
This is generally the channel's repodata.json, but it MAY also be an alternative source like the serialized metadata in a lockfile. The package's info/index.json SHOULD be used as a fallback if no other sources are available.
Additional keys MAY be present in the file and MUST be ignored if not recognized.
./conda-meta/frozen
Optional.
Empty file or JSON document that MUST follow CEP 22.
./conda-meta/state
Optional.
JSON document that MUST provide a dictionary with a single key, env_vars, whose value is a dictionary that maps strings to strings. These are environment variable names and their values, respectively.
conda clients SHOULD parse this document and export the environment variables on environment activation, and unset them on deactivate.
General contents
The rest of the environment is generally populated by the contents of its installed packages, after extraction and linking. As a result, the structure is arbitrary and determined by which packages are installed. Refer to CEP 34 for more details on which conventions to follow.
Packages MAY include files in some special paths that conda clients SHOULD handle in a specific way:
./etc/conda/*.ddirectories./(bin|Scripts)/.{package-name}-{action}.{extension}scripts./condarcconfiguration files
etc/conda/*.d/ directories
The following files and directories MUST be handled by the conda client:
./etc/conda/env_vars.d/: Directory containing JSON documents, each providing a dictionary that maps strings to strings, which are understood as environment variables. The JSON documents SHOULD be loaded alphabetically, with later documents overriding earlier ones. These environment variables SHOULD be exported on environment activation, and unset on deactivation. When bothenv_vars.d/andconda-meta/stateare present, the latter is loaded last and can override the previously defined variables../etc/conda/activate.d/: Directory containing shell scripts. They SHOULD be executed on environment activation, in alphabetical order../etc/conda/deactivate.d/: Directory containing shell scripts. They SHOULD be executed on environment deactivation, in reverse alphabetical order.
Alphabetical order, in this case, MUST be understood as the ascending lexicographical order provided by Unicode Code Point comparisons, just like Python would sort such a list.
Pre- and post-link/unlink scripts
conda clients SHOULD execute scripts located under ./bin/ (Unix) or ./Scripts/ (Windows) with the syntax .{package-name}-{action}.{extension}, where {package-name} corresponds to the package name, {extension} is either sh (Unix) or bat (Windows), and {action} is one of:
pre-link: Executed before the corresponding package is installed / linked.post-link: Executed after the corresponding package is installed / linked.pre-unlink: Executed before the corresponding package is removed / unlinked.post-unlink: Executed after the corresponding package is removed / unlinked. Deprecated; conda clients SHOULD ignore them.
These scripts should be avoided whenever possible. If they are indeed necessary, these rules apply:
- They MUST NOT write to stdout, but they MAY write to
$CONDA_PREFIX/.messages.txt, whose contents SHOULD be reported after the conda client completes all actions. - They MUST NOT touch anything other than the files being installed.
- They MUST NOT depend on any installed or to-be-installed conda packages.
- They SHOULD depend only on standard system tools such as
rm,cp,mv, andln.
Execution SHOULD be performed in topological order. The conda client SHOULD export these environment variables for the scripts to use:
ROOT_PREFIX: Path to the conda client installation root, when applicable.PREFIX: Path to the environment where the package is installed.PKG_NAME: Name of the package.PKG_VERSION: Version of the package.PKG_BUILDNUM: Build number of the package.
Top-level condarc files
Environments MAY include files in these locations which affect the behavior of the conda client performing operations on this environment. The following locations are recognized as valid configuration sources.
./.condarc./condarc./condarc.d/*.yml./condarc.d/*.yaml
Management of a conda environment
Creating a conda environment
An empty directory $CONDA_PREFIX can be turned into a conda environment by creating an empty conda-meta/history file. The conda client MAY register this location into a central registry of environments, such as ~/.conda/environments.txt.
The command used to create that environment MAY be recorded in conda-meta/history, along with the version, timestamp and details of the transaction.
Installing packages
For each package to be installed, conda clients:
- SHOULD download or copy the artifact to the cache location using the "filenames" syntax described in CEP 26.
- SHOULD, if available, verify the integrity of the artifact with its checksum (
sha256is preferred overmd5). - MUST extract and aggregate the artifact to a central location. The extracted directory SHOULD be named as a distribution string (without subdir), as described in CEP 26.
- SHOULD write an
./info/repodata_record.jsonfile in the extracted directory, whose contents MUST be populated with one of:- The
RepodataRecordinformation available in the lockfile, if relevant. - The
RepodataRecordinformation available in the repodata source, if relevant. - The contents of
./info/index.json, as a fallback.
- The
Once extracted, the packages MUST be installed in the target prefix $CONDA_PREFIX by following these steps:
- Execute the relevant
pre-linkscripts. - Link or copy the non-
info/contents of the package into$CONDA_PREFIX. Tools SHOULD follow the manifest file atinfo/paths.json. This means that:- For non-
noarch: pythonpackages, place the contents of the artifact into$CONDA_PREFIX.- If the file contains a prefix placeholder, replace it with the value of
$CONDA_PREFIXand copy the file. - Otherwise, place the file in
$CONDA_PREFIX, as instructed by thepaths.jsonmetadata. Tools MAY offer settings to override this operation (e.g. prefer copies to hardlinks).
- If the file contains a prefix placeholder, replace it with the value of
noarch: pythonpackages follow some extra rules. In particular, they no longer follow a 1:1 correspondence between the path in the artifact and the linked path in$CONDA_PREFIX. The target path depends on variables like the Python version, OS and Python ABI modes. Details are discussed in CEP 17 and CEP 20.
- For non-
- Execute the relevant
post-linkscripts. - Record the package metadata at
$CONDA_PREFIX/conda-meta/{name}-{version}-{build}.json, as instructed above.
While linking paths into their targets, the conda client MAY run into clobbering conflicts (two or more packages wanting to write to the same path). Tools SHOULD at least warn the user about the conflicts and provide ways to handle the situation.
Removing a package
Removing a package from the environment SHOULD follow these instructions:
- Execute the relevant
pre-unlinkscripts. - Remove all the files recorded in its
conda-meta/*.jsonfile (underpaths_data). - Legacy only. Execute the relevant
post-unlinkscripts. - Remove its
conda-meta/*.jsonrecord. - If there were clobbering conflicts, restore the relevant path from the clobbered sources.
Removing an environment
Once an environment contains no packages, the conda client MAY remove it. This process involves clearing the conda-meta/ folder and any condarc files, and deregistering the environment path from the central manifest, if applicable (e.g. ~/.conda/environments.txt). If there were any additional files in the environment directory, the conda client SHOULD report that to the user and offer to leave them in place or to proceed and clear all the contents.
Activating and deactivating an environment
Environment management tools SHOULD implement a mechanism to activate an environment. They MAY also implement logic to deactivate it.
Given a target environment located at $PREFIX, the activation logic MUST involve the following tasks:
- Temporarily modifying the
PATHenvironment variable to include directories that usually contain executables, as discussed in CEP 34:- On Unix systems, it MUST include
$PREFIX/bin. - On Windows systems, it MUST include (in this order):
$PREFIX.- The first directory that exists in this list, if any:
$PREFIX/Library/ucrt64/bin,$PREFIX/Library/clang64/bin,$PREFIX/Library/mingw64/bin,$PREFIX/Library/clangarm64/bin. $PREFIX/Library/mingw-w64/bin.$PREFIX/Library/usr/bin.$PREFIX/Library/bin.$PREFIX/Scripts.$PREFIX/bin.
- On Unix systems, it MUST include
- Handling the activation logic detailed in the
etc/conda/*.ddirectories section.
For deactivation, the effects of the actions above MUST be reverted.
References
Copyright
All CEPs are explicitly CC0 1.0 Universal.