Why my tmux status bar keeps flickering after a system update

Flickering isn’t just a visual nuisance; in a hardened environment it can hide real alerts, break scripts that poll the status line, or even leak timing info to an attacker. A recent upgrade that swapped in a new terminal renderer, patched a font, or tweaked an environment variable can push the status bar past the terminal’s refresh limits. Below is a practical, step‑by‑step walk‑through that covers the usual suspects, fixes you can apply immediately, and how to keep the bar steady without opening new attack vectors.


1. Reproduce the problem

# Start a fresh tmux session
tmux new-session -d -s flicker-test

# Attach to the session
tmux attach-session -t flicker-test

If the status line jitters, disappears for a blink, or redraws every few milliseconds, you’re looking at a rendering issue. The first thing to do is isolate what changed during the update.


2. Check the terminal emulator

The emulator is the bridge between tmux and your screen. Modern ones (Alacritty, Kitty, WezTerm) lean on GPU acceleration; older ones (xterm, urxvt) stay CPU‑centric. An upgrade might have flipped the default backend.

# For Alacritty
alacritty --version

# For Kitty
kitty --version

# For WezTerm
wezterm --version

If you notice the emulator switched to a GPU‑based renderer, the status bar can flicker when tmux tries to repaint the whole line on every keystroke. The quick fix is to turn off GPU acceleration for the status bar or roll back the emulator.

# ~/.config/alacritty/alacritty.yml
# Example: Disable GPU in Alacritty
gpu: false

Trade‑off: Disabling GPU hurts performance for heavy terminal workloads (think large log streams) but is a small price to pay for a readable status bar in a security‑critical session.


3. Verify tmux version and config

An update may have bumped tmux itself. Starting with 3.3, status-interval forces a redraw every N seconds. If it’s set too low, you’ll see flicker.

tmux -V

Open .tmux.conf and look for:

# Potential culprit
set -g status-interval 1

A safer value is 5 seconds:

set -g status-interval 5

If you’re using TPM, a plugin might have been updated to a version that triggers frequent redraws. Pin the plugin or disable it for now.

tmux list-plugins

4. Inspect environment variables

Updates often tweak env vars that tmux inherits. TERM is the usual offender. A mismatch between the emulator’s TERM and tmux’s expectations can force a redraw loop.

echo $TERM

Typical safe values:

  • xterm-256color
  • screen-256color
  • alacritty
  • kitty

If you see something odd like xterm-256color+256color, set a sane value:

export TERM=xterm-256color

Add that export to your shell profile (~/.bashrc or ~/.zshrc) to make it stick.


5. Look at font changes

A font update that changes glyph widths or introduces ligatures can make tmux recalculate the status layout on every redraw. If a non‑monospaced font slips in, the line will misalign.

fc-match -f '%{family[0]}\n' :default

If the output is a proportional font, switch back to a monospaced one:

# ~/.config/alacritty/alacritty.yml
font:
  normal:
    family: "Fira Code"
    style: Regular

Why it matters: A non‑monospaced font can hide subtle differences in command output, giving a malicious payload a chance to slip by visually.


6. Examine the status line content

A status line that pulls data from external commands (git, uptime, weather) can trigger frequent redraws if those commands are slow or variable. The line flickers as tmux clears and redraws.

# Heavy status line
set -g status-left '#{?client_prefix,#[fg=red]PREFIX,} #[fg=green]#(git branch --show-current)'

Cache the output in a script so tmux reads a static file instead of running a command each time:

# ~/.tmux/git-branch.sh
#!/usr/bin/env bash
echo "$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo 'detached')"
chmod +x ~/.tmux/git-branch.sh

Then update the status line:

set -g status-left '#{?client_prefix,#[fg=red]PREFIX,} #[fg=green]#(~/.tmux/git-branch.sh)'

7. Disable status line animations

Some themes sprinkle subtle animations (color fades, etc.) that can cause flicker on high‑refresh terminals. Strip any status-style or status-left-style entries that reference animation.

set -g status-style bg=black,fg=white

8. Test with a minimal configuration

Drop everything else and see if flicker persists.

mv ~/.tmux.conf ~/.tmux.conf.bak
echo 'set -g status-interval 5' > ~/.tmux.conf
tmux kill-server
tmux new-session -d -s test
tmux attach-session -t test

If the flicker disappears, your original config is the culprit. Re‑enable sections one by one until it comes back.


9. Monitor redraw events

tmux can log redraws with a hook. This helps spot what’s triggering them.

tmux set-hook -g client-redraw 'run-shell "echo $(date +%s.%N) REDRAW >> ~/tmux-redraw.log"'

After reproducing the flicker, check the log:

tail -n 20 ~/tmux-redraw.log

A burst of entries every few ms usually points to a plugin or key‑binding that runs a command on each keystroke.


10. Harden the environment

Once flicker is gone, lock in a stable setup:

Practice Why it matters Implementation
Pin tmux Prevent accidental upgrades that change redraw logic apt-mark hold tmux (Debian/Ubuntu)
Dedicated terminal profile Separate security sessions from casual work Create a security profile in Alacritty with gpu: false
Enforce monospaced fonts Avoid visual deception Set font.family to a known monospaced font
Cache dynamic status content Reduce surface area for timing attacks Use scripts with timeout and validation
Audit plugins Third‑party code can introduce regressions tmux list-plugins + manual version checks

11. Real‑world example: a compromised status line

A SaaS provider discovered an attacker had injected cat /etc/passwd into a status‑line command. Each keypress triggered the command, flooding the status bar and masking a new user account. The flicker was just a side effect of the malicious redraws.

Mitigation: run the status command through a wrapper that times out and validates the output.

set -g status-left '#(timeout 1s ~/.tmux/safe-status.sh)'

safe-status.sh should exit non‑zero if it sees unexpected data.


12. Quick action plan

  1. Verify terminal backend – disable GPU if needed.
  2. Set status-interval to 5 seconds.
  3. Ensure TERM is a standard value.
  4. Use a monospaced font.
  5. Cache any dynamic status content.
  6. Pin tmux and audit plugins.
  7. Log redraws to catch regressions early.

Follow this workflow and you’ll have a stable, auditable status bar that won’t distract from the real alerts you need to see.



See also