SSD Nodes Learn 🎉 VPS ab $5.50/Monat
Anleitungen Matt ConnorVon Matt Connor · Aktualisiert 2026-08-13

Python auf Servern: venv, pipx oder uv wählen

Ubuntu 24.04 meldet bei pip install „externally-managed-environment“. Erfahren Sie, wann venv, pipx oder uv passt und wie systemd die Umgebung nutzt.

Warum pip install auf einem neuen Ubuntu-Server fehlschlägt

Die Entscheidung zwischen einer Python-venv, pipx und uv hängt auf einem Server von einer Frage ab: Was installieren Sie? Anwendungsabhängigkeiten gehören in eine virtuelle Umgebung im eigenen Verzeichnis der Anwendung. Befehlszeilenwerkzeuge, die Sie anhand ihres Namens aufrufen möchten, gehören in pipx. uv erledigt beide Aufgaben und ergänzt eine Lockdatei. Das wird wichtig, sobald eine zweite Maschine dieselbe Umgebung erstellen muss. Keines dieser Werkzeuge installiert jedoch in das System-Python, weil ein aktueller Ubuntu-Server dies ausdrücklich verweigert.

Führen Sie sudo pip install requests unter Ubuntu 24.04 aus, bricht pip ab, bevor es auch nur eine Datei herunterlädt.

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.

Hier arbeitet PEP 668 (Python Enhancement Proposal 668, „externally managed environments“) wie vorgesehen. Debian und Ubuntu legen neben dem Interpreter unter /usr/lib/python3.12/EXTERNALLY-MANAGED eine Markierungsdatei ab. pip verweigert dann Schreibzugriffe auf jeden Interpreter, der diese Datei enthält.

Diese Regel existiert wegen der Reihenfolge von sys.path. apt installiert Bibliotheken nach /usr/lib/python3/dist-packages. pip schreibt als root und gegen den Systeminterpreter nach /usr/local/lib/python3.12/dist-packages, und die Paketierung von Debian setzt dieses Verzeichnis in der Suchpfadreihenfolge nach vorne. Geben Sie python3 -c 'import sys; print(sys.path)' selbst aus und prüfen Sie die Reihenfolge. Dadurch überschreibt die von pip geschriebene Kopie die von apt installierte Kopie. Das gilt für jedes Programm auf dem System, das unter /usr/bin/python3 ausgeführt wird, einschließlich der eigenen Werkzeuge der Distribution. cloud-init importiert requests, jinja2 und PyYAML aus diesem Interpreter. Aktualisieren Sie eines davon mit pip und erhalten Sie eine inkompatible Version, schlägt beim nächsten Boot möglicherweise etwas fehl, das Sie nie angefasst haben. Der Traceback nennt dann ein Paket, von dessen Verwendung Sie nichts wussten. apt führt seine eigene Version weiterhin als installiert. Daher erhalten Sie keine Warnung. Die Reparatur lautet sudo apt reinstall python3-requests.

Die daraus folgende Regel ist kurz. Das System-Python gehört zur Distribution. Installieren Sie nichts darin, aktualisieren Sie seine Bibliotheken nicht mit pip, und löschen Sie nicht die Datei EXTERNALLY-MANAGED, nur damit die Meldung verschwindet. Die einzige Aufgabe für /usr/bin/python3 ist das Erstellen virtueller Umgebungen.

venv vs pipx vs uv: die Entscheidungsregel

Wählen Sie anhand dessen, was Sie installieren, nicht anhand des Tools, über das Sie zuletzt gelesen haben.

  • Eine Anwendung, die Sie bereitstellen und als Dienst ausführen, beispielsweise ein Django- oder Flask-Projekt: eine virtuelle Umgebung (venv) im Verzeichnis dieser Anwendung.
  • Ein Kommandozeilentool, das Sie auf Ihrem PATH benötigen, beispielsweise ansible oder httpie: pipx. pipx gibt jedem Tool eine eigene private Umgebung und einen Link in PATH.
  • Ein Projekt, das eine Lockdatei, schnellere Installationen oder eine Python-Version benötigt, die von der Distribution nicht bereitgestellt wird: uv. uv erstellt zusätzlich zu einer normalen venv eine Datei uv.lock.
  • Eine Bibliothek, die ein Distributionstool und nicht Ihr Code benötigt: sudo apt install python3-<name>. Dies ist die einzige unterstützte Möglichkeit, etwas zum Systeminterpreter hinzuzufügen.

pipx und uv tool install erfüllen dieselbe Aufgabe. Auf einem System, auf dem uv bereits installiert ist, benötigen Sie daher nicht zusätzlich pipx. Welches Webframework Sie gewählt haben, ändert daran nichts: Django und Flask auf einem VPS unterscheiden sich darin, was in requirements.txt landet, nicht darin, wie die umgebende Umgebung erstellt wird. Alle folgenden Beispiele verwenden Ubuntu 24.04 und dessen Python 3.12. Passen Sie die Versionsnummer in den Pfaden an, falls Ihre Version abweicht.

Virtuelle Umgebung pro Anwendung erstellen

Ubuntu lagert das Modul venv aus dem Basispaket von Python aus. Auf einem minimalen Image schlägt der erste Versuch daher mit einer Meldung fehl, die genau angibt, was fehlt.

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

Installieren Sie das Modul. Erstellen Sie anschließend die Umgebung als der Benutzer, dem der Code gehören soll.

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

Beachten Sie, was nicht vorhanden ist: weder source noch activate. /srv/myapp/.venv/bin/pip wird aufgrund des Speicherorts der Binärdatei in dieser Umgebung installiert, nicht aufgrund von Variablen, die Sie in der Shell exportiert haben. Überprüfen Sie dies, bevor Sie fortfahren.

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

Die Ausgabe lautet /srv/myapp/.venv. Wenn /usr ausgegeben wird, verwenden Sie den Systeminterpreter, und Ihre Pakete wurden an einem nicht beabsichtigten Ort installiert.

Zwei Eigenschaften einer venv bestimmen, was Sie anschließend damit tun können. Eine venv ist nicht verschiebbar, weil jedes Skript in bin/ eine absolute Shebang-Zeile enthält: head -1 /srv/myapp/.venv/bin/pip liest #!/srv/myapp/.venv/bin/python. Wenn Sie das übergeordnete Verzeichnis umbenennen, schlagen diese Skripte mit bad interpreter: No such file or directory fehl. Eine venv bindet außerdem den Interpreter, mit dem sie erstellt wurde. Dieser ist als die Zeile home in /srv/myapp/.venv/pyvenv.cfg hinterlegt, und bin/python3 ist ein symbolischer Link auf diese Binärdatei. Aktualisieren Sie das Release, sodass python3.12 nicht mehr vorhanden ist, zeigt der symbolische Link auf kein Ziel mehr, und der Dienst beendet sich beim Start mit No such file or directory. In beiden Fällen ist die Lösung identisch: Löschen Sie die venv und erstellen Sie sie aus requirements.txt neu. Das Neuerstellen dauert nur wenige Sekunden. Kopieren Sie eine venv niemals zwischen Maschinen.

Wo sich die venv befindet und wem sie gehört

Legen Sie sie neben dem Code unter /srv/myapp/.venv an, und verwenden Sie pro Anwendung eine eigene venv. Das Deployment besteht dann aus einem einzigen Verzeichnis, die systemd-Unit erhält einen unveränderlichen Pfad, und zwei Anwendungen können sich niemals durch ein gemeinsames Upgrade einer Abhängigkeit gegenseitig beeinträchtigen. Platzieren Sie eine venv nicht in einem Verzeichnis, aus dem Ihr Webserver Dateien direkt veröffentlicht. Sie enthält Ihre Abhängigkeiten und häufig auch Ihre Konfiguration.

Die Besitzverhältnisse sollten Sie kurz festlegen. Lassen Sie einen Benutzer deploy den Code und die Umgebung besitzen, und geben Sie dem Dienstkonto ausschließlich Lese- und Ausführungsrechte.

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

Der Dienst kann seine Abhängigkeiten jetzt importieren, sie aber nicht überschreiben. Dadurch kann ein Fehler mit Codeausführung in der Webanwendung nicht unbemerkt eine Bibliothek auf dem Datenträger ersetzen und einen Neustart überdauern. Dieselbe Vorgehensweise für den Rest des Systems wird unter Dienste mit Benutzern mit den geringsten Rechten ausführen beschrieben.

pipx für Kommandozeilenwerkzeuge

pipx installiert Anwendungen, keine Bibliotheken. Jedes Werkzeug erhält eine eigene Umgebung unter ~/.local/share/pipx/venvs/<name>. Die ausführbaren Dateien dieses Werkzeugs werden nach ~/.local/bin verlinkt. Daher kollidieren zwei Werkzeuge mit unterschiedlichen Versionen derselben Bibliothek nicht miteinander.

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

pipx ensurepath fügt ~/.local/bin durch eine Änderung Ihrer Shell-Startdatei zu PATH hinzu. Die bereits laufende Shell kann der Befehl nicht ändern. Wenn http: command not found direkt nach der Installation ausgeführt wird, haben Sie sich daher normalerweise noch nicht ab- und wieder angemeldet. Das standardmäßige ~/.profile von Ubuntu fügt ~/.local/bin nur hinzu, wenn dieses Verzeichnis beim Anmelden bereits vorhanden ist. Deshalb tritt das Problem bei einem neuen Konto einmalig auf und danach nicht mehr.

Wenn Sie pipx auf eine Bibliothek anwenden, verweigert es die Ausführung mit einer Meldung, die so beginnt:

No apps associated with package requests or its dependencies.

Damit weist das Werkzeug darauf hin, dass es für diesen Zweck nicht geeignet ist. Bibliotheken gehören in die venv einer Anwendung.

Auf einem Server ist der Installationsort entscheidend. Ein einfaches pipx install legt alles im Home-Verzeichnis eines Benutzers ab. Eine als myapp ausgeführte systemd-Unit kann darauf nicht zugreifen. Ein Cronjob von root kann ebenfalls nicht darauf zugreifen. Auch sudo findet es nicht, weil secure_path in /etc/sudoers PATH durch eine feste Liste ersetzt. Wenn ein Werkzeug dem gesamten System zur Verfügung stehen soll, installieren Sie es global.

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

Das Flag --global legt Umgebungen unter /opt/pipx an und verlinkt ausführbare Dateien nach /usr/local/bin. Dieses Verzeichnis befindet sich im standardmäßigen PATH und innerhalb von secure_path. Prüfen Sie zuerst Ihre Version mit pipx --version. Ubuntu 24.04 stellt pipx 1.4.3 bereit. Diese Version ist älter als --global. Eine ältere pipx-Version antwortet mit unrecognized arguments: --global. Legen Sie bei dieser Version die beiden dokumentierten Verzeichnisse selbst fest:

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

command -v ansible sollte /usr/local/bin/ansible ausgeben. Wenn ein Pfad unter /home ausgegeben wird, wurde das Werkzeug im Konto eines einzelnen Benutzers installiert. Kein Dienst wird es finden.

uv für Situationen, in denen Sie eine Lockdatei benötigen

uv ist eine einzelne Binärdatei von Astral. Sie deckt die Funktionen von pip, venv und pip-tools ab und kann außerdem Interpreter herunterladen. Das Tool ist schnell genug, dass sich der Unterschied auf einem kleinen VPS bemerkbar macht. Außerdem schreibt es eine echte Lockdatei.

Das offizielle Installationsprogramm legt uv und uvx in ~/.local/bin ab:

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

Ein Skript auf einem Server per Pipe an eine Shell zu übergeben, erfordert etwas Vorsicht. Fixieren Sie die Version in der URL und lesen Sie die Datei, bevor Sie sie ausführen:

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 funktioniert ebenfalls, wenn pipx bereits installiert ist. uv ist eine eigenständige Binärdatei ohne eigene Python-Abhängigkeit. Daher können Sie sie nach /usr/local/bin kopieren und so allen Benutzern auf dem Server bereitstellen.

Für ein Projekt mit einem pyproject.toml besteht der Ablauf aus vier Befehlen. Nur der letzte Befehl wird auf dem Server ausgeführt.

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

uv lock schreibt uv.lock. Dabei handelt es sich um eine plattformübergreifende Lockdatei mit den exakt aufgelösten Versionen. Sie committen diese Datei zusammen mit Ihrem Code. uv sync erstellt im Projektverzeichnis .venv, um die darin festgelegten Versionen zu verwenden. Auf dem Server ist --frozen das entscheidende Flag: Die Dokumentation definiert es so, dass die Versionen in der Lockdatei als maßgebliche Quelle verwendet werden, anstatt zu prüfen, ob die Lockdatei aktuell ist. Genau dieses Verhalten benötigen Sie bei einer Bereitstellung. --no-dev lässt die Entwicklungsabhängigkeitsgruppe weg.

Ein vorhandenes requirements.txt-Projekt muss nicht konvertiert werden, weil uv die Sprache von pip versteht:

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

Das Ergebnis ist eine gewöhnliche virtuelle Umgebung. .venv/bin/python verhält sich genau so, wie es sich verhalten würde, wenn python3 -m venv sie erstellt hätte. Daher ändert sich im weiteren Verlauf dieses Leitfadens nichts.

Ein Standardwert von uv ist wichtig, bevor Sie das Tool auf einem Server einsetzen. Die Einstellung python-preference ist standardmäßig auf managed gesetzt. Laut Dokumentation werden damit Interpreter ausgewählt, die von uv heruntergeladen und installiert wurden, und nicht bereits auf dem System vorhandene Interpreter. Wenn Sie also uv venv --python 3.13 auf einem System ausführen, auf dem nur 3.12 installiert ist, lädt uv unbemerkt 3.13 nach ~/.local/share/uv/python herunter, anstatt einen Fehler auszugeben. Auf einem Laptop ist das praktisch. Auf einem Server ist es jedoch überraschend, weil Ihr Dienst dann von einem Interpreter in einem Home-Verzeichnis abhängt, den apt upgrade niemals patchen wird. Setzen Sie python-preference in uv.toml auf only-system, wenn Sie den Interpreter der Distribution verwenden möchten. Wenn die Umgebung nicht im Projektverzeichnis liegen soll, gibt UV_PROJECT_ENVIRONMENT das Verzeichnis für die virtuelle Projektumgebung an.

Auf den venv-Interpreter verweisen, nicht auf activate

Hier scheitern die meisten Python-Bereitstellungen. Die Ursache ist ein Missverständnis darüber, was activate tut.

bin/activate ist ein Shell-Skript. Es stellt das bin-Verzeichnis der venv an den Anfang von PATH, setzt VIRTUAL_ENV, speichert die alten Werte, damit deactivate sie wiederherstellen kann, und ändert den Prompt. Es enthält nichts, was der Interpreter selbst liest. Die Aktivierung ist nur eine Erleichterung für Personen, die python an einer Eingabeaufforderung eingeben.

Welche Umgebung tatsächlich ausgewählt wird, hängt davon ab, welche Interpreterdatei Sie ausführen. Beim Start von /srv/myapp/.venv/bin/python sucht Pythons Modul site in dem Verzeichnis, das die ausführbare Datei enthält, und eine Ebene darüber nach einer Datei namens pyvenv.cfg. Wird /srv/myapp/.venv/pyvenv.cfg gefunden, setzt Python sys.prefix auf die venv. Dadurch gelangt das site-packages-Verzeichnis dieser venv in sys.path. Das ist der gesamte Mechanismus. Dafür sind weder eine Umgebungsvariable noch eine Shell erforderlich.

Diese Unit wird daher nie gestartet:

[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 ist keine Shell-Befehlszeile. systemd führt ein Programm direkt aus. Daher gibt es kein source-Builtin, && wird als literales Argument übergeben, und nichts wird expandiert.

Diese Unit startet dagegen und beendet sich anschließend:

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

/usr/bin/python3 ist der Systeminterpreter. Sein sys.path hat Ihre venv nie enthalten. Derselbe Befehl funktioniert in Ihrer SSH-Sitzung nur, weil Sie dort die venv aktiviert hatten. Dadurch hat die Shell python3 über PATH stattdessen zu .venv/bin/python3 aufgelöst.

Den Befehl in /bin/bash -c 'source ... && gunicorn ...' einzuschließen funktioniert. Dadurch wird jedoch ohne Nutzen eine Shell zwischen systemd und Ihren Prozess geschaltet. Ein absoluter Pfad reicht aus:

[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 sollte active (running) mit einem Main PID ausgeben, das Ihr gunicorn-Prozess ist. Bei jeder anderen Ausgabe lesen Sie das Journal.

Die Zeile Environment=PATH= ist nicht für ExecStart erforderlich. Dieser Wert enthält bereits einen vollständigen Pfad. Sie ist für die Prozesse erforderlich, die Ihre Anwendung startet. Ein Dienst erbt von systemd ein kurzes Standard-PATH. Python-Code, der subprocess.run(["ffmpeg", ...]) aufruft, oder ein Management-Befehl, der ein Console-Skript aus der venv als externen Prozess startet, findet die benötigte Datei daher nicht. Das bin-Verzeichnis der venv an den Anfang zu setzen, ist der Teil von activate, den ein Dienst tatsächlich verwendet. Prüfen Sie mit systemctl show -p Environment myapp, welche Umgebung die Unit tatsächlich erhalten hat.

Dieselbe Regel gilt für geplante Aufgaben. cron führt Jobs mit einem PATH von /usr/bin:/bin aus. Eine Crontab-Zeile mit python3 /srv/myapp/cleanup.py verwendet daher den Systeminterpreter und schlägt um drei Uhr morgens mit ModuleNotFoundError fehl. Die Fehlermeldung wird an einen lokalen Mail-Spool gesendet, den niemand liest. Verwenden Sie auch dort den absoluten Pfad zur venv. Wenn die Ausgabe stattdessen im Journal erscheinen und ein Protokoll der letzten Ausführung geführt werden soll, verwendet ein systemd-Service mit zugehörigem Timer dieselbe ExecStart-Zeile.

Ersetzt Docker diese Entscheidung?

Ein Container verfügt über ein eigenes Dateisystem. Die Frage ändert daher ihre Form, verschwindet aber nicht. In einem offiziellen Image wie python:3.12-slim ist Python in /usr/local integriert und enthält keine EXTERNALLY-MANAGED-Markierung. Daher ist pip install als root der vorgesehene Weg, Pakete hinzuzufügen. Eine virtuelle Umgebung bringt hier wenig. Bauen Sie stattdessen FROM ubuntu:24.04, stoßen Sie innerhalb des Images aus demselben Grund wie auf dem Host erneut auf externally-managed-environment: Es handelt sich um den Interpreter der Distribution, der die Markierungsdatei der Distribution enthält.

Viele Images verwenden trotzdem eine virtuelle Umgebung, weil sie einen mehrstufigen Build vereinfacht. Die Builder-Stage installiert nach /opt/venv. Die Runtime-Stage kopiert anschließend nur dieses Verzeichnis und lässt die Compiler zurück. Das Problem mit der Aktivierung wird dabei übernommen. Eine Zeile mit RUN source /opt/venv/bin/activate wirkt nur auf die Shell dieser Build-Schicht. Zur Laufzeit startet der Container daher mit dem System-Interpreter und meldet ModuleNotFoundError. Setzen Sie ENV PATH="/opt/venv/bin:$PATH" oder übergeben Sie CMD den absoluten Pfad /opt/venv/bin/gunicorn. Es handelt sich um denselben Fehler wie bei systemd, nur in einer anderen Datei.

Ein Container ersetzt also die Frage nach dem Interpreter, weil das Image den Interpreter und alles darunter festlegt. Die Frage nach der Versionierung ersetzt er nicht. Ein Image, das aus einem nicht versionierten requirements.txt erstellt wird, löst im nächsten Monat möglicherweise andere Versionen auf. Damit ist zwar der Image-Tag reproduzierbar, nicht aber der Build, der ihn erzeugt hat. Eine Lockdatei wie uv.lock oder eine vollständig versionierte Requirements-Datei schließt diese Lücke, unabhängig davon, ob Container verwendet werden. Wenn eine Anwendung auf einem einzelnen VPS unter systemd läuft, verlagert ein Container diese Entscheidung meist nur in ein Dockerfile, da systemd einen fehlgeschlagenen Prozess bereits neu startet und seine Ausgabe im Journal erfasst. Docker auf einem VPS betreiben lohnt sich, wenn das erstellte Image selbst das Deployment-Artefakt sein soll.

FAQ

Kann ich einfach pip install mit --break-system-packages verwenden?

Nicht auf einem Server, der dauerhaft laufen muss. Das Flag tut genau das, was sein Name sagt: Es entfernt die Schutzvorrichtung, und pip schreibt nach /usr/local/lib/python3.12/dist-packages, das vor dem apt-Verzeichnis in sys.path steht. Ihre Version überschattet dann die Distributionsversion für jedes unter /usr/bin/python3 ausgeführte Systemskript. apt geht weiterhin davon aus, dass seine eigene Version installiert ist. Daher erkennt nichts den Konflikt, bis etwas ausfällt. In einem Container-Image, das Sie jedes Mal von Grund auf neu erstellen, bleibt der Schaden auf dieses Image begrenzt. Dort ist die Verwendung vertretbar. Auf einer von Ihnen verwalteten Maschine erstellen Sie stattdessen eine venv. Das ist ein einziger Befehl.

Wo sollte die virtuelle Umgebung auf einem Server liegen?

Im Verzeichnis der jeweiligen Anwendung, als /srv/myapp/.venv, im Besitz eines deploy-Benutzers. Das Dienstkonto darf dort nur Lese- und Ausführungszugriff haben. Verwenden Sie eine venv pro Anwendung. Bei einer gemeinsamen venv kann ein Upgrade für die erste Anwendung die zweite Anwendung beschädigen. Verschieben oder kopieren Sie eine venv nach ihrer Erstellung nicht. Jedes Skript in ihrem Verzeichnis bin/ enthält diesen absoluten Pfad in der Shebang-Zeile. Daher schlägt eine verschobene venv mit bad interpreter: No such file or directory fehl. Löschen Sie sie stattdessen und erstellen Sie sie aus requirements.txt neu.

Warum schlägt mein systemd-Dienst mit ModuleNotFoundError fehl?

Weil die Unit einen Interpreter ausführt, der nicht zu der venv gehört. Führen Sie systemctl cat myapp aus und lesen Sie ExecStart. Dort muss /srv/myapp/.venv/bin/python oder ein Console-Skript aus demselben Verzeichnis bin/ mit absolutem Pfad angegeben sein. Das Sourcen von activate in einer Unit-Datei funktioniert nicht, weil ExecStart keine Shell ist. systemd meldet dann Failed to locate executable source mit status=203/EXEC. Fügen Sie Environment=PATH=/srv/myapp/.venv/bin:/usr/local/bin:/usr/bin:/bin hinzu, damit auch von Ihrem Code gestartete Subprozesse die Werkzeuge der venv finden.

Sollte ich uv anstelle von venv und pip verwenden?

Verwenden Sie uv, wenn Sie eine Lockdatei benötigen, die Installationszeit störend lang ist oder Sie eine Python-Version benötigen, die Ihre Distribution nicht bereitstellt. uv erstellt eine gewöhnliche venv. Daher ändern sich weder die systemd-Unit noch das Dateilayout. uv sync --frozen installiert genau das, was die Lockdatei festlegt. Wenn eine einzelne Anwendung aus git mit einem festgelegten requirements.txt bereitgestellt wird und die Installation in wenigen Sekunden abgeschlossen ist, reicht python3 -m venv bereits aus. Außerdem müssen Sie dann ein Binary weniger auf dem Server aktualisieren.

#python#venv#pipx#uv#deployment