Forget your environment sorrows
nepenthe builds, versions, and distributes large shared conda/PyPI environments. It solves a dependency manifest with rattler and records the result in a portable lockfile. Publish that lock to a versioned registry so other machines can install the same packages without re-solving.
- Manifests describe dependencies and compose environments.
- Rust solver uses rattler without a Python interpreter.
- Multi-platform locks store
linux-64,osx-arm64, andwin-64environments in onerattler_lockfile, solved from one host. - Storage backends read and write specs using
file://,s3://, andhttps://; package channels can use servers such as Artifactory. - Versioned registry tracks each environment independently and stores immutable, content-addressed locks.
- Installation links packages into a prefix with rattler; conda, mamba, and micromamba are not required.
- Conda export converts a lock to an
@EXPLICITspec forconda create --file. - Credentials are supplied at use time and never written into artifacts.
The CLI and Python API support building, publishing, and installing environments.
Use pack / unpack bundles for air-gapped machines and pyproject.toml
integration to check a project's dependencies against its shared environment.
Frozen environments keep package versions consistent across machines and over time. Maintaining every pin by hand makes upgrades difficult and can lead to dependency hell.
With nepenthe, you specify dependencies and the version constraints you need. Each release solves that list and freezes the result. Consumers install those packages without re-solving.
To update an environment, edit its root dependencies and build a new release. Teams can choose when to adopt it.
Unlike per-project lockfiles (pixi, uv) and re-solved manifests
(environment.yml), nepenthe distributes versioned, immutable environments for
multiple repos to share. See
Motivation for the full comparison.
| For | Read |
|---|---|
| Why it exists: the problem and alternatives | Motivation |
| Users: concepts and how-to guides | The wiki: Concepts, Manifests, Channels & Artifactory, Registry, Installing, Consuming in a Project, Packing, Python API, Backends |
| Getting started | Installation |
| Contributors: how it's built | Architecture |
Create a manifest with the dependencies and version constraints you need:
# environment.yaml
project:
name: demo
channels: [conda-forge]
platforms: [linux-64]
python: ["3.11"]
dependencies:
- numpy >=2
- pip
environments:
app: []Build a fully pinned lockfile without a registry or conda:
nepenthe build --manifest environment.yaml --env app --output-dir .
# → app-py3.11.lock the reproducible artifact: every install gives these exact packagesExport the lock as an @EXPLICIT spec for installation with conda or mamba:
nepenthe export --env app --lock app-py3.11.lock --platform linux-64 -o app.txt
# → app.txt @EXPLICIT list of package URLsInstall from the spec with conda, or publish the lock and install with nepenthe:
# with conda/mamba, from the spec — nepenthe not needed on this machine
conda create --name app --file app.txt
# …or publish the lock to a versioned registry and install with nepenthe — no conda:
nepenthe build --manifest environment.yaml --env app \
--registry file:///srv/nepenthe --version 1.0.0
nepenthe create app --registry file:///srv/nepenthe --python 3.11 --prefix ./envs/app
eval "$(nepenthe activate --prefix ./envs/app)"The CLI is a single multicall binary: np and npb are symlinks to nepenthe
(dispatch on the invoked name). np behaves exactly like nepenthe, and npb
is a shortcut for nepenthe build (so the build step above is just
npb --manifest environment.yaml --env app --output-dir .).
The Python API provides the same operations without requiring conda:
import nepenthe
# producer: solve "app" and publish its lock as v1.0.0
nepenthe.build("environment.yaml", "app", registry="file:///srv/nepenthe", version="1.0.0")
# consumer: install the latest published lock into a prefix
nepenthe.create("app", "file:///srv/nepenthe", "./envs/app")
print(nepenthe.activate("./envs/app"))A repo can pin its shared environment in pyproject.toml and check that its
dependencies are compatible:
[tool.nepenthe]
environment = "app"
registry = "file:///srv/nepenthe"
version = "1.0.0"nepenthe sync # install the referenced environment into .venv
nepenthe check # verify [project.dependencies] are compatible (fails on conflicts)See Installing Environments for the complete CLI
reference, Consuming in a Project for the
pyproject.toml workflow, Python API for the bindings,
Manifests for the manifest format, and the
runnable rust/examples/ for the producer side.
Contributions are welcome under the Apache 2.0 license. See Contributing and Build from Source.
Apache-2.0. See LICENSE for details.