My Minimal tmux Config
If you spend any significant time in a terminal, you’ve probably used tmux.
I’m writing this post for a few reasons:
- People have asked for my config.
- I see too many people wasting their time in the morning, rebuilding their session from the previous day.
- I felt I should justify the configuration to myself rather than setting options ad-hoc.
tmux is often paired with a persistent connection. There are two popular choices: Eternal Terminal and Mosh. The goal is to close your laptop at the end of the day, open it the next morning, and have everything where it was so you can immediately get back into flow.
Note: There are other options. WezTerm has built-in multiplexing, for example.
macOS + iTerm2 + Eternal Terminal + tmux Control Mode
If you use macOS, iTerm2 has deep tmux integration in the form of tmux Control Mode.
tmux windows map to iTerm2 tabs. Native tab navigation and scrollback (both scrolling and find) work just as you’d expect.
tmux control mode does expect a reliable stream channel, so if you want a connection that persists even when network connections are dropped, Mosh will not work. You’ll need Eternal Terminal.
If you use a Mac, this is an excellent configuration. I worked on EdenFS and Watchman for almost five years this way.
mosh + tmux
But now I use Windows and Linux and can’t use iTerm2, so tmux within Mosh it is.
I find tmux’s default keybindings a little awkward, and the colors simultaneously harsh and too minimal, so I made a configuration to match my tastes.
You can download the full .tmux.conf here.
(You can go crazy, but avoiding too much fanciness was my goal. If you want all the bling, install the Tmux Plugin Manager and things like tmux-powerline.
.tmux.conf
First, a small demonstration.
Despite all of the work I put into my recent post about 24-bit color
in
terminals, I
do still use some with limited color support. macOS’s Terminal.app
only supports the 256-color palette, and the Linux console only really
supports 8. The following selects the correct tmux
terminfo entry.
# Detect the correct TERM value for new sessions.
# if-shell uses /bin/sh, so bashisms like [[ do not work.
if "[ $(tput colors) = 16777216 ]" {
set -g default-terminal "tmux-direct"
} {
if "[ $(tput colors) = 256 ]" {
set -g default-terminal "tmux-256color"
} {
set -g default-terminal "tmux"
}
}
I prefer Emacs keybindings in both bash (readline) and tmux.
setw -g mode-keys emacs
The next setting is more legacy terminal insanity. On some (most?)
terminals, programs cannot differentiate between a user pressing the
escape key and the beginning of an escape sequence. readline
and
tmux
default to 500 ms, which adds noticeable
latency in some
terminals when using programs like vi
.
There’s no correct value here. Ideally, your terminal would use an unambiguous code for the escape key, like MinTTY.
set -s escape-time 200
Let’s not be stingy with scrollback! Searching lots of history is worth spending megabytes.
# I can afford 50 MB of scrollback.
# Measured on WSL 1 with:
# yes $(python3 -c "print('y' * 80)")
set -g history-limit 100000
By default, if multiple clients connect to one tmux session, tmux will resize all of the windows to the smallest connected terminal.
This behavior is annoying, and it’s always an accident. Sometimes I’ll leave a temporary connection to a server from home and then another fullscreen connection from work will cram each window into 80x25.
The aggressive-resize
option applies this logic only to the
currently-viewed window, not everything in the session.
setw -g aggressive-resize on
Window titles don’t automatically forward to the whatever graphical terminal you’re using. Do that, and add the hostname, but keep it concise.
set -g set-titles on
set -g set-titles-string "#h: #W"
iTerm2 has this nice behavior where active tabs are visually marked so
you can see, at a glance, which had recent activity. The following two
options offer similar behavior. Setting activity-action
to none
disables any audible ding or visible flash, leaving just a subtle
indication in the status bar.
set -g monitor-activity on
set -g activity-action none
The following is perhaps the most important part of my configuration: tab management. Like browsers and iTerm2, I want my tabs numbered. I want a single (modified) keypress to select a tab, and I want tabs automatically renumbered as they’re created, destroyed, and reordered.
I also want iTerm2-style previous- and next-tab keybindings.
# Match window numbers to the order of the keys on a keyboard.
set -g base-index 1
setw -g pane-base-index 1
setw -g renumber-windows on
# My tmux muscle memory still wants C-b 0 to select the first window.
bind 0 select-window -t ":^"
# Other terminals and web browsers use 9 to focus the final tab.
bind 9 select-window -t ":$"
bind -n "M-0" select-window -t ":^"
bind -n "M-1" select-window -t ":1"
bind -n "M-2" select-window -t ":2"
bind -n "M-3" select-window -t ":3"
bind -n "M-4" select-window -t ":4"
bind -n "M-5" select-window -t ":5"
bind -n "M-6" select-window -t ":6"
bind -n "M-7" select-window -t ":7"
bind -n "M-8" select-window -t ":8"
# Browsers also select last tab with M-9.
bind -n "M-9" select-window -t ":$"
# Match iTerm2.
bind -n "M-{" previous-window
bind -n "M-}" next-window
Note that Emacs assigns meaning to Alt-number. If it matters to you, pick a different modifier.
Now let’s optimize the window ordering. By default, C-b c
creates a
new window at the end. That’s a fine default. But sometimes I want a
new window right after the current one, so define C-b C-c
. Also, add
some key bindings for sliding the current window around.
bind "C-c" new-window -a
bind "S-Left" {
swap-window -t -1
select-window -t -1
}
bind "S-Right" {
swap-window -t +1
select-window -t +1
}
I wanted “C-{“ and “C-}” but terminal key encoding doesn’t work like that.
Next, let’s define some additional key bindings for very common operations.
By default, searching in the scrollback requires entering “copy mode”
with C-b [
and then entering reverse search mode with C-r
.
Searching is common, so give it a dedicated C-b r
.
bind r {
copy-mode
command-prompt -i -p "(search up)" \
"send-keys -X search-backward-incremental '%%%'"
}
And some convenient toggles:
# Toggle terminal mouse support.
bind m set-option -g mouse \; display "Mouse: #{?mouse,ON,OFF}"
# Toggle status bar. Useful for fullscreen focus.
bind t set-option status
Now the status bar. The default status bar is okay, but we can do better.
- Move the tmux session ID next to the hostname on the right side.
- Move the current time to the far right corner.
- Keep the date, but I think I can remember what year it is.
- Ensure there is a single space between the windows and the left edge. Without a space at the edge, it looks weird.
The other half of that improvement is the color scheme. Instead of a harsh black-on-green, I chose a scheme that evokes old amber CRT phosphors or gas plasma displays My dad had a “laptop” with one of those when I was young.
The following color scheme mildly highlights the current window and uses a dark blue for the hostname-and-time section. These colors don’t distract me when I’m not working, but if I do look, the important information is there.
if "[ $(tput colors) -ge 256 ]" {
set -g status-left-style "fg=black bg=colour130"
set -g status-right-style "bg=colour17 fg=orange"
set -g status-style "fg=black bg=colour130"
set -g message-style "fg=black bg=colour172"
# Current window should be slightly brighter.
set -g window-status-current-style "fg=black bg=colour172"
# Windows with activity should be very subtly highlighted.
set -g window-status-activity-style "fg=colour17 bg=colour130"
set -g mode-style "fg=black bg=colour172"
}
And that’s it!
Again, feel free to copy the complete .tmux.conf.
Shell Integration
There’s one more config to mention: adding some shell aliases to .bashrc.
I sometimes want to look at or edit a file right next to my shell.
if [[ "$TMUX" ]]; then
function lv() {
tmux split-window -h less "$@"
}
function ev() {
tmux split-window -h emacs "$@"
}
function lh() {
tmux split-window -v less "$@"
}
function eh() {
tmux split-window -v emacs "$@"
}
fi
(You may notice the aliases use different meanings of horizontal and vertical than tmux. I don’t know, it feels like tmux is backwards, but that could be my brain.)
Happy multiplexing!