Battery dead at 3 PM — a real problem for developer laptops
I’ve been using Fedora as my main development machine for 2 years and I’m pretty happy with the package update cadence, but one thing took a lot of time to tune correctly: power management. My ThinkPad X1 Carbon initially only lasted 4–5 hours under continuous compilation. After understanding how power-profiles-daemon and tuned work together, I pushed it to 7–8 hours under real working conditions.
Fedora 39+ ships with power-profiles-daemon pre-installed — this is the GNOME/freedesktop daemon, completely different from TLP that Ubuntu typically uses. tuned is a lower-level tuning tool developed by Red Hat that directly controls everything from the CPU governor to the disk scheduler. These two don’t replace each other — configured correctly, they complement each other very well.
Why you need both instead of just one
power-profiles-daemon operates at a high level: it exposes 3 profiles (power-saver, balanced, performance) over D-Bus, which GNOME Shell reads and displays in the system tray. When you select a profile, the daemon calls down to drivers like amd-pstate or intel_pstate to adjust EPP (Energy Performance Preference).
tuned goes deeper: it controls CPU governor, IRQ affinity, disk I/O scheduler, network latency, and even kernel parameters via sysctl. The balanced profile in tuned and the balanced profile in power-profiles-daemon are two completely different things.
The problem I ran into was that tuned defaults to overriding the CPU governor, directly conflicting with power-profiles-daemon. It took about 3 evenings of reading source code and testing each option, but I eventually found a way to make the two coexist peacefully.
Installation and checking initial state
On Fedora 39+, power-profiles-daemon is already installed. Quick check:
# Check if the daemon is running
systemctl status power-profiles-daemon
# View the current profile
powerprofilesctl
# Sample output:
# * balanced:
# Driver: amd-pstate-epp (or intel_pstate)
# Degraded: no
#
# power-saver:
# ...
# performance:
# ...
Install tuned if not already present:
sudo dnf install tuned tuned-utils tuned-gtk -y
sudo systemctl enable --now tuned
# Check the currently active profile
tuned-adm active
# View all available profiles
tuned-adm list
Before doing anything else, check whether these two are conflicting:
# Check current CPU governor
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
# If it returns "powersave" — power-profiles-daemon is in control (correct)
# If it returns "schedutil" or "ondemand" — tuned may be overriding it
Detailed configuration to avoid conflicts
Step 1: Choose a tuned profile that doesn’t override the CPU governor
The root cause: some default tuned profiles set the CPU governor, directly clashing with power-profiles-daemon. The solution is to use tuned’s balanced profile but create a custom sub-profile that disables the CPU governor portion:
sudo mkdir -p /etc/tuned/fedora-dev
sudo nano /etc/tuned/fedora-dev/tuned.conf
File contents:
[main]
summary=Fedora Developer Profile — no CPU governor override
include=balanced
[cpu]
# Do not set governor — let power-profiles-daemon manage it
# governor=
energy_perf_bias=normal
min_perf_pct=10
[disk]
apm=128
spindown=0
alpm=min_power
[sysctl]
vm.swappiness=10
vm.dirty_ratio=15
vm.dirty_background_ratio=5
kernel.nmi_watchdog=0
[usb]
autosuspend=1
timeout=10
Activate the new profile:
sudo tuned-adm profile fedora-dev
# Verify there are no warnings
tuned-adm verify
Step 2: Create a udev rule to automatically switch power profiles when plugging/unplugging the charger
This is the part I found most useful. GNOME automatically switches profiles when AC/battery status changes, but if you’re using a different desktop environment or want more explicit control:
sudo nano /etc/udev/rules.d/81-power-profile.rules
# Switch to power-saver when unplugging the charger
SUBSYSTEM=="power_supply", ATTR{online}=="0", RUN+="/usr/bin/powerprofilesctl set power-saver"
# Switch back to balanced when plugging in the charger
SUBSYSTEM=="power_supply", ATTR{online}=="1", RUN+="/usr/bin/powerprofilesctl set balanced"
sudo udevadm control --reload-rules
Step 3: Configure the performance profile for heavy compilation
I often need to build Docker images or compile Rust projects that take 15–20 minutes. Create aliases to switch quickly:
# Add to ~/.bashrc or ~/.zshrc
alias perf-on="powerprofilesctl set performance && echo 'Performance mode ON'"
alias perf-off="powerprofilesctl set balanced && echo 'Balanced mode restored'"
alias bat-mode="powerprofilesctl set power-saver && echo 'Battery saver ON'"
# Or use a script combined with build commands
build-heavy() {
powerprofilesctl set performance
"$@"
powerprofilesctl set balanced
}
In practice I use it like this:
# Automatically switches back to balanced after the build finishes
build-heavy cargo build --release
build-heavy docker build -t myapp .
Checking and monitoring power consumption
View real-time power consumption
# Install powertop if not already present
sudo dnf install powertop -y
# Run as root to see full information
sudo powertop
# Export an HTML report for detailed viewing
sudo powertop --html=/tmp/power-report.html
In the powertop output, the most important column is Power est. — I typically aim for under 8W at idle in power-saver mode, and under 12W in balanced with a few Chrome tabs open.
Monitor CPU frequency and EPP
# View current CPU frequency across all cores
watch -n1 'cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq | sort -n'
# View EPP (Energy Performance Preference) — only available with amd-pstate/intel_pstate
cat /sys/devices/system/cpu/cpu0/cpufreq/energy_performance_preference
# power-saver → "power"
# balanced → "balance_power"
# performance → "performance"
Comprehensive monitor script
#!/bin/bash
# ~/bin/power-status.sh
echo "=== Power Profile ==="
powerprofilesctl | grep -A1 '\*'
echo ""
echo "=== tuned Profile ==="
tuned-adm active
echo ""
echo "=== CPU Governor ==="
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
echo ""
echo "=== EPP ==="
cat /sys/devices/system/cpu/cpu0/cpufreq/energy_performance_preference 2>/dev/null || echo "Not supported"
echo ""
echo "=== Battery Status ==="
upower -i /org/freedesktop/UPower/devices/battery_BAT0 2>/dev/null | grep -E 'state|percentage|energy-rate'
echo ""
echo "=== Current Power Draw ==="
cat /sys/class/power_supply/BAT0/power_now 2>/dev/null | awk '{printf "%.2f W\n", $1/1000000}' || echo "Check /sys/class/power_supply/"
chmod +x ~/bin/power-status.sh
power-status.sh
Adjusting if power consumption is still high
After finishing the configuration, if the battery still drains quickly, run powertop --auto-tune once to see if any devices haven’t had autosuspend enabled:
# Only run this to view suggestions — don't make it permanent as some settings can cause issues
sudo powertop --auto-tune
# Check the "Tunables" tab in powertop interactive mode
# Items marked "Bad" are candidates for further optimization
One thing I learned after 6 months using this setup: don’t enable all auto-tune settings at once. I once had USB mouse lag because autosuspend was too aggressive. Enable one at a time, test for 1–2 days, then move on to the next — slow but reliable.
Real-world results after 6 months
With the setup above (tuned profile fedora-dev + udev rule for automatic switching + aliases for performance mode), my ThinkPad X1 Carbon Gen 11 achieves:
- Idle with a few terminals + browser: ~5W, battery lasts 9–10 hours
- Normal work (coding, Slack, email): ~8–10W, lasts 6–7 hours
- Heavy compilation in performance mode: ~25–30W but finishes ~40% faster than balanced
Compared to before any configuration, battery life improved by about 2–3 hours — enough that I no longer need to carry the charger when working out for the day.

