Tmux + Ghostty: Build a Terminal Workflow You Actually Want to Use

A layered guide to tmux and Ghostty — from first install to floating popups, vim navigation, and a polished dark theme.

HomeLab

You hit a keybinding and a floating lazygit window appears over your work. You review a diff, commit, and dismiss it — back to your editor in under a second. Another keybinding: lazydocker pops up, shows your running containers, and disappears when you are done. You move between vim and terminal panes with Ctrl+hjkl without thinking about which program owns the cursor. Your terminal remembers your layout across SSH disconnects, power failures, and reboots.

This is not a custom IDE. It is tmux running inside Ghostty, configured with about 230 lines of dotfiles.

This post walks through my tmux configuration from the ground up. If you have never used tmux, start with the essentials. If you already know the basics, skip ahead to the floating popups and vim integration. Everything here is in my dotfiles repo — you can copy what you need and ignore the rest.

TL;DR

  • Tmux is a terminal multiplexer — it lets you split your terminal into panes and windows, and keeps sessions alive when you disconnect.
  • Ghostty is a fast, modern terminal emulator that pairs well with tmux through extended keyboard protocols and image support.
  • The config is layered: start with five essential settings that make tmux usable, then add power features like floating popups and vim navigation as you get comfortable.
  • Floating popup windows for lazygit and lazydocker let you access tools without leaving your workspace.

Prerequisites

  • OS: macOS or Linux
  • Package manager: Homebrew (macOS) or your distro’s package manager
  • Terminal: Ghostty (recommended) or any terminal with true color support
  • Skill level: Comfortable using a terminal for basic commands. No tmux experience needed.

What Is Tmux

If you have only ever used one terminal window at a time, tmux is the biggest productivity upgrade you have not tried yet.

Tmux is a terminal multiplexer. Think of it like virtual desktops, but inside your terminal. It gives you three things:

Sessions. A session is a workspace. You might have one session for a web project and another for infrastructure work. Sessions persist even if you close your terminal or lose your SSH connection. Reconnect, reattach, and everything is exactly where you left it.

Windows. Inside a session, windows work like tabs in a browser. Each window is a full terminal screen. You switch between them with a keybinding.

Panes. A window can be split into panes — side-by-side or stacked. Run your editor in one pane and your tests in another, both visible at the same time.

The key concept: tmux runs as a server process that outlives your terminal. Your terminal connects to tmux as a client. Close the client, and the server keeps running. This is why sessions survive disconnects — the server never stopped.

What Is Ghostty

Ghostty is a GPU-accelerated terminal emulator. It is fast, renders text smoothly, and supports modern protocols that older terminals do not.

For tmux users, three Ghostty features matter:

Extended keyboard protocol. Ghostty speaks the CSI-u and kitty keyboard protocol. Without tmux being told about this, Ghostty’s special key sequences get printed as garbage text in your terminal. The tmux config tells tmux to expect these sequences, so keybindings work correctly.

Image passthrough. Ghostty supports sixel and kitty graphics protocols. The tmux config enables allow-passthrough, which lets programs inside tmux display images through Ghostty. Without this, image-rendering tools show nothing.

True color. Ghostty renders the full RGB color spectrum. The tmux config enables 256-color and RGB overrides so your theme colors look correct inside tmux — not the washed-out approximations you get with older terminals.

You can install Ghostty from ghostty.org. It runs on macOS and Linux.

The Essentials

These are the settings that make tmux worth using on day one. Each one fixes a specific annoyance that causes new users to give up.

A Better Prefix Key

Every tmux command starts with a prefix key — you press the prefix, then the command key. The default prefix is Ctrl-b, which is awkward to reach and conflicts with common shortcuts.

This config uses Ctrl-Space:

unbind C-b
set -g prefix C-Space
set -g prefix2 C-@
bind C-Space send-prefix
bind C-@ send-prefix

Ctrl-Space is easy to hit without looking, and it does not conflict with vim, bash, or most other tools. The C-@ line is a fallback — some terminals report Ctrl-Space as a NUL character, and this catches that case.

Zero Escape Delay

By default, tmux waits 500 milliseconds after you press Escape before passing it through. This is meant to detect escape sequences, but it makes vim feel laggy — you press Escape to leave insert mode and nothing happens for half a second.

set -sg escape-time 0

This eliminates the delay entirely. If you use vim or any modal editor inside tmux, this one line is mandatory.

Start Counting at 1

Tmux numbers windows and panes starting at 0. On a keyboard, 0 is far from 1-9, which means your first window is the hardest to reach.

set -g base-index 1
setw -g pane-base-index 1
set -g renumber-windows on

Now your first window is prefix 1, right above the home row. renumber-windows keeps the numbering sequential when you close a window in the middle — no gaps.

Mouse Support

Tmux supports the mouse for selecting panes, resizing, and scrolling. Beginners expect this, and there is no reason to disable it:

set -g mouse on

You can still use keyboard shortcuts for everything. The mouse is a safety net while you learn the keybindings.

Intuitive Splits

The default split bindings (prefix " and prefix %) are impossible to remember. This config replaces them with vim-style mnemonics:

bind v split-window -h -c "#{pane_current_path}"
bind s split-window -v -c "#{pane_current_path}"
bind | split-window -h -c "#{pane_current_path}"
bind - split-window -v -c "#{pane_current_path}"

prefix v splits vertically (a new pane appears to the right). prefix s splits horizontally (a new pane appears below). The -c "#{pane_current_path}" flag means the new pane opens in the same directory you are already in, not your home directory.

Reload Without Restarting

While you are tweaking your config, you need a fast way to reload it:

bind r source-file ~/.tmux.conf \; display-message "Config reloaded"

prefix r reloads the config and shows a confirmation. No need to kill and restart tmux every time you change a setting.

Level Up: Power Features

Once the essentials feel natural, these features are what make the workflow fast.

Floating Popup Windows

This is the feature that makes people stop and ask “how did you do that.” Tmux can open floating popup windows — temporary overlays that appear on top of your panes and disappear when you are done.

The config uses the tmux-floax plugin for a general-purpose floating terminal:

set -g @plugin 'omerxx/tmux-floax'
set -g @floax-bind 't'
set -g @floax-bind-menu 'T'
set -g @floax-width '88%'
set -g @floax-height '76%'
set -g @floax-border-color '#ff6f00'
set -g @floax-text-color '#c8d1dc'
set -g @floax-session-name 'popup-shell'
set -g @floax-title ' floax '

prefix t toggles a floating terminal. It appears centered over your workspace with a rounded orange border. Run a quick command, dismiss it with prefix t again, and you are back to your layout. The floating terminal has its own session, so it persists between toggles — your command history and working directory are preserved.

But the real power is tool-specific popups. These keybindings open lazygit and lazydocker in floating windows:

bind g if-shell "command -v lazygit >/dev/null 2>&1" \
  "display-popup -E -x C -y C -w 88% -h 76% -T ' lazygit ' -d '#{pane_current_path}' 'lazygit'" \
  "display-message 'lazygit not found in PATH'"

bind D if-shell "command -v lazydocker >/dev/null 2>&1" \
  "display-popup -E -x C -y C -w 88% -h 76% -T ' lazydocker ' -d '#{pane_current_path}' 'lazydocker'" \
  "display-message 'lazydocker not found in PATH'"

prefix g opens lazygit in a floating popup. You can stage files, write commits, push — all without leaving your editor pane. Close lazygit and the popup disappears. prefix D does the same for lazydocker, giving you a full container management TUI in a floating overlay.

The if-shell wrapper checks whether the tool is installed before opening the popup. If it is not, you get a status message instead of an error.

The pattern is reusable. Any TUI tool can become a floating popup:

bind <key> display-popup -E -x C -y C -w 88% -h 76% -T ' <title> ' -d '#{pane_current_path}' '<command>'

Replace <key>, <title>, and <command> with whatever you want. -E closes the popup when the command exits. -x C -y C centers it. -w and -h set the size as a percentage of your terminal.

There is also a scrollback pager popup — prefix p opens your terminal scrollback in neovim so you can search and copy from long output:

bind p run-shell -b "tmux display-popup -E -x C -y C -w 88% -h 76% -T ' pager ' ~/.local/bin/tmux-pager '#{pane_id}'"

Seamless Vim-Tmux Navigation

If you use vim or neovim, this plugin eliminates the mental overhead of “am I in a vim split or a tmux pane?”

set -g @plugin 'christoomey/vim-tmux-navigator'

With vim-tmux-navigator installed in both tmux and vim, Ctrl+h/j/k/l moves between panes regardless of whether they belong to vim or tmux. Move left from a vim split into a tmux pane and back — the same keybinding, no prefix required.

The config also includes fallback navigation with the prefix key:

bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R

Pane Resizing

Two levels of precision for resizing panes:

# Shift+hjkl = 5-cell increments (coarse)
bind -r H resize-pane -L 5
bind -r J resize-pane -D 5
bind -r K resize-pane -U 5
bind -r L resize-pane -R 5

# Ctrl+Arrow = 1-cell increments (fine)
bind -r C-Left resize-pane -L 1
bind -r C-Down resize-pane -D 1
bind -r C-Up resize-pane -U 1
bind -r C-Right resize-pane -R 1

The -r flag makes these repeatable — press the prefix once, then tap the resize key multiple times without re-pressing the prefix. prefix H H H resizes left by 15 cells.

Synchronize Panes

This one is for anyone managing multiple servers. Toggle synchronized input across all panes in a window:

bind C-a setw synchronize-panes \; display-message "Sync: #{?pane_synchronized,ON,OFF}"

prefix Ctrl-a turns sync on. Now everything you type appears in every pane simultaneously. Split your window into four panes, SSH into four servers, and run the same command on all of them at once. Press prefix Ctrl-a again to turn it off.

Vim-Style Copy Mode

Tmux has a copy mode for scrolling through output and copying text. This config makes it work like vim:

setw -g mode-keys vi

bind -T copy-mode-vi v send-keys -X begin-selection
bind -T copy-mode-vi V send-keys -X select-line
bind -T copy-mode-vi C-v send-keys -X rectangle-toggle
bind -T copy-mode-vi Escape send-keys -X cancel
bind -T copy-mode-vi C-u send-keys -X halfpage-up
bind -T copy-mode-vi C-d send-keys -X halfpage-down

Enter copy mode with prefix [, then use v to start a selection, V for line selection, or Ctrl-v for rectangle selection — the same keys as vim. The tmux-yank plugin handles copying to the system clipboard.

Mouse scrolling is also tuned down to 1 line per wheel tick instead of the default 5, which makes scrolling through output much smoother:

bind -T copy-mode-vi WheelUpPane   send-keys -X -N 1 scroll-up
bind -T copy-mode-vi WheelDownPane send-keys -X -N 1 scroll-down

Making It Look Good

The default tmux appearance is functional but ugly. A few settings make it something you do not mind staring at all day.

Status Bar

The status bar sits at the top and shows the session name, window list, and date:

set -g status-position top
set -g status-style "bg=default,fg=#545b66"
set -g status-format[0] '#[align=left,fg=#c8d1dc] #S  #{W:#{?window_active,#[fg=#c8d1dc] ▸#W ,#[fg=#545b66]  #W }  }#[align=centre,fg=#545b66]%a %b %d  %H:%M'

The active window gets a marker and brighter text. Inactive windows fade into the background. The date and time sit centered. bg=default means the status bar is transparent — it takes on the terminal’s background color, so it blends with Ghostty’s theme.

Pane Borders and Colors

set -g pane-border-style "fg=#3a4148"
set -g pane-active-border-style "fg=#ff6f00"

Inactive pane borders are dark gray, almost invisible. The active pane border is bright orange — you always know which pane has focus without looking at the cursor. The same orange carries through to popup borders and the floax accent color, keeping the visual language consistent.

Messages

set -g message-style "bg=#1f2428,fg=#c8d1dc"

Status messages use a dark background with light text. Subtle, readable, and consistent with the overall dark theme.

Security Considerations

A few settings in this config have security implications worth knowing about.

  • allow-passthrough lets programs inside tmux send escape sequences directly to your terminal. This is needed for image rendering, but it means a program running inside tmux (including over SSH) could send arbitrary escape sequences to Ghostty. If you SSH into untrusted hosts through tmux, consider setting allow-passthrough off and only enabling it in sessions where you need image support.
  • Synchronize panes sends your keystrokes to every pane in a window simultaneously. If you forget sync is on and switch to a pane with a different context — say, a production server next to a dev server — you will run the same command in both places. The status message (“Sync: ON/OFF”) helps, but build a habit of checking before typing commands with side effects.
  • Clipboard access via tmux-yank copies selected text to your system clipboard. In shared or recorded terminal sessions, be aware that clipboard operations may be visible to screen-sharing tools or session recorders.

None of these are reasons to avoid the features — just things to be aware of so you can use them intentionally.

Troubleshooting

  • Ctrl-Space does not work as prefix: On macOS, Spotlight or an input method may be capturing Ctrl-Space. Check System Settings > Keyboard > Shortcuts > Input Sources and disable “Select the previous input source” if it conflicts. On Linux, check your IBus or Fcitx input method settings.
  • Garbage characters after keybindings: Your terminal is sending extended key sequences that tmux does not expect. Make sure your tmux version is 3.3a or later (tmux -V) and that the extended-keys setting is enabled in your config.
  • TPM plugins not installing: After pressing prefix I, if nothing happens, verify that TPM is installed at ~/.tmux/plugins/tpm. If the directory does not exist, clone it: git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm
  • Colors look wrong inside tmux: Verify your TERM is set correctly. Outside tmux, echo $TERM should show xterm-ghostty or xterm-256color. Inside tmux, it should show tmux-256color or screen-256color. The config sets default-terminal to handle this, but a conflicting shell profile can override it.
  • Slow response after pressing Escape in vim: Make sure escape-time is set to 0. Run tmux show -g escape-time to verify. If it shows anything other than 0, your config may not be loading — check the file path and try prefix r to reload.

Getting Started

Here is how to go from zero to a working tmux + Ghostty setup.

1. Install Ghostty. Download from ghostty.org. On macOS, it is a standard .dmg install.

2. Install tmux. On macOS with Homebrew:

brew install tmux

3. Install the tools (optional but recommended):

brew install lazygit lazydocker neovim

4. Copy the config. Save my .tmux.conf to ~/.tmux.conf. The full config and all related dotfiles are in the dotfiles repo.

5. Start tmux and install plugins. Open Ghostty, type tmux, then press prefix I (that is Ctrl-Space, then Shift-i). TPM — the tmux plugin manager — will auto-bootstrap itself and install the plugins. You will see a message when it is done.

6. Reload. Press prefix r to reload the config. You should see the orange-accented status bar and the new keybindings.

Start with the essentials — the prefix key, splits, and navigation. Use prefix t to try the floating terminal. When you are ready, try prefix g for lazygit. Add features as they become useful, not all at once.

What’s Next

This is the first post in a dotfiles series. Tmux is the foundation — it manages your workspace — but the terminal emulator and editor configs complete the picture. Next up is a walkthrough of my Ghostty configuration: font rendering, keybindings, theme setup, and how it integrates with the tmux config covered here.

What does your terminal workflow look like? Are you using tmux, or something else entirely? Drop a comment — I would like to hear what tools and keybindings you rely on.