Hacker News new | ask | show | jobs
by gatvol 245 days ago
UV is super fast and great for environment management, however it's not at all well suited to a containerised environment, unless I'm missing something fundamental (unless you like using an env in your container that is).
10 comments

uv works great in a container, you can tell it to skip creating a venv and use the system's version of Python (in this case, let's say Python 3.14 from the official Python image on the Docker Hub).

The biggest wins are speed and a dependable lock file. Dependencies get installed ~10x faster than with pip, at least on my machine.

Both of my Docker Compose starter app examples for https://github.com/nickjj/docker-flask-example and https://github.com/nickjj/docker-django-example use uv.

I also wrote about making the switch here: https://nickjanetakis.com/blog/switching-pip-to-uv-in-a-dock...

> unless you like using an env in your container that is

A virtual environment, minimally, is a folder hierarchy and a pyvenv.cfg file with a few lines of plain text. (Generally they also contain a few dozen kilobytes of activation scripts that aren't really necessary here.) If you're willing to incur the overhead of using a container image in the first place, plus the ~35 megabyte compiled uv executable, what does a venv matter?

Works fine in containers as far as I can tell. I don't even bother configuring it to not use a venv. Doesn't hurt anything
Why not?

In my docker files I use `uv sync` to install deps vs `pip install -f requirements.txt`

And then set my command to `uv run my_command.py` vs calling Python directly.

> it's not at all well suited to a containerised environment

Could you elaborate?

Why not use a virtualenv in your container?
This is still a complete pain to work with. Virtualenv in general is a "worst of worlds" solution. It has a lot of the same problems as just globally pip installing packages, requires a bit of path mangling to work right, or special python configs, etc. In the past, it's also had a bad habit of leaking dependencies, though that was in some weird setups. It's one of the reasons I would recommend against python for much of anything that needs to be "deployed" vs throw away scripts. UV seems to handle all of this much better.
I'm intrigued. I've been using virtualenv in numerous companies for about 8 years, traditionally wrapped in virtualenvwrappers, and now in uv.

UV doesn't change any of that for me - it just wraps virtualenv and pip downloads dependencies (much, much) more quickly - the conversion was immediate and required zero changes.

UV is a pip / virtualenv wrapper. And It's a phenomenal wrapper - absolutely changed everything about how I do development - but under the hood it's still just virtualenv + pip - nothing changed there.

Can you expand on the pain you've experienced?

Regarding "things that need to be deployed" - internally all our repos have standardized on direnv (and in some really advanced environments, nix + direnv, but direnv alone does the trick 90% of the time) - so you just "cd <somedir>", direnv executes your virtualenv and you are good to go. UV takes care of the pip work.

Has eliminated 100% use of virtualenvwrappers and direct-calls to pip. I'd love to hear a use case where that doesn't work for you - we haven't tripped across it recently.

> UV is a pip / virtualenv wrapper.

Not quite; it reimplements the pip functionality (in a much smarter way). I'm pretty sure it reimplements the venv functionality, too, although I'm not entirely sure why (there's not a lot of room for improvement).

("venv" is short for "virtual environment", but "virtualenv" is specifically a heavyweight Python package for creating them with much more flexibility than the standard library option. Although the main thing making it "heavyweight" is that it vendors wheels for pip and Setuptools — possibly multiple of each.)

> It has a lot of the same problems as just globally pip installing packages

No, it doesn't. It specifically avoids the problem of environment pollution by letting you just make another environment. And it avoids the problem of interfering with the system by not getting added to sys.path by default, and not being in a place that system packages care about. PEP 668 was specifically created in cooperation between the Python team and Linux distro maintainers so that people would use the venvs instead of "globally pip installing packages".

> requires a bit of path mangling to work right, or special python configs, etc. In the past, it's also had a bad habit of leaking dependencies, though that was in some weird setups.

Genuinely no idea what you're talking about and I've been specifically studying this stuff for years.

> It's one of the reasons I would recommend against python for much of anything that needs to be "deployed" vs throw away scripts. UV seems to handle all of this much better.

If you're using uv, you're using Python.

> by letting you just make another environment

This is actually what I'm talking about .. Why do I need a whole new python environment rather than just scoping the dependencies of an application to that application? That model makes it significantly harder to manage multiple applications/utilities on a machine, particularly if they have conflicting package versions etc. Being able to scope the dependencies to a specific code base without having to duplicate the rest of the python environment would be much better than a new virtualenv.

> Why do I need a whole new python environment rather than just scoping the dependencies of an application to that application?

If you haven't before, I strongly encourage you to try creating a virtual environment and inspecting what it actually contains.

"A whole new Python environment" is literally just a folder hierarchy and a pyvenv.cfg file, and some symlinks so that the original runtime executable has an alternate path. (On Windows it uses some stub wrapper executables because symlinks are problematic and .exe files are privileged; see e.g. https://paul-moores-notes.readthedocs.io/en/latest/wrappers.... .) And entirely unnecessary activation scripts for convenience.

If you wanted to be able to put multiple versions of dependencies into an environment, and have individual applications see what they need and avoid conflicts, you'd still need folders to organize stuff and config data to explain which dependencies are for which applications.

And you still wouldn't be able to solve the diamond dependency problem because of fundamental issues in the language design (i.e modules are cached, singleton objects).

When you make a virtual environment you don't do anything remotely like "duplicating the rest of the Python environment". You can optionally configure it to "include" the base environment's packages by having them added to sys.path.

> Why do I need a whole new python environment rather than just scoping the dependencies of an application to that application?

But… that’s what a virtualenv is. That’s the whole reason it exists. It lets you run 100 different programs, each with its own different and possibly conflicting dependencies. And yeah, those dependencies are completely isolated from each other.

I think his point is that you could have just had a situation where multiple versions of the same dependency could be installed globally rather than creating a new isolation each time.
I haven’t really had this issue. UV’s recommendation is to mount the uv.lock and install those manages package versions to the container’s global pip environment. We haven’t had much issue at my work, where we use this to auto-manage python developer’s execution environments at scale.
> UV’s recommendation is to mount the uv.lock and install those manages package versions to the container’s global pip environment.

Source? That's an option, but it's not even explicitly mentioned in the related documentation [1].

[1] https://docs.astral.sh/uv/guides/integration/docker/

https://docs.astral.sh/uv/guides/integration/docker/#interme...

You kind of have to read between the lines and "know" this is a good solution and then when you see it it's like "right of course".

Nice tricks! I wasn't aware of the cache mounts, so I was building with UV_NO_CACHE=1. Cache mounts should also come handy when installing OS packages in multi-stage builds.

I still couldn't find the "global pip environment" part, unless by that you meant the "active pip environment", pointed by VIRTUAL_ENV during image building.

Yeah I see now I was mixing techniques in two sections of that document
Mounting uv.lock doesn't actually work if you have intra-repository dependencies. UV can't deal with packages that lack metadata (because it's not mounted): https://github.com/astral-sh/uv/issues/15715
The only reason I haven't switched is its still barely-there support for air-gapped systems [1].

And lack of non-local venv support [2].

[1] https://github.com/astral-sh/uv/issues/10203

[2] https://github.com/astral-sh/uv/issues/1495

Same here. I still use conda from time to time for this.
> (unless you like using an env in your container that is).

What's the problem with that?

You just make your script's entry point be something like this:

    uv venv --clear
    uv sync
    uv run main.py
Doesn’t this mean pid 0 in the container is uv instead of python? Does uv run just spawn a child python process?
> Does uv run just spawn a child python process?

Yes, I suppose you could use it in conjunction with something like https://github.com/krallin/tini.

    ENV UV_SYSTEM_PYTHON=1