Python venvがUbuntu 26.04へのアップグレード後に壊れる理由と直し方
Ubuntu 24.04から26.04に上げるとシステムのpython3は3.12から3.14に変わり、24.04で作ったvenvは動かなくなります。原因をpyvenv.cfgとABIから説明し、requirementsの書き出し、venvの作り直し、pipxとuv、systemdユニットの修正まで解説します。
Ubuntu 26.04 へのアップグレード後に Python venv が壊れる理由
Ubuntu 24.04 から 26.04 にアップグレードすると、24.04 で作った Python の venv(仮想環境)は動かなくなります。システムの python3 が 3.12 から 3.14 に変わるのに、venv の中身は 3.12 を前提に作られているからです。直し方は、新しい python3 で venv を作り直し、パッケージを入れ直すことです。
作り直しが必要なのは、venv の中の複数の場所に 3.12 というバージョンが書き込まれているためです。以下では、その結びつきを自分のサーバーで確かめる方法を最初に説明します。そのあと、アップグレード前に書き出しておくもの、作り直しの手順、systemd ユニットと cron の確認、pipx と uv の扱いを順に説明します。
何が変わるのか: python3 は 3.12 から 3.14 へ
2026年10月2日時点の packages.ubuntu.com では、26.04 (resolute) の python3 パッケージは 3.14.3-0ubuntu2 で、python3.14 に依存しています。24.04 (noble) の python3 は 3.12 系です。同じ日に確認したところ、python3.12 というパッケージは 26.04 のリポジトリにありません。
アップグレード後に、次のコマンドで切り替わったことを確認できます。
python3 --version
readlink -f /usr/bin/python3python3 --version が Python 3.14 で始まり、readlink -f が /usr/bin/python3.14 を返せば、システム側の切り替えは終わっています。apt で入れた python3-requests のような Debian パッケージは、apt が 3.14 用のものに入れ替えます。問題になるのは、apt の外で pip を使って作った venv です。
venv はなぜ新しい Python に追従できないのか
venv の中身は、ベースとなるインタープリター(Python 本体)へのリンクと、パッケージを入れるディレクトリと、小さな設定ファイルです。このうち複数の場所に、作成時の Python のバージョンが入っています。ここでは例として /srv/app/.venv にある venv を使い、1か所ずつ確認します。
bin/python は何を指しているか
ls -l /srv/app/.venv/bin/ | grep pythonpython3 -m venv で作った venv では、bin/python3 が /usr/bin/python3 へのシンボリックリンクになり、bin/python と bin/python3.12 は同じディレクトリの python3 を指す形がよく見られます。この形の venv では、アップグレード後の bin/python は 3.14 を起動します。リンク先の /usr/bin/python3 自体が 3.14 になったからです。名前に 3.12 と付いた bin/python3.12 も、リンクをたどると同じ 3.14 に行き着きます。
python3.12 -m venv のようにバージョンを指定して作った venv では、リンク先が /usr/bin/python3.12 になります。こちらは /usr/bin/python3.12 が残っているかどうかで結果が変わります。消えていれば、リンクは存在しないファイルを指すので、venv の Python は起動しません。
どちらの形でも、実際に起動するバージョンは次のコマンドで分かります。
/srv/app/.venv/bin/python --versionpyvenv.cfg が記録していること
cat /srv/app/.venv/pyvenv.cfghome 行は、ベースのインタープリターがあるディレクトリです。通常は /usr/bin です。version 行は作成時のバージョンで、24.04 で作ったものなら 3.12 系の番号が入っています。Python は起動時にこのファイルを読み、home のディレクトリにある Python を自分の本体として扱います。home = /usr/bin のままなら、そこにある 3.14 とその標準ライブラリが使われます。version 行が 3.12 のままでも、上の --version が 3.14 を返すので、記録と実際が食い違っていることが分かります。
site-packages は lib/python3.12 の下にある
pip で入れたパッケージは lib/python3.12/site-packages に入っています。ディレクトリ名にバージョンが含まれている点が重要です。3.14 は自分のバージョンの lib/python3.14/site-packages を探すので、3.12 の下に入れたパッケージは見えません。
ls /srv/app/.venv/lib/
/srv/app/.venv/bin/python -c 'import sys; print(sys.version); print(sys.path)'lib/ の下には python3.12 しかなく、sys.path には lib/python3.12/site-packages が含まれていないはずです。これが、venv の Python 自体は起動するのに、アプリが import の段階で止まる理由です。pip も同じ lib/python3.12/site-packages に入っているので、venv の中の pip もそのままでは使えません。
コンパイル済みの拡張モジュールは 3.12 専用
ディレクトリ名を python3.14 に変えても、確実には直りません。numpy、psycopg2、pydantic-core、cryptography のように C や Rust で書かれた部分を持つパッケージは、特定の Python 向けにビルドされた .so ファイルを含んでいます。そのファイル名に、対象の Python を示す印があります。
find /srv/app/.venv/lib/python3.12/site-packages -name '*.cpython-312-*.so' | head
python3 -c 'import importlib.machinery as m; print(m.EXTENSION_SUFFIXES)'1行目は、cpython-312 という印の付いたファイルを表示します。2行目は、新しい Python が読み込む拡張子の一覧を表示します。x86_64 のサーバーでは .cpython-314-x86_64-linux-gnu.so が先頭に来て、cpython-312 は一覧にありません。そのため 3.14 は、3.12 用の拡張モジュールを読み込み対象として探しません。ABI(application binary interface、コンパイル済みのコード同士の取り決め)が Python のマイナーバージョンごとに違うので、wheel(ビルド済みのパッケージ)もバージョンごとに別のファイルとして配布されています。
コンソールスクリプトのシェバン
gunicorn や celery のように pip が bin/ に作るコマンドは、1行目(シェバン)に venv の中の Python の絶対パスが書かれています。
head -1 /srv/app/.venv/bin/gunicornこれらのコマンドは venv の Python で動くので、上の問題をすべて引き継ぎます。
4か所を合わせると、結論は一つです。venv は新しい Python で作り直します。中身を手で 3.14 に合わせても、コンパイル済みのファイルは使えるようになりません。
アップグレード前にやること: venv の中身を書き出す
作り直しに必要なのは、各 venv に何が入っていたかの一覧です。24.04 のうちなら、venv の pip がまだ動きます。Ubuntu 24.04 から 26.04 へのアップグレード手順を始める前に、次の作業を済ませてください。
まず、サーバー上の venv をすべて探します。venv には必ず pyvenv.cfg があるので、これを目印にします。
sudo find / -xdev -name pyvenv.cfg 2>/dev/null-xdev は、別のファイルシステムに入らないための指定です。/home や /srv を別のディスクにしている場合は、sudo find /home /srv -name pyvenv.cfg 2>/dev/null のように、そのパスも指定して実行します。
見つかった venv ごとに、Python のバージョンとパッケージの一覧を書き出します。
cd /srv/app
.venv/bin/python --version > python-version-before.txt
.venv/bin/pip freeze > requirements-py312.txt
wc -l requirements-py312.txtpip freeze は、入っているパッケージを 名前==バージョン の形で1行ずつ出力します。wc -l の行数が 0 なら、その venv には何も入っていないか、別の venv を見ています。プロジェクトに requirements.txt や pyproject.toml があり、正しく管理されているなら、そちらを正とします。書き出したファイルは、確認用の控えとして使います。
pipx で入れたツールは、ツールを入れたユーザーごとに一覧を取ります。
pipx list --short > ~/pipx-before.txtsystemd ユニットと crontab の内容も控えておきます。あとで、venv のパスやバージョン付きのパスを探すときに使います。app.service は自分のサービス名に置き換えてください。
systemctl cat app.service > ~/app-service-before.txt
sudo crontab -l > ~/root-crontab-before.txtアップグレード後に python3.12 は残っているか
do-release-upgrade は最後に、もう使われないパッケージ(obsolete packages)を削除するかどうかを尋ねます。ここで削除を断った場合に /usr/bin/python3.12 が残るかどうかは、推測せずに自分のサーバーで確かめてください。
ls -l /usr/bin/python3.12
apt-cache policy python3.12
apt list '~o'
apt-mark showauto | grep python3.12ls -l で、ファイルがあるかどうかが分かります。apt-cache policy のバージョン表に /var/lib/dpkg/status しか出てこない場合、そのパッケージはインストールされていますが、有効なリポジトリのどこからも提供されていません。apt list '~o' は、そうしたリポジトリにないパッケージをまとめて表示します。apt-mark showauto の結果に出てくれば、そのパッケージには「依存関係として自動で入った」という印が付いています。
残っていたとしても、それに頼るのは避けてください。理由は次のとおりです。
- 26.04 のリポジトリに
python3.12はないので、残ったファイルにはセキュリティ更新が届きません。 - 自動で入った印が付いていれば、もう
python3は 3.12 に依存していないので、あとでsudo apt autoremoveを実行したときに削除対象になります。削除はその日の作業とは関係のないタイミングで起き、venv はそこで初めて壊れます。 /usr/bin/python3を指している venv は、3.12 が残っていても 3.14 で起動します。3.12 が残ることで動き続けるのは、/usr/bin/python3.12を直接指す venv だけです。- 次のリリースアップグレードで、同じ問題がもう一度起きます。
どうしても 3.12 で動かし続ける必要があるなら、OS に残ったファイルではなく、自分で管理する 3.12 を用意する方が安全です。方法はあとの uv の節で説明します。
アップグレード自体が途中で止まった場合や、apt が壊れた状態になった場合は、venv より先にそちらを直してください。手順は失敗した Ubuntu リリースアップグレードからの復旧にまとめています。
Python 3.14 で venv を作り直す手順
ここからは 26.04 の上で作業します。まず、venv を作るための部品を入れます。Ubuntu では venv モジュールの一部が別パッケージに分かれているので、python3-venv がないと python3 -m venv は venv を作れません。
sudo apt install python3-venv次に、古い venv を消さずに別の名前へ移し、元と同じパスに新しい venv を作ります。venv は作ったあとでパスを変えると動きません。シェバンと activate スクリプトに絶対パスが書かれているからです。そのため、別の場所で作ってから名前を変える方法は使えません。最終的なパスに直接作ります。
cd /srv/app
sudo systemctl stop app.service
mv .venv .venv.py312
python3 -m venv .venv
.venv/bin/python --version
.venv/bin/pip install --upgrade pip
.venv/bin/pip install -r requirements-py312.txt
.venv/bin/pip check--version は Python 3.14 で始まるはずです。pip check は、入ったパッケージ同士の依存関係が満たされているかを調べます。足りない依存関係や合わないバージョンがあれば、そのパッケージ名を表示します。
venv の持ち主にも注意してください。アプリが app のような専用ユーザーで動いているなら、venv もそのユーザーで作ります。root で作ると、アプリがあとで venv に書き込もうとしたときに権限が足りません。
sudo -u app python3 -m venv /srv/app/.venv
sudo -u app /srv/app/.venv/bin/pip install -r /srv/app/requirements-py312.txt3.14 用の wheel がないパッケージ
requirements-py312.txt は、3.12 の時点のバージョンで固定されています。古いバージョンには、3.14 用の wheel がないことがあります。その場合 pip はソースからのビルドを試みるので、C コンパイラーと Python のヘッダーファイルが必要になります。ビルドに進ませず、wheel がないものを先に見つけるには次のようにします。
.venv/bin/pip install --only-binary=:all: -r requirements-py312.txt--only-binary=:all: はソースからのビルドを禁止するので、wheel がないパッケージのところで止まります。純粋な Python だけのパッケージでも、ソース配布(sdist)しか公開していないものはここで止まるので、止まった理由は1件ずつ確認します。3.14 用の wheel がない場合の基本の対処は、そのパッケージを 3.14 に対応したバージョンまで上げることです。PyPI のパッケージページで cp314 の wheel があるバージョンを確認し、requirements の該当行を書き換えます。ソースからビルドする方法もありますが、build-essential と python3-dev が必要になり、本番サーバーにコンパイラーを置くことになります。
依存パッケージのメジャーバージョンが上がると、アプリ側の修正が必要になることがあります。バージョンを上げたら、新しい venv の Python でアプリのテストを実行してから本番に戻してください。
書き出しを忘れた場合
アップグレード後は古い venv の pip が動きません。ただし、各パッケージの記録(*.dist-info ディレクトリ)はファイルとして残っています。新しい venv の pip に古い site-packages を読ませれば、一覧を作れます。
cd /srv/app
.venv/bin/pip freeze --path .venv.py312/lib/python3.12/site-packages > requirements-py312.txt
cat requirements-py312.txt--path は、一覧を取るディレクトリを指定するオプションです。パッケージのメタデータを読むだけなので、3.12 の Python がなくても動きます。出力の中身は目で確認してから使ってください。
アプリが新しい venv で問題なく動くことを確認したら、古い venv を消します。
rm -rf /srv/app/.venv.py312venv を作り直したあとに systemd ユニットと cron を確認する
同じパスに venv を作り直したなら、ExecStart=/srv/app/.venv/bin/gunicorn のような行はそのまま使えます。直す必要があるのは、パスにバージョンが入っている行と、システムの Python を直接指している行です。
grep -rn -e python3.12 -e .venv /etc/systemd/system /etc/cron.d /etc/crontab 2>/dev/null
sudo crontab -l
sudo crontab -u app -l探すものは次のとおりです。
ExecStart=/usr/bin/python3.12 manage.py ...のように、3.12 を直接指す行。venv の Python(/srv/app/.venv/bin/python)に書き換えます。Environment=PYTHONPATH=/srv/app/.venv/lib/python3.12/site-packagesのように、site-packages のパスを書いた行。venv の Python を使えば site-packages は自動で見つかるので、多くの場合この行は削除できます。- cron の中で
python3.12を呼んでいる行。これも venv のbin/pythonの絶対パスに書き換えます。cron の環境ではPATHが短いので、絶対パスで書く方が確実です。
ユニットファイルを書き換えたら、systemd に設定を読み直させてから起動します。
sudo systemctl daemon-reload
sudo systemctl start app.service
systemctl status app.service
journalctl -u app.service -n 50 --no-pagerstatus が active (running) を示し、ログに起動時の例外が出ていなければ完了です。起動しない場合は、終了コードから原因を切り分けます。その手順はsystemd ユニットが起動しないときの終了コードの読み方で説明しています。
pipx で入れたツールを入れ直す
pipx は、コマンドラインツールを1つずつ専用の venv に入れます。その venv は、ユーザーごとに ~/.local/share/pipx/venvs/ の下にあります(環境変数 PIPX_HOME を設定している場合はそちらです)。中身は普通の venv なので、上と同じ理由で壊れます。apt で入れた pipx はシステムの python3 で動き、新しく作る venv にも、指定がなければ自分が動いている Python を使います。
pipx の公式ドキュメントは、pipx reinstall-all を「Python を新しいバージョンに上げたあと、すべてのパッケージで新しい Python を使いたいときのもの」と説明しています。各パッケージをアンインストールし、最初のインストールと同じオプションで入れ直します。
pipx reinstall-all
pipx listpipx list は、各パッケージがどの Python で入っているかを表示します。すべて 3.14 になっていれば完了です。1つだけ入れ直すなら pipx reinstall <パッケージ名> を使います。どちらのコマンドも、--python で使う Python を指定できます。
pipx はユーザーごとに独立しています。ツールを入れているユーザーそれぞれで実行してください。root で reinstall-all を実行しても、一般ユーザーのツールは入れ直されません。最後に pipx list --short の結果を ~/pipx-before.txt と比べ、なくなったツールがないかを確認します。入れ直しに失敗したツールは、3.14 に対応した新しいバージョンが出ていないかを確認してください。
uv で管理している場合は何が違うのか
uv は、Python 本体を自分でダウンロードして管理できます。uv が入れた Python は、Linux では通常 ~/.local/share/uv/python/ の下に置かれ、apt とは関係がありません。そのため、uv が管理する Python で作った venv は、OS のアップグレードで /usr/bin/python3 が変わっても影響を受けません。
ただし、uv で作った venv がすべてこれに当てはまるわけではありません。uv のドキュメントによると、python-preference の既定値は managed で、uv が管理する Python を優先します。一方で、システムの Python が見つかれば、新しくダウンロードするよりそちらを使います。24.04 で uv 管理の Python を一度も入れずに uv venv を実行していれば、その venv は /usr/bin の Python で作られている可能性があります。
どちらなのかは、pyvenv.cfg の home 行で分かります。
grep home /srv/app/.venv/pyvenv.cfg
uv python list --only-installedhome が ~/.local/share/uv/python/ の下(ファイルの中では展開された絶対パス)を指していれば、その venv は uv の管理する Python で動いていて、今回のアップグレードの影響は受けていません。home = /usr/bin なら、システムの Python で作られた venv なので、上と同じ理由で作り直しが必要です。
uv のプロジェクト(pyproject.toml と uv.lock があるもの)なら、作り直しは短く済みます。依存関係は uv.lock に記録されているので、書き出しの作業はいりません。pyproject.toml の requires-python が 3.14 を許していることを先に確認してください。
cd /srv/app
uv python install 3.14
uv python pin 3.14
rm -rf .venv
uv sync
uv run python --versionuv python pin は、使う Python のバージョンを .python-version ファイルに書き込みます。uv sync は、そのバージョンで .venv を作り、uv.lock のとおりにパッケージを入れます。最後のコマンドが Python 3.14 で始まれば完了です。
アプリがまだ 3.14 に対応していない場合は、上の 2 行で 3.12 を指定すれば、uv が管理する 3.12 で venv を作れます。これが、OS に残った 3.12 に頼らずに 3.12 を使い続ける方法です。ただし、この 3.12 には apt の更新が届かないので、新しいパッチ版への更新は uv 側で自分で行う必要があります。3.14 への移行が終わるまでの一時的な手段として使ってください。
requirements.txt で運用している uv 環境なら、uv venv --python 3.14 で venv を作り、uv pip install -r requirements-py312.txt でパッケージを入れます。どの道具をどの用途に使うかは、サーバーでの venv と pipx と uv の使い分けで比較しています。
次のアップグレードに備えて
Ubuntu の LTS は2年ごとに出て、そのたびにシステムの Python のマイナーバージョンが変わることがあります。次の作業を楽にするために、依存関係の一覧はサーバーの上ではなくリポジトリで管理します。venv を作るコマンドは、短いスクリプトにしておきます。systemd ユニットと cron には python3.12 や lib/python3.12 のようなバージョン付きのパスを書かず、venv の bin/python だけを書きます。こうしておけば、次回の作業は venv の作り直しとサービスの再起動だけになります。
FAQ
Ubuntu 26.04 にアップグレードしたら、venv のパッケージが import できなくなったのはなぜですか?
26.04 ではシステムの python3 が 3.14 になります。24.04 で python3 -m venv を使って作った venv の多くは、bin/python が /usr/bin/python3 へのリンクなので、アップグレード後は 3.14 が起動します。3.14 は lib/python3.14/site-packages を探しますが、パッケージは lib/python3.12/site-packages に入っているので見つかりません。.venv/bin/python --version と ls .venv/lib/ で確認できます。直し方は、新しい python3 で venv を作り直してパッケージを入れ直すことです。
lib/python3.12 を python3.14 に名前変更すれば venv は直りますか?
確実には直りません。numpy や cryptography のようにコンパイル済みの部分を持つパッケージは、cpython-312 という印の付いた .so ファイルを含んでいて、3.14 はその名前のファイルを読み込み対象にしません。3.14 が読む拡張子は python3 -c 'import importlib.machinery as m; print(m.EXTENSION_SUFFIXES)' で確認できます。pip freeze でパッケージの一覧を取り、venv を作り直してください。
アップグレード時に古いパッケージの削除を断れば、python3.12 と今の venv は使い続けられますか?
残っているかどうかは、ls -l /usr/bin/python3.12 と apt-cache policy python3.12 で自分のサーバーを確認してください。残っていても、頼るのは避けてください。2026年10月2日時点で 26.04 のリポジトリに python3.12 はないので、セキュリティ更新が届きません。自動で入ったという印が付いていれば、あとの sudo apt autoremove で削除されます。また、/usr/bin/python3 を指す venv は、3.12 が残っていても 3.14 で起動します。
pipx で入れたコマンドが Ubuntu のアップグレード後に動きません。どう直しますか?
pipx のツールはそれぞれ専用の venv に入っているので、システムの Python が変わると同じ理由で壊れます。ツールを入れたユーザーで pipx reinstall-all を実行すると、最初のインストールと同じオプションで、すべてのツールを新しい Python で入れ直します。pipx list で各ツールの Python が 3.14 と表示されれば完了です。pipx はユーザーごとに独立しているので、ツールを入れたユーザーそれぞれで実行してください。