Quick start#
Installation#
conda install -c conda-forge conda-workspaces
pixi global install conda-workspaces
Both methods provide the cw and ct shortcut commands.
Installing into a conda base environment also registers the
conda workspace and conda task plugin subcommands.
Your first tasks#

Create a conda.toml in your project root:
[tasks]
hello = "echo 'Hello from conda-workspaces!'"
test = { cmd = "pytest tests/ -v", depends-on = ["build"] }
build = "python -m build"
Run a task:
conda task run hello
conda task run test # runs build first, then test
conda task list # see all tasks
Tasks run in your current conda environment. No workspace definition is required — you can start with tasks alone and add workspace features later.
Your first workspace#

Added in version 0.4.0: conda workspace quickstart composes init, add, install, and
shell into a single bootstrap command; pass --no-shell for CI or
--json for a scriptable summary.
The fastest path from “empty directory” to “installed environment with
an activated shell” is conda workspace quickstart:
conda workspace quickstart python=3.14 numpy
# or copy an existing workspace's manifest instead of running init
conda workspace quickstart --copy ../other-workspace
# scripted / CI: skip the interactive shell, emit a JSON summary
conda workspace quickstart --no-shell --name demo "python=3.12" "numpy>=2"
conda workspace quickstart --no-shell -e dev "python=3.12" pytest
conda workspace quickstart --json --name demo "python=3.12"
quickstart composes the other commands for you: it runs init
(unless you pass --copy / --clone to copy an existing workspace’s
manifest), adds any specs passed on the command line, installs the
selected environment, and drops into a shell. It forwards the flags
you already know from init (--format, --name, -c/--channel,
--override-channels, --platform), install (-e/--environment,
--force-reinstall, --locked, --frozen), and conda’s shared flags
(--dry-run, --json, --yes). Use --no-shell for CI or scripted
runs. --json implies --no-shell, silences the status banners the
nested init / add / install handlers would otherwise print, and
emits a single structured {workspace, environment, manifest, specs_added, shell_spawned} payload on stdout — safe to pipe into
jq.
Positional specs are added as private dependencies of the selected
environment, including the default environment. Quickstart creates the
environment when necessary, writes the specs below
[environments.<name>.dependencies], and installs its
.conda/envs/<name> prefix. To share dependencies across environments,
run conda workspace init followed by conda workspace add without
-e/--environment.
--locked and --frozen cannot be combined with positional specs.
Adding specs changes the manifest and requires a new lock, so
quickstart rejects that combination before creating or copying a
manifest. Omit the lock mode while bootstrapping specs, then use
conda workspace install --locked or --frozen for later installs.
New workspaces inherit conda’s configured channels in their existing
order. Repeated -c/--channel values are prepended in command-line
order. Pass --override-channels with at least one -c to use only
the explicit channels. Initialization fails when no channel is
available. quickstart --copy and --clone preserve the source
manifest’s channels instead of applying these initialization options.
Inherited channel URLs are written without basic authentication,
Anaconda /t/<token>/ path segments, queries, or fragments. Keep the
credential-free channel identity in the manifest and configure authentication
through Conda outside the repository.
Manual setup#

If you prefer to wire the commands together yourself, start from
conda workspace init and incrementally add dependencies — each
add installs into the affected environment and refreshes
conda.lock:
conda workspace init --name my-project
conda workspace add "python>=3.12" "numpy>=2"
conda workspace add --feature test "pytest>=8.0"
conda workspace envs
Or add workspace configuration to a conda.toml by hand:
[workspace]
name = "my-project"
channels = ["conda-forge"]
platforms = ["linux-64", "osx-arm64", "win-64"]
[dependencies]
python = ">=3.10"
numpy = ">=1.24"
[feature.test.dependencies]
pytest = ">=8.0"
pytest-cov = ">=4.0"
[environments]
default = []
test = { features = ["test"] }
[workspace]
name = "my-project"
channels = ["conda-forge"]
platforms = ["linux-64", "osx-arm64", "win-64"]
[dependencies]
python = ">=3.10"
numpy = ">=1.24"
[feature.test.dependencies]
pytest = ">=8.0"
pytest-cov = ">=4.0"
[environments]
default = []
test = { features = ["test"] }
[tool.conda.workspace]
name = "my-project"
channels = ["conda-forge"]
platforms = ["linux-64", "osx-arm64", "win-64"]
[tool.conda.dependencies]
python = ">=3.10"
numpy = ">=1.24"
[tool.conda.feature.test.dependencies]
pytest = ">=8.0"
pytest-cov = ">=4.0"
[tool.conda.environments]
default = []
test = { features = ["test"] }
Tip
cw and ct are available as shorter aliases for conda workspace
and conda task.
Or use the init command to scaffold one:
conda workspace init
# or: conda workspace init --format conda
# or: conda workspace init --format pyproject
Install environments#
conda workspace install
This creates project-local conda environments under .conda/envs/ for
each environment defined in your manifest. A conda.lock file is
generated automatically after solving.
You can select an exact manifest with the global --file / -f
option:
conda workspace --file path/to/conda.toml install
The path must name a manifest file. Omit the option to auto-detect a manifest by searching the current directory and its parents.
To recreate environments from scratch, use --force-reinstall. This also
recreates prefixes when installation uses --locked, --frozen, or CI strict
mode:
conda workspace install --force-reinstall
The flag does not force a new solve. Combine it with --no-lock when both a
fresh solve and prefix recreation are required.
To preview a workspace change without writing it, pass --dry-run:
conda workspace quickstart "python>=3.12" --dry-run
conda workspace add "pandas>=2" --dry-run
conda workspace install --force-reinstall --dry-run
conda workspace lock --dry-run
A preview may parse manifests, validate inputs, and run the solver. It
does not modify manifests, lockfiles, environment prefixes, activation
metadata, archives, receipts, or extraction targets, and it does not
modify configured package caches. quickstart validates a temporary
prospective manifest and discards it afterward.
Lock#

The conda workspace lock command runs the solver and records the solution in
conda.lock without installing any environments:
conda workspace lock
Reproducible installs#
Use --locked to install from the lockfile. This validates that the
lockfile is still fresh relative to the manifest — if the manifest has
changed, the install fails:
conda workspace install --locked
Use --frozen to install from the lockfile as-is, without checking
freshness:
conda workspace install --frozen
Run in workspace environments#
Once your workspace is installed, run tasks in specific environments:
conda task run -e test pytest -v
Run a one-shot command in an environment:
conda workspace run -e test -- python -c "import numpy; print(numpy.__version__)"
Or spawn an interactive shell:
conda workspace shell -e test
Add and remove dependencies#
Changed in version 0.4.0: conda workspace add / remove now install into the affected
environment(s) and refresh conda.lock by default (matching
pixi add / pixi remove). Use --no-install to update the
manifest and lockfile without touching the prefix, or
--no-lockfile-update for the previous manifest-only behaviour.
conda workspace add numpy
add and remove update the manifest, install into the affected
prefixes, and refresh a complete conda.lock in one go — the same
shape as pixi add / pixi remove.
For conda workspace add, a bare package name preserves an existing
declaration. An explicit MatchSpec replaces the whole declaration, and
rich fields such as channel or build are written as an inline
table. Use an explicit wildcard such as numpy=* to clear prior
constraints. Unsupported MatchSpec fields fail before the manifest is
written. If the existing entry is { workspace = true }, a bare add
keeps that marker. An explicit spec replaces only that membership entry
and leaves [workspace.dependencies] unchanged.
Add to a specific feature (only prefixes for environments composing that feature are installed or updated):
conda workspace add --feature test pytest
Add a dependency directly to one environment without changing its shared features:
conda workspace add --environment test coverage
--feature and --environment are mutually exclusive. Environment
dependencies are written below [environments.<name>], and only that
prefix is installed. --platform composes with either selector and
addresses a target table below the selected location. The complete
mapping is in the dependency mutation rules.
For example:
conda workspace add --platform osx-arm64 llvm-openmp
conda workspace add --feature test --platform win-64 "pytest<9"
conda workspace add --environment test --platform win-64 pywin32
Add a PyPI dependency:
conda workspace add --pypi requests
Remove a dependency:
conda workspace remove numpy
Update one or more declared conda roots without replacing their manifest constraints:
conda workspace update numpy scipy
Like add and remove, update addresses one exact declaration
location. Use --feature, --environment, and --platform for named
feature, private environment, and target-specific declarations:
conda workspace update --feature test pytest
conda workspace update --environment test coverage
conda workspace update --platform win-64 pywin32 --no-install
Bare names preserve existing constraints. Explicit MatchSpecs replace
the selected declaration. update manages conda dependencies only, updates
affected installed host prefixes, and leaves uninstalled prefixes absent.
A normal update requires at least one affected host prefix to be installed.
Use --no-install when none is installed or for a lock-only update, including
a target that does not match the host.

Removal clears direct prefix requests that are absent from the resolved
manifest before installing the remaining dependency closure. A removed
package stays installed only when another dependency requires it.
Lock generation does not inherit stale prefix requests, including when
--no-install leaves the existing prefix unchanged. A later
conda workspace install reconciles that prefix to the lock. Conda
packages added directly to a workspace prefix are removed when they
are absent from the lock, so declare additions in the manifest.
If you want the old manifest-only behaviour, or to stage a batch of edits before running the solver, opt out per command:
conda workspace add numpy --no-install # update manifest + complete conda.lock, skip install
conda workspace add numpy --no-lockfile-update # update manifest only
conda workspace add numpy --force-reinstall # recreate the affected env(s) from scratch
conda workspace add numpy --dry-run # solve only, touch nothing on disk
Running add or remove from inside conda workspace shell works,
but an already-activated shell will not pick up new entries under
$PREFIX/etc/conda/activate.d/ — the command prints a hint asking
you to exit and re-run conda workspace shell when that happens.
List packages and environments#
conda workspace list # packages in default env
conda workspace list -e test # packages in test env
conda workspace envs # list defined environments
Workspace overview#
conda workspace info
conda workspace info -e test # details for a specific environment
conda workspace info --json # complete machine-readable snapshot
conda workspace info --json --packages # include installed package records
Next steps#
Read about features to learn how environments and tasks work
See the configuration reference for all manifest options
Check out the tutorials for more in-depth guides