Taming Gentoo's USE Flags: A Practical Strategy for Consistent Builds
Gentoo's USE flags offer fine-grained control but silently drift across updates. A structured per-package declaration strategy makes builds predictable, bisectable, and auditable — with a concrete container example.
25 Jan 2026, 22:54 UTC

The Problem: Flag Drift Across Updates
You set up a Gentoo system, carefully tune USE flags in /etc/portage/make.conf, and everything compiles cleanly. Six months later, a @world update pulls in a new dependency that ignores your global flags because a package-specific override in /etc/portage/package.use/ conflicts with a newly added default. The build fails, or worse, succeeds with missing functionality you only discover at runtime.
USE flags are Gentoo's compile-time feature toggles. Each flag (like ssl, wayland, python) tells Portage whether to enable optional code paths and pull in corresponding dependencies. The system evaluates flags in a strict precedence order: profile defaults → make.conf → package.use/ files → per-package package.use/ entries → command line. When these layers contradict, the most specific wins — but the interaction isn't always obvious.
A Concrete Strategy: Explicit Per-Package Declarations
Instead of relying on global USE in make.conf to propagate everywhere, declare flags explicitly for each package that needs them. This makes the effective configuration visible in one place and prevents silent changes when profiles or ebuilds update.
Create a structured /etc/portage/package.use/ directory with one file per package or functional group:
/etc/portage/package.use/
├── 00-base
├── 10-desktop
├── 20-server
└── 99-local
Prefix filenames with numbers to control load order (Portage reads them lexicographically). In 00-base, set flags that apply broadly but need to be explicit:
# 00-base
sys-apps/portage -epydoc
app-shells/bash -plugins
sys-libs/glibc -audit -caps -cet -fortify -multiarch -nscd -profile -rtld-debug -stack-realignment -static-libs -systemtap
In 10-desktop, enable graphical stack flags only for packages that actually use them:
# 10-desktop
media-libs/mesa vulkan video_cards_radeon video_cards_intel
x11-libs/gtk+ -colord -cloudprint -introspection
x11-libs/qt5gui -eglfs -gles2 -vulkan
www-client/firefox -eme-free -gmp-autoupdate -pulseaudio -screencast -wayland
media-sound/pulseaudio -bluetooth -dbus -equalizer -gtk -orc -realtime -systemd -webrtc-aec
This approach surfaces conflicts immediately. If media-libs/mesa adds a new video_cards_nouveau flag default, your explicit video_cards_radeon video_cards_intel line prevents it from being pulled in.
Worked Example: Building a Minimal Container Base
Suppose you need a minimal Gentoo stage3 for a container that runs a single Go binary. You want no documentation, no NLS, no X11, no systemd, and no unnecessary libraries. Instead of a sprawling make.conf, create /etc/portage/package.use/99-container:
# 99-container
* -doc -man -nls -gtk -gtk3 -qt5 -qt6 -wayland -X -alsa -pulseaudio -systemd -elogind -policykit -udev -acl -caps -pam -selinux -apparmor
app-admin/doas -pam
sys-apps/busybox -static -pam -systemd
sys-apps/openrc -selinux -netifrc
sys-libs/glibc -audit -caps -cet -fortify -multiarch -nscd -profile -rtld-debug -stack-realignment -static-libs -systemtap
sys-libs/ncurses -ada -cxx -debug -doc -gpm -minimal -profile -static-libs -test -tinfo -trace
sys-libs/readline -static-libs -utils
dev-lang/go -race -vet
net-misc/openssh -kerberos -ldap -libedit -pam -selinux -skey -ssl -systemd -xmss
The leading * wildcard applies the negative flags to every package unless overridden later in the same file. The specific entries below then re-enable only what's needed (like ssl for openssh). Run emerge --pretend --verbose @world to verify the flag set before committing.
Trade-Off: Maintenance Overhead
Explicit per-package flags require more lines than a global USE="-X -wayland -systemd". When you add a new package, you must decide its flags. However, this overhead pays off in two ways:
- Predictable diffs:
git diff /etc/portage/package.use/shows exactly what changed and why. - Bisectable regressions: If a build breaks after an update,
git bisecton your package.use directory isolates the flag change.
The risk is stale flags. A package may drop a flag you explicitly set (harmless, Portage warns) or add a new flag you didn't anticipate (silent, may pull unwanted deps). Mitigate by running euse -I periodically to list all flags known to installed packages, then cross-reference with your declarations.
Verification Checklist
- After editing
package.use/, runemerge --pretend --verbose @worldand scan for unexpected[ebuild N ]or[ebuild R ]entries. - Check effective flags for a specific package:
emerge --pretend --verbose =category/package-1.2.3shows the resolved USE line. - List all active flags system-wide:
emerge --info | grep ^USE=. - Audit for flags you set that no longer exist:
euse -I | grep -v -f <(cat /etc/portage/package.use/* | grep -v '^#' | awk '{print $2}' | tr ' ' '\n' | sort -u)(run as root).
Closing Action
Pick one package you rebuild frequently (e.g., sys-kernel/gentoo-kernel or www-client/firefox). Move its flags out of make.conf into a dedicated file under /etc/portage/package.use/. Commit the change to version control. Next time you update, you'll see exactly what that package pulls — and why.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.