SSD Nodes Learn 🎉 VPS mula $5.50/buwan
Mga Gabay Matt ConnorNi Matt Connor · Na-update 2026-08-13

Python venv, pipx o uv: Alin ang Gamitin sa Server?

Ayusin ang “externally-managed-environment” sa Ubuntu: alamin kung kailan gagamit ng venv, pipx o uv, at paano ituro ang tamang environment sa systemd.

Bakit nabibigo ang pip install sa bagong Ubuntu server

Ang pagpili sa pagitan ng Python venv, pipx, at uv sa isang server ay nakasalalay sa isang tanong: ano ang ini-install mo? Ang mga dependency ng application ay dapat nasa isang virtual environment sa loob ng sariling directory ng application. Ang mga command-line tool na gusto mong patakbuhin gamit ang pangalan nito ay dapat nasa pipx. Kayang gawin ng uv ang dalawang ito at nagdadagdag ito ng lockfile, na nagiging mahalaga kapag kailangan nang mag-build ng parehong environment sa ikalawang machine. Wala sa mga ito ang nag-i-install sa system Python, dahil tahasang tinatanggihan iyon ng kasalukuyang Ubuntu server.

Patakbuhin ang sudo pip install requests sa Ubuntu 24.04 at hihinto ang pip bago pa ito mag-download ng kahit isang file.

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.

Ito ang PEP 668 (Python enhancement proposal 668, “externally managed environments”) na gumagana ayon sa layunin nito. Naglalagay ang Debian at Ubuntu ng marker file sa tabi ng interpreter sa /usr/lib/python3.12/EXTERNALLY-MANAGED, at tumatanggi ang pip na magsulat sa anumang interpreter na may ganitong marker.

Umiiral ang rule dahil sa sys.path order. Nag-i-install ang apt ng mga library sa /usr/lib/python3/dist-packages. Kapag pinatakbo ang pip bilang root laban sa system interpreter, nagsusulat ito sa /usr/local/lib/python3.12/dist-packages, at inilalagay ng packaging ng Debian ang directory na iyon nang mas maaga sa search path. I-print ito mismo gamit ang python3 -c 'import sys; print(sys.path)' at basahin ang order. Kaya natatabunan ng kopyang isinulat ng pip ang kopyang ini-install ng apt para sa bawat program sa server na tumatakbo sa ilalim ng /usr/bin/python3, kabilang ang sariling tools ng distribution. Ini-import ng cloud-init ang requests, jinja2 at PyYAML mula sa interpreter na iyon. Kapag in-upgrade mo ang isa sa mga ito gamit ang pip at napunta sa isang incompatible na release, maaaring mabigo sa susunod na boot ang isang bagay na hindi mo naman ginalaw, na may traceback na nagbabanggit ng package na hindi mo alam na kasama sa dependency chain. Naitatala pa rin ng apt ang sarili nitong version bilang installed, kaya walang warning, at ang repair ay sudo apt reinstall python3-requests.

Maikli ang sumusunod na rule. Pagmamay-ari ng distribution ang system Python. Huwag mag-install dito, huwag i-upgrade ang mga library nito gamit ang pip, at huwag burahin ang EXTERNALLY-MANAGED file para mawala ang mensahe. Ang tanging trabaho na dapat ipagawa sa /usr/bin/python3 ay ang pagbuo ng mga virtual environment.

venv vs pipx vs uv: tuntunin sa pagpili

Pumili batay sa ini-install mo, hindi sa tool na pinakahuli mong nabasa.

  • Isang application na dine-deploy at pinapatakbo bilang service, gaya ng Django o Flask project: isang virtual environment (venv) sa loob ng directory ng application na iyon.
  • Isang command-line tool na gusto mong ilagay sa iyong PATH, gaya ng ansible o httpie: pipx, na nagbibigay sa bawat tool ng pribadong environment at isang link sa PATH.
  • Isang project na nangangailangan ng lockfile, mas mabilis na installation, o Python version na hindi kasama sa distribution: uv, na gumagawa ng karaniwang venv at ng uv.lock file.
  • Isang library na kailangan ng distribution tool at hindi ng iyong code: sudo apt install python3-<name>, ang tanging suportadong paraan upang magdagdag ng anuman sa system interpreter.

Pareho ang ginagawa ng pipx at uv tool install, kaya hindi na kailangan ng pipx sa isang machine na mayroon nang uv. Walang epekto rito ang pinili mong web framework: ang Django at Flask sa isang VPS ay nagkakaiba sa inilalagay sa requirements.txt, hindi sa paraan ng pagbuo ng environment sa paligid nito. Ginagamit sa lahat ng sumusunod na halimbawa ang Ubuntu 24.04 at ang Python 3.12 nito, kaya baguhin ang version sa loob ng mga path kung iba ang gamit mo.

Gawin ang venv para sa bawat application

Inihihiwalay ng Ubuntu ang module na venv mula sa base Python package. Kaya sa minimal image, mabibigo ang unang pagtatangka at eksaktong ililista ng mensahe kung ano ang nawawala.

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

I-install ito. Pagkatapos, gawin ang environment bilang user na magiging may-ari ng code.

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

Pansinin kung ano ang wala roon: walang source at walang activate. Nag-i-install ang /srv/myapp/.venv/bin/pip sa environment na iyon dahil sa lokasyon ng binary, hindi dahil may na-export ka sa shell. Kumpirmahin ito bago magpatuloy.

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

Ipi-print nito ang /srv/myapp/.venv. Kung /usr ang mai-print nito, ginagamit mo ang system interpreter at napunta ang mga package mo sa lokasyong hindi mo nilayon.

Dalawang property ng venv ang tumutukoy sa mga maaari mong gawin dito pagkatapos. Hindi relocatable ang venv dahil may absolute shebang line ang bawat script sa bin/: binabasa ng head -1 /srv/myapp/.venv/bin/pip ang #!/srv/myapp/.venv/bin/python. Kapag pinalitan mo ang pangalan ng parent directory, mabibigo ang mga script na iyon gamit ang bad interpreter: No such file or directory. Naka-pin din ang interpreter na gumawa sa venv. Nakatala ito bilang linyang home sa /srv/myapp/.venv/pyvenv.cfg, at symlink ang bin/python3 patungo sa binary na iyon. Kapag in-upgrade mo ang release at nawala ang python3.12, wala nang target ang symlink at mamamatay ang service sa pagsisimula gamit ang No such file or directory. Pareho ang ayos sa dalawang sitwasyon: i-delete ang venv at gumawa ng bago mula sa requirements.txt. Ilang segundo lang ang pag-rebuild. Huwag kailanman kumopya ng venv sa ibang machine.

Kung saan ilalagay ang venv, at kung sino ang may-ari nito

Ilagay ito sa tabi ng code sa /srv/myapp/.venv, at gumamit ng isang venv para sa bawat application. Dahil dito, nasa iisang directory ang deployment, path na hindi nagbabago ang ibinibigay sa systemd unit, at hindi kailanman mapapahinto ng dalawang application ang isa’t isa dahil sa isang shared dependency upgrade. Huwag maglagay ng venv sa anumang lokasyong direktang pinapadalhan ng files ng iyong web server, dahil naglalaman ito ng iyong dependencies at madalas pati ng iyong configuration.

Maglaan ng kalahating minuto para sa ownership. Hayaan ang isang deploy user na maging may-ari ng code at environment, at bigyan lamang ang service account ng read at execute access.

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

Maaari nang i-import ng service ang mga dependency nito, pero hindi nito maaaring baguhin ang mga ito. Ibig sabihin, hindi maaaring tahimik na palitan ng code execution bug sa web application ang library sa disk at manatili ito pagkatapos ng restart. Saklaw sa pagpapatakbo ng mga service bilang least-privilege user ang parehong prinsipyo kapag inilapat sa iba pang bahagi ng machine.

pipx para sa command-line tools

Ang pipx ay nag-i-install ng applications, hindi libraries. Bawat tool ay may sariling environment sa ilalim ng ~/.local/share/pipx/venvs/<name>, at naka-link ang mga executable ng tool na iyon sa ~/.local/bin. Dahil dito, hindi nagkakabanggaan ang dalawang tool na nangangailangan ng magkaibang version ng iisang library.

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

Idinadagdag ng pipx ensurepath ang ~/.local/bin sa PATH sa pamamagitan ng pag-edit ng shell startup file. Hindi nito mababago ang shell na kasalukuyan mo nang ginagamit. Kaya kung http: command not found agad ang resulta pagkatapos ng installation, malamang na hindi ka pa nag-log out at nag-log in muli. Idinadagdag lang ng default na ~/.profile ng Ubuntu ang ~/.local/bin kapag umiiral na ang directory na iyon sa oras ng login. Kaya isang beses lang ito nagiging problema sa bagong account at hindi na nauulit.

Kapag itinuro mo ang pipx sa isang library, tatanggi ito at magpapakita ng mensaheng nagsisimula sa:

No apps associated with package requests or its dependencies.

Sinasabi ng tool na mali ang instrumentong ginagamit mo. Dapat ilagay ang libraries sa venv ng application.

Ang mahalagang detalye sa server ay ang lokasyon. Ang karaniwang pipx install ay naglalagay ng lahat sa home directory ng isang user. Hindi ito makikita ng systemd unit na tumatakbo bilang myapp. Hindi rin ito makikita ng root cron job, at hindi rin ito mahahanap ng sudo dahil pinapalitan ng secure_path sa /etc/sudoers ang PATH ng fixed list. Kung dapat gamitin ng buong machine ang isang tool, i-install ito globally.

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

Inilalagay ng --global flag ang mga environment sa /opt/pipx at inililink ang mga executable sa /usr/local/bin. Kasama ito sa default na PATH at nasa loob din ng secure_path. Suriin muna ang version gamit ang pipx --version. Sa Ubuntu 24.04, pipx 1.4.3 ang package, na mas luma kaysa --global. Sa mas lumang version, ang sagot ng pipx ay unrecognized arguments: --global. Sa version na iyon, ikaw mismo ang magtakda ng dalawang documented directory:

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

Dapat mag-print ang command -v ansible ng /usr/local/bin/ansible. Kung nag-print ito ng path sa ilalim ng /home, napunta ang tool sa account ng isang user lamang at hindi ito mahahanap ng anumang service.

uv kapag kailangan mo ng lockfile

Ang uv ay isang single binary mula sa Astral na sumasaklaw sa ginagawa ng pip, venv, at pip-tools, at kaya rin nitong mag-download ng mga interpreter. Sapat ang bilis nito para mapansin ang kaibahan sa isang maliit na VPS, at nagsusulat ito ng aktuwal na lockfile.

Inilalagay ng official installer ang uv at uvx sa ~/.local/bin:

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

Kailangang mag-ingat sandali kapag nagpi-pipe ng script papunta sa shell sa isang server. I-pin ang version sa URL at basahin ang file bago ito patakbuhin:

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

Gumagana rin ang pipx install uv kapag mayroon nang pipx. Ang uv ay isang self-contained binary na walang sariling Python dependency, kaya valid na paraan ang pagkopya nito sa /usr/local/bin upang maibahagi ito sa bawat user sa server.

Para sa project na may pyproject.toml, apat na command ang workflow, at ang huli lamang ang pinapatakbo sa server.

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

Isinusulat ng uv lock ang uv.lock, isang cross-platform lockfile na naglalaman ng eksaktong mga resolved version, at kino-commit mo ito katabi ng code. Binubuo ng uv sync ang .venv sa project root upang tumugma rito. Sa server, ang --frozen ang mahalagang flag: tinutukoy ito ng documentation bilang paggamit sa mga version sa lockfile bilang source of truth sa halip na tingnan kung up to date ang lockfile. Ito ang behavior na kailangan sa deployment. Iniiwan ng --no-dev ang development dependency group.

Hindi kailangang i-convert ang isang umiiral na requirements.txt project, dahil nauunawaan ng uv ang wika ng pip:

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

Ang nalilikha nito ay isang ordinaryong virtual environment. Gumagana ang .venv/bin/python eksakto gaya ng gagawin nito kung python3 -m venv ang bumuo nito, kaya walang kailangang baguhin sa mga susunod na bahagi ng guide na ito.

May isang default ng uv na dapat mong malaman bago ito gamitin sa server. Ang python-preference setting nito ay default sa managed. Inilalarawan ito sa documentation bilang pagpili sa “mga dina-download at ini-install ng uv” kaysa sa mga interpreter na mayroon na sa system. Kaya ang uv venv --python 3.13 sa isang server na 3.12 lamang ang naka-install ay tahimik na kumukuha ng 3.13 sa ~/.local/share/uv/python sa halip na mag-fail. Maginhawa ito sa laptop ngunit nakakagulat sa server, dahil nakadepende na ang service mo sa isang interpreter na nasa home directory at hindi kailanman maipa-patch ng apt upgrade. Itakda ang python-preference sa only-system sa uv.toml kung gusto mong gamitin ang interpreter ng distribution. Kung gusto mong ilagay ang environment sa ibang lokasyon kaysa sa project root, tinutukoy ng UV_PROJECT_ENVIRONMENT ang directory na gagamitin para sa project virtual environment.

Ituro ang systemd sa venv interpreter, hindi sa activate

Dito kadalasang nasisira ang mga Python deployment, at ang sanhi ay maling pagkaunawa sa ginagawa ng activate.

Ang bin/activate ay isang shell script. Inuuna nito ang directory na bin ng venv sa PATH, itinatakda ang VIRTUAL_ENV, sine-save ang mga dating value para maibalik ng deactivate, at binabago ang prompt. Wala itong nilalamang binabasa mismo ng interpreter. Convenience lamang ang activation para sa taong nagta-type ng python sa prompt.

Ang aktuwal na pumipili ng environment ay ang interpreter file na ine-execute mo. Kapag nagsimula ang /srv/myapp/.venv/bin/python, hinahanap ng Python site module ang isang file na pyvenv.cfg sa directory na kinaroroonan ng executable at sa directory na isang level sa itaas nito. Kapag nakita ang /srv/myapp/.venv/pyvenv.cfg, itinatakda ang sys.prefix sa venv, kaya napupunta ang site-packages ng venv sa sys.path. Iyan ang buong mechanism. Hindi ito nangangailangan ng environment variable o shell.

Kaya hindi kailanman nagsisimula ang unit na ito:

[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

Ang ExecStart ay hindi isang shell command line. Direktang nag-e-execute ang systemd ng program, kaya walang source builtin, ipinapasa ang && bilang literal na argument, at walang nag-e-expand.

At nagsisimula ang unit na ito, pagkatapos ay namamatay:

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

Ang /usr/bin/python3 ay ang system interpreter, at hindi kailanman napaloob sa sys.path nito ang venv mo. Gumagana ang parehong command sa SSH session mo dahil na-activate mo roon ang venv, kaya ni-resolve ng shell ang python3 sa pamamagitan ng PATH papunta sa .venv/bin/python3.

Gumagana ang pag-wrap ng command sa /bin/bash -c 'source ... && gunicorn ...'. Naglalagay din ito ng shell sa pagitan ng systemd at ng process mo nang walang pakinabang, samantalang sapat na ang isang absolute path:

[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

Dapat i-report ng systemctl status myapp ang active (running) na may Main PID na iyong gunicorn process. Kung iba ang lumabas, basahin ang journal.

Hindi naroon ang linyang Environment=PATH= para sa ExecStart, dahil dala na nito ang buong path. Naroon ito para sa mga process na sinisimulan ng application mo. Nagmamana ang isang service ng maikling default na PATH mula sa systemd, kaya hindi mahahanap ng Python code na tumatawag sa subprocess.run(["ffmpeg", ...]), o ng management command na nag-e-execute ng console script mula sa venv, ang kailangan nito. Ang paglalagay sa directory na bin ng venv bilang unang entry ang isang bahagi ng activate na talagang ginagamit ng service. Tingnan kung ano talaga ang natanggap ng unit gamit ang systemctl show -p Environment myapp.

Saklaw din ng parehong rule ang mga naka-schedule na trabaho. Nagpapatakbo ang cron ng mga job gamit ang PATH na /usr/bin:/bin, kaya ang crontab line na nagbabasa ng python3 /srv/myapp/cleanup.py ay nagpapatakbo ng system interpreter at nagfa-fail nang may ModuleNotFoundError alas-3 ng madaling-araw, habang napupunta ang error sa local mail spool na walang nagbabasa. Isulat din doon ang absolute venv path. Para mapunta sa journal ang output na iyon at magkaroon ng record ng huling run, isang pares ng systemd service at timer ang gumagamit ng parehong linyang ExecStart.

Maaari bang palitan ng Docker ang desisyong ito?

May sarili nitong filesystem ang isang container, kaya nagbabago ang anyo ng tanong sa halip na mawala. Sa isang official image gaya ng python:3.12-slim, built-in sa /usr/local ang Python at walang dalang EXTERNALLY-MANAGED marker, kaya ang pip install bilang root ang tamang paraan para magdagdag ng packages at kaunti lamang ang pakinabang ng venv. Sa halip, i-build ang FROM ubuntu:24.04 at muli mong makikita ang externally-managed-environment sa loob ng image, sa parehong dahilan gaya sa host: ito ang interpreter ng distribution na may dalang marker file ng distribution.

Gumagamit pa rin ng venv ang maraming image dahil pinapasimple nito ang multi-stage build. Nag-i-install ang builder stage sa /opt/venv, at kinokopya ng runtime stage ang directory na iyon habang iniiwan ang mga compiler. Kasama ring napapasa ang problema sa activate. Ang isang RUN source /opt/venv/bin/activate line ay nakaaapekto lamang sa shell ng build layer na iyon, kaya sa runtime nagsisimula ang container gamit ang system interpreter at naglalabas ng ModuleNotFoundError. Itakda ang ENV PATH="/opt/venv/bin:$PATH", o ibigay sa CMD ang absolute path na /opt/venv/bin/gunicorn. Pareho itong bug sa systemd, ngunit nasa ibang file.

Pinapalitan ng container ang tanong tungkol sa interpreter dahil naka-pin ng image ang interpreter at lahat ng nasa ilalim nito. Hindi nito pinapalitan ang tanong tungkol sa pinning. Ang image na binuo mula sa hindi naka-pin na requirements.txt ay maaaring mag-resolve ng ibang versions sa susunod na buwan. Ibig sabihin, reproducible ang image tag, pero hindi reproducible ang build na gumawa nito. Ang lockfile gaya ng uv.lock, o isang fully pinned requirements file, ang nagsasara sa gap na iyon, container man o hindi. At kapag isang application lang ang tumatakbo sa isang VPS sa ilalim ng systemd, kadalasan ay inililipat lamang ng container ang parehong desisyon sa isang Dockerfile, dahil nire-restart na ng systemd ang failed process at kinukuha ang output nito sa journal. Sulit ang Pagpapatakbo ng Docker sa isang VPS kapag gusto mong ang mismong built image ang ide-deploy.

FAQ

Maaari ko bang gamitin na lang ang pip install kasama ang --break-system-packages?

Hindi sa server na kailangan mong patuloy na patakbuhin. Ginagawa ng flag ang eksaktong sinasabi nito: inaalis nito ang guard, at nagsusulat ang pip sa /usr/local/lib/python3.12/dist-packages, na nauuna sa apt directory sa sys.path. Dahil dito, nagiging shadow ang version mo sa version ng distribution para sa bawat system script na tumatakbo sa ilalim ng /usr/bin/python3. Naniniwala pa rin ang apt na naka-install ang sarili nitong version, kaya walang nakakakita sa conflict hanggang sa may masira. Sa loob ng container image na bawat pagkakataon ay nire-rebuild mula sa simula, sa image lang nananatili ang pinsala, kaya katanggap-tanggap ito roon. Sa machine na mina-maintain mo, gumawa ng venv. Isang command lang iyon.

Saan dapat ilagay ang virtual environment sa server?

Sa sariling directory ng application, bilang /srv/myapp/.venv, na pagmamay-ari ng deploy user, at ang service account ay may read at execute access lamang. Gumamit ng tig-isang venv bawat application, dahil maaaring masira ng upgrade para sa unang application ang pangalawa kapag shared ang venv. Huwag ilipat o kopyahin ang venv matapos itong gawin: nakasulat ang absolute path nito sa shebang line ng bawat script sa bin/ directory nito, kaya mabibigo ang inilipat na venv at lalabas ang bad interpreter: No such file or directory. I-delete ito at i-rebuild mula sa requirements.txt sa halip.

Bakit nabibigo ang systemd service ko dahil sa ModuleNotFoundError?

Dahil interpreter na hindi sa venv ang ine-execute ng unit. Patakbuhin ang systemctl cat myapp at basahin ang ExecStart. Dapat nitong tukuyin ang /srv/myapp/.venv/bin/python, o ang console script mula sa kaparehong bin/ directory, gamit ang absolute path. Hindi gagana ang pag-source ng activate sa unit file dahil hindi shell ang ExecStart, at iniuulat ng systemd ang Failed to locate executable source kasama ang status=203/EXEC. Idagdag ang Environment=PATH=/srv/myapp/.venv/bin:/usr/local/bin:/usr/bin:/bin upang mahanap din ng anumang subprocess na sinisimulan ng code mo ang tools ng venv.

Dapat ko bang gamitin ang uv sa halip na venv at pip?

Gamitin ang uv kapag kailangan mo ng lockfile, kapag sapat na mabagal ang install time para maging problema, o kapag kailangan mo ng Python version na hindi inilalabas ng iyong distribution. Gumagawa ito ng ordinaryong venv, kaya hindi nagbabago ang systemd unit at file layout, at eksaktong ini-install ng uv sync --frozen ang nire-record ng lockfile. Kung nagde-deploy ang isang application mula sa git gamit ang naka-pin na requirements.txt at natatapos ang install sa loob ng ilang segundo, sapat na ang python3 -m venv, at isa itong binary na mas kaunti mong kailangang panatilihing updated sa server.

#python#venv#pipx#uv#deployment