SSD Nodes Learn 🎉 VPS od $5.50/mies.
Przewodniki Matt ConnorAutor: Matt Connor · Zaktualizowano 2026-08-13

Python venv, pipx czy uv na serwerze? Wybór narzędzia

Błąd externally-managed-environment na Ubuntu blokuje instalację pakietów przez pip. Wybierz venv dla aplikacji, pipx dla narzędzi CLI lub uv dla obu i skonfiguruj systemd.

Dlaczego pip install kończy się niepowodzeniem na świeżym serwerze Ubuntu

Wybór między Python venv, pipx a uv na serwerze sprowadza się do jednego pytania: co jest instalowane? Zależności aplikacji powinny znajdować się w wirtualnym środowisku wewnątrz katalogu danej aplikacji. Narzędzia wiersza poleceń, które mają być wywoływane bezpośrednio po nazwie, powinny być instalowane przez pipx. Narzędzie uv obsługuje oba te przypadki i dodaje plik blokady (lockfile), co staje się istotne, gdy drugie urządzenie musi zbudować identyczne środowisko. Żadne z tych narzędzi nie instaluje pakietów w systemowym interpreterze Python, ponieważ współczesne serwery Ubuntu kategorycznie tego zabraniają.

Uruchomienie sudo pip install requests na Ubuntu 24.04 powoduje, że pip przerywa działanie przed pobraniem jakiegokolwiek pliku.

error: externally-managed-environment

× This environment is externally managed
╰─> To install Python packages system-wide, try apt install
    python3-xyz, where xyz is the package you are trying to
    install.

    If you wish to install a non-Debian-packaged Python package,
    create a virtual environment using python3 -m venv path/to/venv.
    Then use path/to/venv/bin/python and path/to/venv/bin/pip.

    If you wish to install a non-Debian packaged Python application,
    it may be easiest to use pipx install xyz, which will manage a
    virtual environment for you.

note: If you believe this is a mistake, please contact your Python installation or OS distribution provider. You can override this behaviour by passing --break-system-packages.

Jest to wynik działania PEP 668 (Python enhancement proposal 668, "środowiska zarządzane zewnętrznie"). Systemy Debian i Ubuntu umieszczają plik znacznikowy obok interpretera w /usr/lib/python3.12/EXTERNALLY-MANAGED, a pip odmawia zapisu w każdym interpreterze, który posiada taki plik.

Zasada ta istnieje ze względu na kolejność sys.path. apt instaluje biblioteki w /usr/lib/python3/dist-packages. pip, uruchomiony z uprawnieniami root dla interpretera systemowego, zapisuje pliki w /usr/local/lib/python3.12/dist-packages, a pakiety Debiana umieszczają ten katalog wcześniej w ścieżce wyszukiwania. Można to sprawdzić samodzielnie za pomocą python3 -c 'import sys; print(sys.path)' i zweryfikować kolejność. W rezultacie kopia zapisana przez pip przesłania kopię zainstalowaną przez apt dla każdego programu w systemie uruchamianego przez /usr/bin/python3, w tym dla własnych narzędzi dystrybucji. cloud-init importuje requests, jinja2 oraz PyYAML z tego interpretera. Aktualizacja jednego z nich za pomocą pip do niekompatybilnej wersji powoduje, że komponent, którego nie modyfikowano, przestaje działać przy następnym uruchomieniu, zgłaszając błąd związany z pakietem, o którego istnieniu użytkownik nie wiedział. apt nadal rejestruje swoją wersję jako zainstalowaną, więc system nie generuje ostrzeżeń, a naprawa jest sudo apt reinstall python3-requests.

Wynikająca z tego zasada jest krótka. Systemowy Python należy do dystrybucji. Nie należy instalować w nim pakietów, nie należy aktualizować jego bibliotek za pomocą pip i nie należy usuwać pliku EXTERNALLY-MANAGED, aby pozbyć się komunikatu o błędzie. Jedynym zadaniem dla /usr/bin/python3 jest tworzenie wirtualnych środowisk.

venv a pipx a uv: zasada wyboru

Wybór narzędzia powinien zależeć od rodzaju instalowanego oprogramowania, a nie od tego, o którym z nich czytano ostatnio.

  • Aplikacja wdrażana i uruchamiana jako usługa, na przykład projekt Django lub Flask: jedno środowisko wirtualne (venv) wewnątrz katalogu danej aplikacji.
  • Narzędzie wiersza poleceń, które ma być dostępne w PATH, takie jak ansible lub httpie: pipx, który zapewnia każdemu narzędziu prywatne środowisko oraz jeden odnośnik w PATH.
  • Projekt wymagający pliku blokady (lockfile), szybszej instalacji lub wersji Python, której nie dostarcza dystrybucja: uv, który tworzy standardowe venv oraz plik uv.lock.
  • Biblioteka wymagana przez narzędzie systemowe, a nie przez własny kod: sudo apt install python3-<name>, jedyny wspierany sposób dodawania czegokolwiek do interpretera systemowego.

Narzędzia pipx oraz uv tool install pełnią tę samą funkcję, więc serwer, na którym zainstalowano już uv, nie wymaga dodatkowo pipx. Wybór frameworka webowego nie ma tu znaczenia: Django i Flask na VPS różnią się zawartością requirements.txt, a nie sposobem budowania otaczającego je środowiska. Wszystkie poniższe przykłady wykorzystują Ubuntu 24.04 oraz wbudowany Python 3.12, dlatego w przypadku innej wersji należy odpowiednio dostosować ścieżki.

Budowa środowiska venv dla poszczególnych aplikacji

System Ubuntu wydziela moduł venv z podstawowego pakietu Python, dlatego na minimalnym obrazie pierwsza próba zakończy się niepowodzeniem z komunikatem wskazującym dokładnie, czego brakuje.

The virtual environment was not created successfully because ensurepip is not
available.  On Debian/Ubuntu systems, you need to install the python3-venv
package using the following command.

    apt install python3.12-venv

Należy zainstalować ten moduł, a następnie utworzyć środowisko jako użytkownik, który będzie właścicielem kodu.

sudo apt update
sudo apt install -y python3-venv
sudo install -d -o deploy -g deploy -m 755 /srv/myapp
sudo -u deploy python3 -m venv /srv/myapp/.venv
sudo -u deploy /srv/myapp/.venv/bin/pip install -r /srv/myapp/requirements.txt

Zwróć uwagę na to, czego brakuje: brak source oraz activate. Polecenie /srv/myapp/.venv/bin/pip instaluje pakiety w tym środowisku ze względu na lokalizację pliku binarnego, a nie przez zmienne wyeksportowane do powłoki. Potwierdź to przed przejściem dalej.

/srv/myapp/.venv/bin/python -c 'import sys; print(sys.prefix)'

Polecenie to wyświetla /srv/myapp/.venv. Jeśli wyświetli /usr, oznacza to, że używany jest interpreter systemowy, a pakiety trafiły w niepożądane miejsce.

Dwie właściwości środowiska venv decydują o tym, co można z nim później zrobić. Środowisko venv nie jest przenośne, ponieważ każdy skrypt w bin/ zawiera bezwzględną ścieżkę w linii shebang: head -1 /srv/myapp/.venv/bin/pip odczytuje #!/srv/myapp/.venv/bin/python. Zmiana nazwy katalogu nadrzędnego spowoduje, że skrypty te zakończą się błędem bad interpreter: No such file or directory. Środowisko venv przypisuje również interpreter, który je utworzył, co jest zapisane w linii home w pliku /srv/myapp/.venv/pyvenv.cfg, a bin/python3 jest dowiązaniem symbolicznym do tego pliku binarnego. Aktualizacja wydania systemu, po której python3.12 przestaje istnieć, sprawia, że dowiązanie traci cel, a usługa przestaje działać przy starcie z błędem No such file or directory. W obu przypadkach rozwiązanie jest takie samo: należy usunąć venv i zbudować nowy na podstawie requirements.txt. Ponowna budowa trwa sekundy. Nigdy nie kopiuj środowiska venv między maszynami.

Lokalizacja venv i jego właściciel

Umieść venv obok kodu w /srv/myapp/.venv i utrzymuj jeden venv dla każdej aplikacji. Wdrożenie staje się wtedy pojedynczym katalogiem, jednostka systemd otrzymuje niezmienną ścieżkę, a dwie aplikacje nigdy nie uszkodzą się nawzajem poprzez aktualizację współdzielonej zależności. Nie umieszczaj venv w miejscu, z którego serwer WWW bezpośrednio publikuje pliki, ponieważ zawiera on zależności oraz często konfigurację.

Kwestia własności wymaga chwili uwagi. Niech użytkownik deploy będzie właścicielem kodu oraz środowiska, a konto usługi powinno posiadać jedynie uprawnienia do odczytu i wykonywania.

sudo adduser --system --group --no-create-home myapp
sudo chown -R deploy:myapp /srv/myapp
sudo chmod -R o-rwx /srv/myapp

Usługa może teraz importować swoje zależności, ale nie może ich nadpisywać. Oznacza to, że błąd wykonania kodu w aplikacji WWW nie pozwoli na cichą podmianę biblioteki na dysku, która przetrwałaby restart. To samo rozumowanie zastosowane do reszty systemu zostało opisane w uruchamianie usług z uprawnieniami minimalnymi.

pipx dla narzędzi wiersza poleceń

pipx służy do instalacji aplikacji, a nie bibliotek. Każde narzędzie otrzymuje własne środowisko w ~/.local/share/pipx/venvs/<name>, a pliki wykonywalne tego narzędzia są dowiązywane do ~/.local/bin. Dzięki temu dwa narzędzia wymagające różnych wersji tej samej biblioteki nigdy nie wchodzą w konflikt.

sudo apt update
sudo apt install -y pipx
pipx ensurepath
pipx install httpie

pipx ensurepath dodaje ~/.local/bin do PATH poprzez edycję pliku startowego powłoki. Nie może zmienić powłoki, w której użytkownik aktualnie pracuje, dlatego http: command not found zaraz po instalacji zazwyczaj oznacza, że sesja nie została jeszcze zakończona i rozpoczęta ponownie. Domyślny plik ~/.profile w systemie Ubuntu dodaje ~/.local/bin tylko wtedy, gdy katalog ten istnieje w momencie logowania. Dlatego problem ten występuje jednorazowo na nowym koncie i nie powtarza się w przyszłości.

Wskazanie biblioteki narzędziu pipx kończy się odmową z komunikatem zaczynającym się od:

No apps associated with package requests or its dependencies.

Jest to informacja, że wybrano niewłaściwe narzędzie. Biblioteki powinny znajdować się w wirtualnym środowisku (venv) aplikacji.

Kluczową kwestią na serwerze jest lokalizacja. Standardowe polecenie pipx install umieszcza wszystko w katalogu domowym użytkownika. Jednostka systemd działająca jako myapp nie ma do niego dostępu, zadanie cron uruchomione przez root również go nie widzi, a sudo go nie odnajdzie, ponieważ secure_path w /etc/sudoers zastępuje PATH stałą listą ścieżek. W przypadku narzędzia, z którego ma korzystać cały system, należy przeprowadzić instalację globalną.

sudo pipx install --global ansible
sudo pipx ensurepath --global

Flaga --global umieszcza środowiska w /opt/pipx, a pliki wykonywalne dowiązuje do /usr/local/bin, który znajduje się w domyślnej zmiennej PATH oraz wewnątrz secure_path. Najpierw należy sprawdzić wersję za pomocą pipx --version, ponieważ Ubuntu 24.04 dostarcza pipx w wersji 1.4.3, która jest starsza niż --global, a starsze wersje pipx odpowiadają za pomocą unrecognized arguments: --global. W tej wersji należy samodzielnie ustawić dwa udokumentowane katalogi:

sudo env PIPX_HOME=/opt/pipx PIPX_BIN_DIR=/usr/local/bin pipx install ansible
command -v ansible

command -v ansible powinno wyświetlić /usr/local/bin/ansible. Jeśli wyświetlona ścieżka znajduje się w /home, narzędzie zostało zainstalowane na koncie pojedynczego użytkownika i żadna usługa systemowa go nie odnajdzie.

uv, gdy potrzebny jest plik blokady

uv to pojedynczy plik binarny od Astral, który zastępuje funkcjonalność pip, venv oraz pip-tools, a dodatkowo potrafi pobierać interpretery. Działa na tyle szybko, że różnica jest zauważalna nawet na małym serwerze VPS, a przy tym tworzy rzeczywisty plik blokady.

Oficjalny instalator umieszcza uv oraz uvx w ~/.local/bin:

curl -LsSf https://astral.sh/uv/install.sh | sh
uv --version

Przesyłanie skryptu bezpośrednio do powłoki na serwerze wymaga chwili uwagi. Należy przypiąć wersję w adresie URL i przeczytać zawartość pliku przed jego uruchomieniem:

curl -LsSf https://astral.sh/uv/0.12.3/install.sh -o uv-install.sh
less uv-install.sh
sh uv-install.sh

pipx install uv również działa, jeśli pipx jest już zainstalowany. uv to samodzielny plik binarny bez własnych zależności od Pythona, więc skopiowanie go do /usr/local/bin jest poprawnym sposobem na udostępnienie go wszystkim użytkownikom w systemie.

W przypadku projektu z pyproject.toml, proces roboczy składa się z czterech poleceń, z których tylko ostatnie jest uruchamiane na serwerze.

uv init myapp
uv add flask gunicorn
uv lock
uv sync --frozen --no-dev

uv lock tworzy uv.lock, wieloplatformowy plik blokady zawierający dokładnie rozwiązane wersje pakietów, który należy zatwierdzić w repozytorium wraz z kodem. uv sync tworzy .venv w katalogu głównym projektu, aby dopasować środowisko do pliku blokady. Na serwerze kluczowa jest flaga --frozen: dokumentacja definiuje ją jako użycie wersji z pliku blokady jako jedynego źródła prawdy, zamiast sprawdzania, czy plik blokady jest aktualny, co jest zachowaniem pożądanym podczas wdrażania. --no-dev pomija grupę zależności programistycznych.

Istniejący projekt requirements.txt nie wymaga konwersji, ponieważ uv obsługuje formaty używane przez pip:

uv venv /srv/myapp/.venv
uv pip install --python /srv/myapp/.venv/bin/python -r /srv/myapp/requirements.txt

Wynikiem jest standardowe środowisko wirtualne. .venv/bin/python zachowuje się dokładnie tak samo, jak gdyby zostało utworzone przez python3 -m venv, więc dalsza część tego przewodnika pozostaje aktualna.

Przed użyciem uv na serwerze warto poznać jedno z jego domyślnych ustawień. Parametr python-preference ma domyślną wartość managed, co oznacza wybieranie interpreterów "pobranych i zainstalowanych przez uv" zamiast tych już obecnych w systemie. W rezultacie uv venv --python 3.13 na serwerze, który posiada tylko wersję 3.12, po cichu pobierze wersję 3.13 do ~/.local/share/uv/python, zamiast zgłosić błąd. Jest to wygodne na laptopie, ale zaskakujące na serwerze, ponieważ usługa zaczyna zależeć od interpretera znajdującego się w katalogu domowym, którego apt upgrade nigdy nie zaktualizuje. Należy ustawić python-preference na only-system w uv.toml, jeśli wymagane jest użycie interpretera systemowego. Jeśli środowisko ma znajdować się w innym miejscu niż katalog główny projektu, UV_PROJECT_ENVIRONMENT pozwala wskazać katalog dla środowiska wirtualnego projektu.

Wskazanie interpretera venv w systemd zamiast skryptu activate

W tym miejscu dochodzi do większości awarii wdrożeń Python, co wynika z błędnego zrozumienia działania activate.

bin/activate to skrypt powłoki. Dodaje on katalog bin środowiska venv na początek zmiennej PATH, ustawia VIRTUAL_ENV, zapisuje stare wartości, aby deactivate mogło je przywrócić, oraz zmienia znak zachęty. Skrypt nie zawiera niczego, co byłoby odczytywane przez sam interpreter. Aktywacja jest udogodnieniem dla użytkownika wpisującego python w wierszu poleceń.

O wyborze środowiska decyduje wyłącznie plik interpretera, który uruchamiasz. Gdy startuje /srv/myapp/.venv/bin/python, moduł site języka Python szuka pliku pyvenv.cfg w katalogu zawierającym plik wykonywalny oraz w katalogu nadrzędnym. Znalezienie /srv/myapp/.venv/pyvenv.cfg ustawia sys.prefix na venv, co umieszcza katalog site-packages tego środowiska w sys.path. To cały mechanizm. Nie wymaga on żadnych zmiennych środowiskowych ani powłoki.

Dlatego ta jednostka nigdy nie wystartuje:

[Service]
ExecStart=source /srv/myapp/.venv/bin/activate && gunicorn app:app
myapp.service: Failed to locate executable source: No such file or directory
myapp.service: Failed at step EXEC spawning source: No such file or directory
myapp.service: Main process exited, code=exited, status=203/EXEC

ExecStart nie jest linią poleceń powłoki. systemd uruchamia program bezpośrednio, więc nie ma wbudowanego polecenia source, && jest przekazywane jako dosłowny argument i nic nie ulega rozwinięciu.

A ta jednostka startuje, po czym kończy działanie:

[Service]
ExecStart=/usr/bin/python3 /srv/myapp/app.py
ModuleNotFoundError: No module named 'flask'

/usr/bin/python3 to systemowy interpreter, a jego sys.path nigdy nie zawierało twojego venv. To samo polecenie działa w sesji SSH tylko dlatego, że aktywowałeś tam venv, więc powłoka rozwiązała python3 poprzez PATH na .venv/bin/python3.

Zawinięcie polecenia w /bin/bash -c 'source ... && gunicorn ...' zadziała. Wstawia to jednak niepotrzebną powłokę między systemd a proces, podczas gdy wystarczy podanie pełnej ścieżki:

[Unit]
Description=myapp web service
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=myapp
Group=myapp
WorkingDirectory=/srv/myapp
Environment=PYTHONUNBUFFERED=1
Environment=PATH=/srv/myapp/.venv/bin:/usr/local/bin:/usr/bin:/bin
ExecStart=/srv/myapp/.venv/bin/gunicorn --workers 3 --bind 127.0.0.1:8000 app:app
Restart=on-failure
RestartSec=5
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=full

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now myapp
systemctl status myapp
journalctl -u myapp -n 50 --no-pager

systemctl status myapp powinno zgłosić active (running) z Main PID, którym jest twój proces gunicorn. Jeśli jest inaczej, sprawdź dziennik.

Linia Environment=PATH= nie służy ExecStart, który już posiada pełną ścieżkę. Służy ona procesom uruchamianym przez twoją aplikację. Usługa dziedziczy krótką, domyślną zmienną PATH od systemd, więc kod Python wywołujący subprocess.run(["ffmpeg", ...]) lub polecenie zarządzające, które uruchamia skrypt konsolowy z venv, nie znajdzie wymaganych plików. Umieszczenie katalogu bin środowiska venv na początku to jedyny element activate, z którego usługa faktycznie korzysta. Sprawdź, co jednostka otrzymała w rzeczywistości za pomocą systemctl show -p Environment myapp.

Ta sama zasada dotyczy zadań zaplanowanych. cron uruchamia zadania ze zmienną PATH ustawioną na /usr/bin:/bin, więc linia w crontab o treści python3 /srv/myapp/cleanup.py uruchamia systemowy interpreter i kończy się błędem ModuleNotFoundError o trzeciej nad ranem, a komunikat o błędzie trafia do lokalnej skrzynki pocztowej, której nikt nie czyta. Tam również należy wpisać pełną ścieżkę do venv. Aby uzyskać ten wynik w dzienniku i rejestr ostatniego uruchomienia, para jednostki systemd service i timer wykorzystuje tę samą linię ExecStart.

Czy Docker zmienia tę decyzję?

Kontener posiada własny system plików, więc problem zmienia postać, zamiast zniknąć. W oficjalnym obrazie, takim jak python:3.12-slim, Python jest wbudowany w /usr/local i nie posiada znacznika EXTERNALLY-MANAGED, dlatego pip install jako root jest zalecaną metodą dodawania pakietów, a venv wnosi w tym przypadku niewiele. Jeśli jednak budujesz FROM ubuntu:24.04, ponownie napotkasz externally-managed-environment wewnątrz obrazu z tego samego powodu, co na hoście: jest to interpreter dystrybucji zawierający jej plik znacznikowy.

Wiele obrazów nadal korzysta z venv, ponieważ upraszcza to budowanie wieloetapowe (multi-stage build). Etap budowania instaluje pakiety w /opt/venv, a etap uruchomieniowy kopiuje tylko ten katalog, pozostawiając kompilatory w poprzedniej warstwie. Problem z aktywacją przenosi się wraz z nim. Linia RUN source /opt/venv/bin/activate wpływa tylko na powłokę danej warstwy budowania, więc w czasie uruchomienia kontener startuje z interpretera systemowego i zgłasza ModuleNotFoundError. Należy ustawić ENV PATH="/opt/venv/bin:$PATH" lub podać CMD ścieżkę bezwzględną /opt/venv/bin/gunicorn. Jest to ten sam błąd co w przypadku systemd, tyle że w innym pliku.

Zatem kontener zmienia charakter pytania o interpreter, ponieważ obraz przypina (pinning) interpreter i wszystko, co się w nim znajduje. Nie eliminuje jednak kwestii przypinania wersji. Obraz zbudowany na podstawie nieprzypiętego requirements.txt w przyszłym miesiącu może zawierać inne wersje pakietów, co oznacza, że tag obrazu jest powtarzalny, ale proces budowania, który go stworzył, już nie. Plik blokady (lockfile), taki jak uv.lock, lub w pełni przypięty plik requirements, wypełnia tę lukę, niezależnie od użycia kontenerów. Gdy jedna aplikacja działa na jednym VPS pod kontrolą systemd, kontener w większości przypadków przenosi tę samą decyzję do pliku Dockerfile, ponieważ systemd już teraz restartuje nieudane procesy i przechwytuje ich wyjście w dzienniku. Uruchamianie Dockera na VPS jest uzasadnione, gdy celem jest wdrożenie gotowego obrazu jako jednostki dystrybucyjnej.

FAQ

Czy mogę użyć pip install z flagą --break-system-packages?

Nie na serwerze, który musi działać w sposób ciągły. Flaga ta robi dokładnie to, co sugeruje jej nazwa: usuwa zabezpieczenie, a pip zapisuje pliki w /usr/local/lib/python3.12/dist-packages, który w sys.path ma wyższy priorytet niż katalog apt. Własna wersja pakietu przesłania wersję systemową dla każdego skryptu uruchamianego przez /usr/bin/python3, a apt nadal uznaje, że zainstalowana jest jego własna wersja. W rezultacie konflikt nie zostanie wykryty, dopóki coś nie ulegnie awarii. Wewnątrz obrazu kontenera, który jest każdorazowo budowany od zera, szkoda ogranicza się tylko do tego obrazu, więc takie rozwiązanie jest tam dopuszczalne. Na maszynie, którą utrzymujesz, utwórz venv. To tylko jedno polecenie.

Gdzie na serwerze powinno znajdować się środowisko wirtualne?

Wewnątrz katalogu aplikacji, jako /srv/myapp/.venv, z właścicielem będącym użytkownikiem wdrożeniowym, przy czym konto usługi powinno mieć jedynie uprawnienia do odczytu i wykonywania. Utrzymuj jedno środowisko venv na aplikację, ponieważ współdzielenie środowiska oznacza, że aktualizacja jednej aplikacji może uszkodzić drugą. Nie przenoś ani nie kopiuj venv po jego utworzeniu: każdy skrypt w jego katalogu bin/ ma wpisaną ścieżkę bezwzględną w linii shebang, więc przeniesione środowisko venv kończy się błędem bad interpreter: No such file or directory. Zamiast tego usuń je i zbuduj ponownie z requirements.txt.

Dlaczego moja usługa systemd kończy się błędem ModuleNotFoundError?

Ponieważ jednostka uruchamia interpreter, który nie należy do venv. Uruchom systemctl cat myapp i przeczytaj ExecStart. Musi ona wskazywać na /srv/myapp/.venv/bin/python lub skrypt konsolowy z tego samego katalogu bin/ przy użyciu ścieżki bezwzględnej. Użycie source activate w pliku jednostki nie zadziała, ponieważ ExecStart nie jest powłoką, a systemd zgłasza Failed to locate executable source z status=203/EXEC. Dodaj Environment=PATH=/srv/myapp/.venv/bin:/usr/local/bin:/usr/bin:/bin, aby każdy podproces uruchamiany przez kod również znajdował narzędzia venv.

Czy powinienem używać uv zamiast venv i pip?

Używaj uv, gdy potrzebujesz pliku blokady (lockfile), gdy czas instalacji jest zbyt długi lub gdy potrzebujesz wersji Python, której nie dostarcza dystrybucja. Narzędzie to tworzy standardowe środowisko venv, więc jednostka systemd i układ plików pozostają bez zmian, a uv sync --frozen instaluje dokładnie to, co zapisano w pliku blokady. Jeśli pojedyncza aplikacja jest wdrażana z git z przypiętym requirements.txt, a instalacja kończy się w kilka sekund, python3 -m venv jest wystarczające i stanowi o jeden plik binarny mniej do aktualizowania na serwerze.

#python#venv#pipx#uv#deployment