Eren's Tmux Configuration

Eren's Tmux Configuration
GitHub

Building a Tmux Status Bar That Actually Works

I spent way too long trying to get a good looking tmux status bar. Not “it kind of works” good looking, but the powerline segmented bar you see in screenshots on r/unixporn, with system stats, proper icons, and colors that don’t fall apart the second you reload your config. Along the way I ran into three separate problems that are barely documented anywhere. So here’s everything I learned.

What We’re Building

The final result is a powerline style status bar with colored segments that flow into each other using arrow separators. On the left you get your session name in a blue block. Your window tabs sit in the middle as individual segments, with the active one popping in cyan. On the right, four segments show CPU load, RAM usage, the date, and the time, each with its own icon and background color.

It looks clean. It looks modern. And every color actually renders.

The Three Traps Everyone Falls Into

Trap 1: Variables Don’t Work the Way You Think

The first thing you’ll try is defining color variables so you don’t have to repeat hex codes everywhere. Tmux gives you %hidden for this, and it looks perfect:

%hidden BLUE="#8898b4"
set -g status-style "fg=#{BLUE}"

This does not work. The %hidden keyword creates server environment variables, but #{BLUE} in a format string looks up tmux format variables (things like session_name or window_index). Your custom variable doesn’t exist in that namespace, so it resolves to an empty string. Your bar renders with no colors and you have no idea why.

The next thing you’ll try is user options with the @ prefix:

set -g @BLUE "#8898b4"
set -g status-left "#[fg=#{@BLUE}] hello"

This is closer. The #{@BLUE} format does resolve correctly in some contexts. If you test it with tmux display-message -p "#{@BLUE}" it prints the color value just fine. But it fails inside #[...] style directives. Tmux parses the style block before it expands format variables, so fg=#{@BLUE} becomes a literal string and your terminal has no idea what to do with it.

The fix is boring but reliable: hardcode every color. Put a comment block at the top of your theme section as a reference palette, and use the raw hex values everywhere. It’s ugly in the config file but it’s the only approach that actually works.

# Palette reference:
#   bg=#1a1816  bg_light=#2e2a26  bg_hl=#3e3a36
#   fg=#ddd5c8  fg_dim=#c4bcb0
#   blue=#8898b4  cyan=#78a8a0  green=#88a878

set -g status-style "fg=#c4bcb0,bg=#1a1816"

Trap 2: Your Icons Are Probably Missing

If you’re copying config snippets from dotfile repos or blog posts, there’s a real chance the Nerd Font icon characters are silently getting stripped. This happens with certain text editors, clipboard managers, and even some file transfer tools. The config loads fine, tmux doesn’t complain, but your powerline arrows and icons just aren’t there. You see blank spaces where the arrows should be.

The way to catch this is to hex dump the relevant lines:

sed -n '119p' ~/.tmux.conf | od -A x -t x1z

If you see nothing but 20 (space) bytes where your icons should be, the characters got eaten. You’ll need to write them back using a tool that handles Unicode properly. Python works great for this since you can use explicit codepoints:

ARROW_R = '\ue0b0'   # Powerline right arrow
ARROW_L = '\ue0b2'   # Powerline left arrow
TERMINAL = '\uf120'  # Terminal icon

Trap 3: Not All Nerd Font Icons Are Equal

Even with a Nerd Font installed, some icon ranges don’t render in every font variant. The safest icons are from the Font Awesome range (U+F000 through U+F2E0). These are included in every Nerd Font build. Icons from the Material Design range or the newer extensions are hit or miss depending on your font and version.

Stick to these and you’ll be fine:

Icon Codepoint Description
  U+F120 Terminal
  U+F2DB Microchip / CPU
  U+F1C0 Database / RAM
  U+F073 Calendar
  U+F017 Clock
  U+F0E7 Bolt / prefix

Prerequisites

Before you start, you need two things.

A Nerd Font. Download one from nerdfonts.com and set it as your terminal’s font. I use UbuntuMono Nerd Font Mono. JetBrainsMono, FiraCode, and Hack are also popular choices. Make sure you pick the “Mono” variant for terminal use.

Tmux 3.2 or newer. You can check with tmux -V. Most of this config works on older versions too, but a few features like popup-border-lines need 3.2+.

The Status Bar Config

Here’s the full status bar section. Every color is hardcoded, every icon is from the safe Font Awesome range, and the powerline arrows create proper segmented transitions between each block.

# ── Status Bar ───────────────────────────────────────────────
set -g status on
set -g status-position bottom
set -g status-justify left
set -g status-style "fg=#c4bcb0,bg=#1a1816"

# Left: session name in a blue powerline block
set -g status-left-length 40
set -g status-left "#[fg=#1a1816,bg=#8898b4,bold]  #S #[fg=#8898b4,bg=#1a1816] "

The left side is straightforward. Dark text on a blue background for the session name, with a powerline right arrow () transitioning back to the base dark background.

# Window tabs: inactive
setw -g window-status-format "#[fg=#2e2a26,bg=#1a1816]#[fg=#706860,bg=#2e2a26] #I #W #[fg=#2e2a26,bg=#1a1816]"

# Window tabs: active
setw -g window-status-current-format "#[fg=#78a8a0,bg=#1a1816]#[fg=#1a1816,bg=#78a8a0,bold] #I #W #[fg=#78a8a0,bg=#1a1816]"

setw -g window-status-separator " "

Each window tab is a self contained powerline segment. Inactive tabs get a dark, muted look. The active tab flips to a solid cyan background with dark text so it pops immediately. The left arrow () caps the left edge and the right arrow () caps the right edge, giving each tab that pointed segment shape.

# Right: powerline segments for load, ram, date, time
set -g status-right-length 120
set -g status-right "#{?client_prefix,#[fg=#e0ba78,bg=#1a1816]#[fg=#1a1816,bg=#e0ba78,bold]  #[fg=#e0ba78,bg=#1a1816] ,}#[fg=#2e2a26,bg=#1a1816]#[fg=#88a878,bg=#2e2a26]  #(cat /proc/loadavg | cut -d' ' -f1) #[fg=#3e3a36,bg=#2e2a26]#[fg=#78a8a0,bg=#3e3a36]  #(free -h | awk '/Mem/{print $3\"/\"$2}') #[fg=#2e2a26,bg=#3e3a36]#[fg=#c4bcb0,bg=#2e2a26]  %b %d #[fg=#8898b4,bg=#2e2a26]#[fg=#1a1816,bg=#8898b4,bold]  %H:%M "

The right side is where it gets fun. Four segments flow into each other using left arrows () as transitions. The backgrounds alternate between two dark shades to create visual separation, and the final time segment uses the same blue as the session pill on the left, bookending the whole bar.

The system stats use simple shell commands inside #():

  • cat /proc/loadavg | cut -d' ' -f1 grabs the 1 minute load average
  • free -h | awk '/Mem/{print $3"/"$2}' shows used and total RAM

These update on every status-interval tick, which is set to 5 seconds in my config.

There’s also a conditional prefix indicator. When you press your prefix key, an orange bolt segment appears on the right. When you release it, the segment disappears. This is handled by the #{?client_prefix,...} conditional at the start of the status right string.

How Powerline Arrows Work

If the format strings look like hieroglyphics, here’s what’s going on. There are two special characters doing all the visual work:

The right arrow (U+E0B0) creates a triangle pointing right. You place it at the end of a segment. Set its foreground to the segment’s background color and its background to whatever comes next. This carves the right edge of the segment into a point.

The left arrow (U+E0B2) creates a triangle pointing left. You place it at the start of a segment. Same idea: foreground is the segment’s color, background is whatever was before it.

So a single floating segment looks like this:

#[fg=SEGMENT,bg=BASE]  #[fg=TEXT,bg=SEGMENT] content #[fg=SEGMENT,bg=BASE]
                ^^^^                                                  ^^^^
            left entry                                           right exit

And when two segments flow into each other, the arrow between them has the outgoing segment as its background and the incoming segment as its foreground:

#[fg=TEXT,bg=SEG_A] content #[fg=SEG_B,bg=SEG_A]  #[fg=TEXT,bg=SEG_B] content
                                                ^^^^
                                        transition arrow

Once you see the pattern, reading and modifying these strings becomes much easier.

The Full Color Palette

I built this around a warm, muted dark theme. Here’s the palette if you want to tweak it or adapt it to your own colors:

Role Hex Usage
Background #1a1816 Status bar base, pane bg
Background light #2e2a26 Inactive segments
Background highlight #3e3a36 Alternate segments
Foreground #ddd5c8 Primary text
Foreground dim #c4bcb0 Secondary text
Muted #706860 Inactive window text
Blue #8898b4 Session pill, time pill, borders
Cyan #78a8a0 Active window, RAM stat
Green #88a878 CPU/load stat
Orange #e0ba78 Prefix indicator
Yellow #d4a85c Activity alerts

To adapt this to your own theme, replace the hex values in the status bar lines. Just remember: hardcode everything, don’t try to use variables.

Wrapping Up

The tmux status bar is one of those things that looks simple until you actually try to build one from scratch. The variable system is a trap, the Unicode handling is fragile, and the format string syntax takes some getting used to. But once you get past those bumps, you end up with something that’s genuinely useful. I glance at my status bar dozens of times a day now and it tells me exactly what I need to know: which session I’m in, what my windows are, and whether my machine is under load. All without opening htop.

If something isn’t rendering right, check the hex dump first. Nine times out of ten, the characters just aren’t in the file.