How to create a Python uv project on NixOS
Make uv usable on NixOS
Developing Python projects on NixOS is usually a little bit more complicated than doing so on other Linux distributions. This so because, as you may know, NixOS does not respect the Filesystem Hierarchy Standard (FHS) like the others. When a dependency is searching for an external library, it normally searches at /usr/lib or /usr/local/lib, nowhere to be found on NixOS and this leads to an error. To put things in context, you might like this video from Vimjoyer explaining the issue while proposing some solutions:
Today, I’m going to talk about how you can use both uv and NixOS together to manage your Python project. Using this method, you will have a standard Python uv project that other people can use or develop on other platforms while still being able to build your project on NixOS.
The main ingredients to the recipe are uv2nix and divenv. uv2nix will parse our pyproject.toml file to find the library needed for our dependencies, and divenv will help us automate the process.
Setting up direnv
With direnv, we can enable an update each time we cd into our working directory.
Add direnv to your global configuration and activate it:
programs.direnv = {
enable = true;
nix-direnv.enable = true;
};2. In your project, or latter if you don’t have a project yet, add a file named .envrc at its root:
use flakeThank you, trinityforce for this one.
Creating a Python uv project
Let’s create a new Python project together to put this is practice:
In a terminal, create a new folder and move in:
mkdir uv-project && cd uv-project2. For automation, add the .envrc file I previously talked about:
echo “use flake” > .envrcI created two templates of uv2nix to be up-to-date with Python 3.13 and 3.14.
3. Create the project for Python 3.14 (or use #python313 for the previous version):
nix flake init --template github:Pierre-Thibault/my-uv2nix#python3144. Make this a Git repository for our flake to work correctly:
git init && git add --all5. Run direnv allow to approve its content:
direnv allowOur environment is now activated.
6. You should be able to run a Python shell:
python8. Exit the Python shell. Run our script:
uv run helloIt should print:
Hello from hello-world!9. Add a uv dependency:
uv add numpy10. Test that our dependency is present:
python
import numpyThis should give an error:
Traceback (most recent call last):
File “<python-input-0>”, line 1, in <module>
import numpy
ModuleNotFoundError: No module named ‘numpy’This is because our Nix environment is not up-to-date. Exit the Python shell. Let’s fix it.
11. To update the environment, you can go out and go back to the project directory (this is why we created the .envrc file). Or, we can use direnv reload.
direnv reload12. Test back that our dependency is present:
python
import numpyNow our environment is up-to-date, so we have no error. We can start working on our project with the template as a starting point.
So, the development cycle here is to use uv to add or remove dependencies and use direnv reload to update the Nix side.
If you are using an external tool like Pycharm for example, you may want to create a symbolic link to the Python located in the nix store and use the link in the tool to ease updates.
ln -s `which python` pythonThis recipe makes our project sharable with other uv users regardless of their platform while still being able to develop on NixOS.
Note that this setup is still new to me and I did not use it on a real project yet. If you encounter an issue, please let me know in the comments.
Happy coding!
