Multi Platform
Pixi's vision includes being supported on all major platforms. Sometimes that needs some extra configuration to work well. On this page, you will learn what you can configure to align better with the platform you are making your application for.
Here is an example manifest file that highlights some of the features:
[workspace]
# Default workspace info....
# A list of platforms you are supporting with your package.
platforms = ["win-64", "linux-64", "osx-64", "osx-arm64"]
[dependencies]
python = ">=3.8"
[target.win-64.dependencies]
# Overwrite the needed python version only on win-64
python = "3.7"
[activation]
scripts = ["setup.sh"]
[target.win-64.activation]
# Overwrite activation scripts only for windows
scripts = ["setup.bat"]
[tool.pixi.workspace]
# Default workspace info....
# A list of platforms you are supporting with your package.
platforms = ["win-64", "linux-64", "osx-64", "osx-arm64"]
[tool.pixi.dependencies]
python = ">=3.8"
[tool.pixi.target.win-64.dependencies]
# Overwrite the needed python version only on win-64
python = "~=3.7.0"
[tool.pixi.activation]
scripts = ["setup.sh"]
[tool.pixi.target.win-64.activation]
# Overwrite activation scripts only for windows
scripts = ["setup.bat"]
Platform definition#
The workspace.platforms defines which platforms your workspace supports.
When multiple platforms are defined, Pixi determines which dependencies to install for each platform individually.
All of this is stored in a lock file.
Running pixi install on a platform that is not configured will warn the user that it is not setup for that platform:
❯ pixi install
WARN Not installing dependency for (default) on current platform: (osx-arm64) as it is not part of this project's supported platforms.
Declaring virtual packages per platform#
A bare-string entry like "linux-64" is shorthand for "the conda subdir linux-64 with Pixi's default declared virtual packages".
You can also describe a platform as an inline table to pin the virtual packages the solver should treat as available.
You can for example add a CUDA toolkit version or a glibc minimum version as a virtual package.
Replaces [system-requirements]
These inline-table entries are the recommended way to declare CUDA, glibc, macOS, archspec, and similar constraints. The older [system-requirements] table still parses but is deprecated; see Migrating from [system-requirements] for the equivalent forms.
[workspace]
platforms = [
"osx-arm64",
{ platform = "linux-64", cuda = "12.0", glibc = "2.28" },
{ name = "jetson-nano", platform = "linux-aarch64", cuda = "12.8" },
]
Each inline-table entry has:
platform: the conda subdir the entry targets (e.g.linux-64,osx-arm64). Required.name: optional workspace-scoped identifier the platform is referenced by elsewhere (infeature.<name>.platforms, in lockfile rows, in CLI commands). When omitted, Pixi synthesizes a name fromplatformplus the declared virtual packages, so two entries that declare the same set in different key order share the same identifier.- Friendly keys for the common virtual packages:
cuda,amdgpu,archspec,glibc,linux,macos(aliasosx),windows. Each maps onto the matching__nameconda virtual package (cuda->__cuda,amdgpu->__amdgpu,glibc->__glibc,macos->__osx, etc.). archspecnames a CPU microarchitecture (x86_64_v3,skylake,m1,armv8.2a, ...) rather than a version. The name must be one the bundled archspec database knows, and Pixi rejects anything else with a suggestion: a name no host can ever report would otherwise silently produce a platform that never matches. Conda build strings cannot contain-, so the spelling uses underscores and dots (x86_64_v3, notx86-64-v3). A CPU newer than the bundled database can't be named until Pixi ships an updated archspec; setarchspec = "0"to declare the microarchitecture explicitly unknown. Unlike many other virtual packages, a machine does not satisfyarchspecby carrying a high enough version. It satisfies it by being that microarchitecture or one the archspec database says is a strict superset of it, so azen2host covers a platform declaringx86_64_v3but ahaswellhost does not cover one declaringzen2. A machine whose own microarchitecture is unknown is accepted, since Pixi can neither prove nor disprove that it covers the requirement.-
cudaalso accepts a{ driver, arch }table that declares the CUDA driver version (__cuda) together with the GPU compute capability (__cuda_arch):pixi.tomlplatforms = [ { name = "gpu", platform = "linux-64", cuda = { driver = "12.0", arch = "8.6" } }, ]driveris exactly equivalent to the barecuda = "12.0"form. Per the conda CEP,__cuda_archis meaningless without__cuda, soarchrequiresdriver; declaringarch(or a raw__cuda_arch) alone is rejected.
-
amdgpu = truedeclares__amdgpu, which only states that an AMD GPU is present. An AMDGPU target name (gfx90a,gfx1100, ...) additionally declares the GPU architecture as__amdgpu_arch, which conda stores as an ISA version (gfx90ais9.0.10):pixi.tomlplatforms = [ { name = "rocm", platform = "linux-64", amdgpu = true }, { name = "mi250", platform = "linux-64", amdgpu = "gfx90a" }, ]A raw
__amdgpu_archtakes the ISA version and is rejected without__amdgpu. Concrete AMDGPU architectures must match exactly: a higher ISA version does not imply compatibility with a lower one. Unlike CUDA compute capability,amdgpu = "gfx90a"is not a minimum architecture requirement.
- For virtual packages without a friendly key, a raw
__name = "version"entry is also accepted as an escape hatch, for example__conda_forge_openmpi = "5.0"for a name a channel-registered detector reports. The solver treats it as available exactly like the friendly keys, and a machine satisfies it when it provides that name with the same build string at the declared version or newer (__amdgpu_archrequires the exact version). Pixi's built-in detection only reports the names it knows (__win,__osx,__linux,__cuda,__cuda_arch,__amdgpu,__amdgpu_arch,__archspec, and the libc family__glibc/__musl/__eglibc); any other name has to come from a detector for the host check to pass.
A feature's platforms array is a list of names that must each resolve to a workspace platform (or be a bare conda subdir, which Pixi treats as an alias for that subdir).
This is how you bind a feature to the rich variant:
[workspace]
platforms = [
"osx-arm64",
{ platform = "linux-64", cuda = "12.0" },
]
[feature.gpu]
platforms = ["linux-64-cuda-12-0"] # the synthesized name for the entry above
Platform names in pixi.lock
Platforms are written to pixi.lock under the same name the manifest
uses (explicit or synthesized), so tools that consume the lock file can
look up a platform by its manifest name. Renaming a platform in
pixi.toml never requires a re-solve: Pixi matches the locked entries to
the manifest by their contents (subdir plus declared virtual packages)
when the lock file is read, and the next pixi lock rewrites just the
names. Lock files from older Pixi versions that used short aliases
(p1, p2, ...) are matched the same way and updated on their next
write.
Adding the current machine#
To get binaries optimized for the machine you are on, let Pixi detect it for you instead of writing the inline table by hand:
With --auto-detect, Pixi resolves the current subdir and the virtual packages it detects on the host (macOS version, glibc, archspec, CUDA, ...) into a concrete platform entry.
It inserts this platform first in platforms, so it wins platform selection on this machine.
Because it writes a normal entry to pixi.toml, the result is checked in and shared with everyone using the workspace.
# Give the detected platform a custom name instead of the synthesized one.
pixi workspace platform add my-laptop --auto-detect
# Override individual virtual packages on top of what was detected.
pixi workspace platform add --auto-detect --cuda 12.4
Pixi deduplicates by definition (subdir plus declared virtual packages), not by name: if an entry with the same definition already exists it is reused and moved to the front rather than duplicated. Adding a platform whose definition already exists under a different name is rejected: two names for one definition would only produce a redundant duplicate solve.
Trim it for portability
Auto-detection captures your machine exactly, which is usually more specific than your packages actually need.
After installing, pixi info reports each environment's Minimum platform (the virtual-package requirements some resolved dependency really places on the machine), so you can see which ones are safe to drop with pixi workspace platform edit.
Virtual package detectors#
Channels can register virtual package detectors: packages that report system capabilities, such as an MPI runtime or a driver installed outside the environment. Pixi uses their results to check whether your machine satisfies a platform's declared virtual packages. Detectors can extend or replace Pixi's built-in detection.
Pixi runs detectors when their virtual packages are needed for the current machine, not for foreign target platforms. Workspace lock-file solves use the virtual packages declared in the manifest, not detected values.
Detectors execute code from the channel, so Pixi asks whether to trust them before running them. You can save the decision in the configuration. Without a terminal, detectors without a saved decision are skipped.
Managing platforms from the CLI#
pixi workspace platform is the CLI surface for these entries:
pixi workspace platform add <PLATFORM> [--cuda 12.0] [--cuda-arch 8.6] [--amdgpu] [--amdgpu-arch gfx90a] [--glibc 2.28] ...appends bare subdirs or rich platforms (or the current machine via--auto-detect, see above).--cuda-archrequires--cuda(or an existing__cuda) and serializes ascuda = { driver, arch }.--amdgpu-archalso declares__amdgpuand serializes asamdgpu = "gfx90a". Other virtual packages use__name[=version[=build_string]]positionals, for example__site_service=2=h1.pixi workspace platform edit <NAME> [--cuda 12.1] [--remove-virtual-package __glibc]mutates a custom platform's declared virtual packages.pixi workspace platform move <NAME> --to-top | --to-bottom | --before <NAME> | --after <NAME>reorders an entry; since order is selection priority, this is how you promote or demote a platform.pixi workspace platform listinspects what is declared.pixi workspace platform remove <NAME>drops an entry.
The mutating subcommands keep pixi.lock in sync.
Target specifier#
With the target specifier, you can overwrite the original configuration specifically for a single platform.
If you are targeting a specific platform in your target specifier that was not specified in your workspace.platforms then Pixi will throw an error.
Dependencies#
It might happen that you want to install a certain dependency only on a specific platform, or you might want to use a different version on different platforms.
In the above example, we specify that we depend on msmpi only on Windows.
We also specifically want python on 3.8 when installing on Windows.
This will overwrite the dependencies from the generic set of dependencies.
This will not touch any of the other platforms.
You can use pixi's CLI to add these dependencies to the manifest file.
This also works for the host and build dependencies.
Which results in this.
[target.win-64.host-dependencies]
posix = "1.0.0.*"
[target.osx-64.build-dependencies]
clang = "16.0.6.*"
Activation#
Pixi's vision is to enable completely cross-platform workspaces, but you often need to run tools that are not built by your projects.
Generated activation scripts are often in this category, default scripts in unix are bash and for windows they are bat
To deal with this, you can define your activation scripts using the target definition.
[activation]
scripts = ["setup.sh", "local_setup.bash"]
[target.win-64.activation]
scripts = ["setup.bat", "local_setup.bat"]
win-64 it will only execute the target scripts not the scripts specified in the default activation.scripts
Wildcard platform selectors#
When several workspace platforms share configuration, you can match them with a * wildcard in the target selector instead of repeating each block.
The pattern is matched against the platform name, so it is most useful together with custom platform names:
[workspace]
platforms = [
{ name = "cuda-win-64", platform = "win-64", cuda = "12" },
{ name = "cuda-linux-64", platform = "linux-64", cuda = "12" },
"win-64",
"linux-64",
]
[target."cuda-*".tasks]
test = "python test.py --cuda"
train = "python train.py --cuda"
Here both cuda-win-64 and cuda-linux-64 pick up the test and train tasks, while the bare win-64 and linux-64 platforms do not.
A few details:
*is the only metacharacter and matches any run of characters. Patterns are matched in full and are case-sensitive (cuda-*,*-64,*cuda*).- When more than one selector matches a platform, the one defined later in the manifest wins, the same way
[target.linux]and[target.linux-64]already combine. Place a specific[target.cuda-win-64]override after the[target."cuda-*"]block. - Wildcards are only allowed on workspace and feature targets. They are rejected in
[package.target]and[package.build.target], which resolve by subdir.