Upgrading Python on FreeBSD with Poudriere
If you’re running an older Python version on FreeBSD that’s picked up known CVEs, and you don’t want to gamble on an untested package hitting a production system, building it yourself with poudriere is the safer route. This walks through setting up poudriere and using it to build and deploy a newer Python version — in this example, upgrading from python311 to python313.
This approach is especially useful if you’re stuck on an older base system release for a while (say, due to a busy season or a scheduling conflict) and want to patch specific security issues without doing a full, disruptive OS upgrade right away.
1. Install poudriere
pkg install poudriere
2. Configure poudriere
Edit /usr/local/etc/poudriere.conf and set the essentials:
ZPOOL=zroot # your ZFS pool name
BASEFS=/usr/local/poudriere
FREEBSD_HOST=https://download.freebsd.org
DISTFILES_CACHE=/usr/ports/distfiles
USE_TMPFS=all
Poudriere works best on ZFS. It can run on UFS too, but expect slower builds.
3. Create a build jail matching your live system
The jail should match the FreeBSD release you’re actually running in production, since that’s what the resulting packages need to be compatible with:
poudriere jail -c -j fbsd135 -v 13.5-RELEASE -a amd64
4. Create a ports tree
poudriere ports -c -p default
5. Build the new Python version
poudriere bulk -j fbsd135 -p default lang/python313
This builds python313 and shows you everything else that gets pulled into the dependency chain. It’s worth reviewing this output closely — it tells you up front what else in your ports tree currently depends on the old Python version and will need rebuilding.
Check the end of the log for a clean [100%] completion with no failed builds before moving on.
6. Point pkg at your local poudriere repo
Add a repo config file at /usr/local/etc/pkg/repos/local.conf:
local: {
url: "file:///usr/local/poudriere/data/packages/fbsd135-default",
enabled: yes,
priority: 10
}
Then refresh pkg’s view of the repo:
pkg update
7. Install the new Python version
pkg install lang/python313
8. Rebuild anything still linked to the old version
REBUILD=$(pkg query -g "%n:%dn" '*' | grep py3 | grep -v py313 | cut -d : -f 1 | sort -u)
pkg install -f $REBUILD
9. Confirm the old version is gone and the CVEs are cleared
pkg audit -F
Run this last check to confirm the vulnerable package no longer shows up in the audit report, and that nothing new got flagged in the process.
A note on scope
This process patches a specific package-level vulnerability without touching the base FreeBSD release. It’s a useful stopgap, but it isn’t a substitute for keeping the base system itself on a supported release. If you’re using this approach because you’re behind on a major FreeBSD version upgrade, treat it as buying time — not as a long-term fix.