How to write a recipe that depends on CUDA
Recipes for packages that depend on the CUDA Toolkit typically are more complicated compared to other compiled-code recipes. This guide covers best practices and common situations for recipes that depend on CUDA.
Add the CUDA compiler
Add the {{ compiler('cuda') }} template to your recipe to install NVCC and enable CUDA
variants in your feedstock. Remember to rerender.
- v0 (meta.yaml)
- v1 (recipe.yaml)
requirements:
build:
- {{ compiler('cuda') }}
requirements:
build:
- ${{ compiler('cuda') }}
Pin the host environment
Pin the cuda-version package to cuda_compiler_version in the host environment, so that
any host dependencies are compatible with the CUDA compiler.
- v0 (meta.yaml)
- v1 (recipe.yaml)
requirements:
host:
- cuda-version {{ cuda_compiler_version }}.*
requirements:
host:
- cuda-version ${{ cuda_compiler_version }}.*
Only use the required CUDA components
Do not use CUDA metapackages (e.g. cuda-toolkit) to specify dependencies. Instead, specify only the minimal subset of CUDA components that your package requires (e.g. libcufft-dev). Specifying a -dev/-devel package as a host requirement makes your package require the corresponding non-dev package at run time, because the -dev/-devel packages have run_exports.
Build both CUDA and non-CUDA variants
If your recipe has separate variants for CUDA and non-CUDA builds:
- Use the
cuda_compiler_versionvariable as a switch for the CUDA requirements. - Give the CUDA variant a higher build number than the non-CUDA variant, so the solver prefers it where it can be installed.
- Add the
__cudavirtual package as a runtime requirement of the CUDA variant. This makes the CUDA variant uninstallable on machines without a CUDA driver, which would otherwise download large GPU packages needlessly. Users without a CUDA driver get the non-CUDA variant instead.
For example:
- v0 (meta.yaml)
- v1 (recipe.yaml)
{% set build_number = 0 %}
build:
# Prefer the CUDA variant when it is installable
number: {{ build_number + (100 if cuda_compiler_version != "None" else 0) }}
requirements:
build:
- {{ compiler('cuda') }} # [cuda_compiler_version != "None"]
host:
- libcublas-dev # [cuda_compiler_version != "None"]
- cuda-version {{ cuda_compiler_version }}.* # [cuda_compiler_version != "None"]
run:
# Makes the CUDA variant uninstallable without a CUDA driver
- __cuda # [cuda_compiler_version != "None"]
context:
build_number: 0
build:
# Prefer the CUDA variant when it is installable
number: ${{ build_number + (100 if cuda_compiler_version != "None" else 0) }}
requirements:
build:
- if cuda_compiler_version != "None":
then: ${{ compiler('cuda') }}
host:
- if cuda_compiler_version != "None":
then:
- libcublas-dev
- cuda-version ${{ cuda_compiler_version }}.*
run:
# Makes the CUDA variant uninstallable without a CUDA driver
- if: cuda_compiler_version != "None"
then: __cuda
Support CUDA Enhanced Compatibility
If your package's code already supports CUDA Enhanced Compatibility, make these changes to the recipe so that the conda package supports it too:
- Ignore the run exports from any CTK library
-dev/-develpackages that your package depends on. Otherwise, the run exports would require runtime libraries with versions equal to or greater than the versions used to build the package. - Ignore the run exports of the
cuda-versionpackage. - Add explicit runtime dependencies (or constraints for soft/optional dependencies) that specify the desired version range for the dependencies whose run exports you ignored.
For example, because of run exports in the libcublas-dev package, a package that requires libcublas has a run requirement on libcublas corresponding to {{ cuda_compiler_version }} or newer. To make it compatible with all CUDA minor versions from the same CUDA major version family, add the following:
- v0 (meta.yaml)
- v1 (recipe.yaml)
build:
# Ignore run exports from CUDA libraries in the host section
ignore_run_exports_from:
- libcublas-dev
# Ignore cuda-version itself; do not ignore exports from
# compiler('cuda'), which has other needed exports
ignore_run_exports:
- cuda-version
requirements:
build:
- {{ compiler('cuda') }}
host:
- libcublas-dev
- cuda-version {{ cuda_compiler_version }}.*
run:
# Since we've ignored the run export from libcublas-dev, we pin
# libcublas manually. Having a pin_compatible on cuda-version with
# max_pin='x' and min_pin='x' will constrain libcublas to the same
# major version as what we pinned in host.
- {{ pin_compatible('cuda-version', max_pin='x', min_pin='x') }}
- libcublas
requirements:
build:
- ${{ compiler('cuda') }}
host:
- libcublas-dev
- cuda-version ${{ cuda_compiler_version }}.*
run:
# Since we've ignored the run export from libcublas-dev, we pin
# libcublas manually. Having a pin_compatible on cuda-version with
# max_pin='x' and min_pin='x' will constrain libcublas to the same
# major version as what we pinned in host.
- ${{ pin_compatible('cuda-version', max_pin='x', min_pin='x') }}
- libcublas
# Ignore run exports from CUDA libraries in the host section
ignore_run_exports:
from_package:
- libcublas-dev
# Ignore cuda-version itself; do not ignore exports from
# compiler('cuda'), which has other needed exports
by_name:
- cuda-version
Set CUDA architecture targets
Packages built on conda-forge should target broad compatibility across CUDA devices. This means passing explicit compiler arguments to NVCC to target all supported CUDA architectures instead of accepting the compiler's default target.
Use variables set by the CUDA compiler conda package
The CUDA compiler package activation script automatically sets the environment variables CUDAARCHS and NVCC_GENCODE to all supported CUDA architectures for the target host platform when CONDA_BUILD is detected and when they are not already set. Use these environment variables in your build script instead of hard-coding architectures. You then do not need to update your build scripts when new architectures become available in new releases of the CUDA Toolkit.
Use variables set by the PyTorch conda package
The libtorch/pytorch packages similarly set CF_TORCH_CUDA_ARCH_LIST in their activation scripts. To keep the CUDA targets of a PyTorch module up to date, set TORCH_CUDA_ARCH_LIST=${CF_TORCH_CUDA_ARCH_LIST} in your build environment.
For example, in your build script:
- build.sh
- bld.bat
export TORCH_CUDA_ARCH_LIST="$CF_TORCH_CUDA_ARCH_LIST"
set TORCH_CUDA_ARCH_LIST=%CF_TORCH_CUDA_ARCH_LIST%
Fix nvcuda.dll not found on Windows
The scripts
used to install the CUDA Toolkit on Windows cannot provide nvcuda.dll as part of the installation because no GPU is physically present in the CI machines.
As a result, you might get linking errors in the postprocessing steps of conda build:
WARNING (arrow-cpp,Library/bin/arrow_cuda.dll): $RPATH/nvcuda.dll not found in packages,
sysroot(s) nor the missing_dso_whitelist.
.. is this binary repackaging?
For now, you will have to add nvcuda.dll to the missing_dso_whitelist:
- v0 (meta.yaml)
- v1 (recipe.yaml)
build:
...
missing_dso_whitelist:
- "*/nvcuda.dll" # [win]
build:
...
dynamic_linking:
missing_dso_allowlist:
- if: win
then: "*/nvcuda.dll"
Adopt a new CUDA version
As new CUDA versions come out regularly, periodically conda-forge needs to decide how many versions will be supported within resource constraints. As of August 2026, conda-forge supports CUDA 12 and 13.
conda-forge manages the adoption of a new CUDA major version with a migration. The migration bot opens a pull request in your feedstock with the changes needed to build for the new version.
To update to the latest supported versions outside of a migration, or if your feedstock no longer builds old CUDA versions, rerender the feedstock. There may be other fixes needed for the feedstock depending on when it was last updated.