vmux
Appsvmux Daemon

Shell Integration

Wire vmux into your shell rcfiles, auto-attach on login, and use vmux state to power your prompt.

vmux is just a CLI, so shell integration is whatever you want it to be. This page collects useful patterns for zsh and bash. Adapt to fish, nushell, etc. by translating syntax.

Shorter aliases

A handful of one-letter aliases usually pays off:

# ~/.zshrc or ~/.bashrc
alias v='vmux'
alias va='vmux attach'
alias vls='vmux ls'
alias vps='vmux state | jq'                 # human-readable state dump

vmux already auto-resolves the daemon and the default session, so va is a complete "open my main session" command from any shell.

Auto-attach on login

If you want your default interactive shell to drop straight into vmux when you log in or open a new terminal window, gate it on a few interactivity checks.

zsh

# ~/.zshrc — top of file, before plugins
if [[ -o interactive ]] \
  && [[ -z "$VMUX_SESSION" ]] \
  && [[ -z "$INSIDE_EMACS" ]] \
  && [[ -z "$VSCODE_INJECTION" ]] \
  && [[ "$TERM_PROGRAM" != "vscode" ]]; then
    exec vmux attach -s default
fi

The VMUX_SESSION guard exists so panes the daemon spawns don't recursively re-enter vmux attach. Set the variable in server.environment of your config:

{
  "schemaVersion": 1,
  "server": {
    "environment": { "VMUX_SESSION": "1" }
  }
}

bash

# ~/.bashrc
case $- in
  *i*) ;;
  *) return ;;
esac
if [[ -z "$VMUX_SESSION" ]] && [[ -z "$INSIDE_EMACS" ]]; then
  exec vmux attach -s default
fi

exec replaces the login shell's process with vmux attach, so you don't end up with two layered shells.

Detecting vmux in your prompt

Shell rcfiles that need to behave differently inside a vmux pane can check the same environment variable used by the auto-attach guard:

if [[ -n "$VMUX_SESSION" ]]; then
  PROMPT="%F{cyan}vmux%f $PROMPT"
fi

If you only set the variable through server.environment, the daemon controls when it appears — convenient for distinguishing daemon-spawned shells from vmux pane launch shells you intentionally launched without the env override.

Status line

Power-line-style prompts can show the active session and pane count by calling vmux state once per prompt. Cache aggressively — vmux state is cheap (a single round trip over a Unix socket) but not free.

# Zsh prompt fragment, refreshed by precmd
__vmux_status() {
  vmux state 2>/dev/null \
    | jq -r 'select(.) | "\(.name) [\(.tabs | length) tabs · \(.tabs[0].panes | length) panes]"' \
    || true
}
RPROMPT='%F{8}$(__vmux_status)%f'

If you really want sub-prompt latency, use openObservationConnection via a small Swift helper rather than vmux state — but for most setups, one syscall per prompt is unmeasurable.

fzf-driven session picker

# Pick a session from the list and attach to it
vmuxp() {
  local choice
  choice=$(vmux ls | fzf --select-1 --exit-0 | cut -f1)
  if [[ -n "$choice" ]]; then
    vmux attach -s "$choice"
  fi
}

Bind it to a key in zsh:

zle -N vmuxp
bindkey '^G' vmuxp     # Ctrl+G

Per-pane environment

vmux always sets these inside spawned shell panes (in addition to whatever you put in server.environment):

VariableSet byNotes
TERMthe daemon's PTYFixed at xterm-256color for shell panes. Override per-pane via pane launch --env TERM=… if needed.
LINES, COLUMNSthe PTY at spawn timeUpdated automatically by the kernel when the daemon resizes the pane on SIGWINCH.
SHELLinherited from server.shellSame value used to spawn the process.
anything in server.environmentconfigWins over inherited values with the same name.

There is no built-in VMUX_PANE_ID or VMUX_SESSION_ID exposed inside the pane today. If you want one, set it explicitly with pane launch --env:

PANE=$(vmux pane create -s ci --role shell)
vmux pane launch "$PANE" --env "VMUX_PANE_ID=$PANE" -- "$SHELL"

Programmable workflows

The pane subcommands are designed to be scripted. A common pattern is "pre-build a session layout with two panes, one running a watcher, one running a shell":

#!/usr/bin/env bash
set -euo pipefail

vmux session create -s dev >/dev/null || true
SHELL_PANE=$(vmux state -s dev | jq -r '.tabs[0].panes[0].paneID')

# Split: add a watcher pane to the right
WATCH=$(vmux pane create -s dev --from "$SHELL_PANE" --axis vertical --title "watch")
vmux pane launch "$WATCH" --cwd "$PWD" -- npm run watch

# Drop in interactively on the shell pane
vmux attach -s dev

Sourcing this from ~/bin/dev-up and aliasing it to dev gives you a one-command project-startup workflow.

tmux co-existence

vmux and tmux have no shared state; you can run both side by side. A common setup is:

  • tmux outside, splitting your terminal into windows.
  • vmux attach inside a single tmux pane for cross-device session continuity.

vmux does not assume it owns the terminal — it puts the terminal in raw mode on attach and restores the original termios on detach, so handing control back and forth is clean.

You should not alias tmux to vmux. They are different multiplexers with different keybinds and prefix keys; aliasing only causes confusion.

SSH agent forwarding inside vmux panes

Panes inherit the daemon's environment. Make sure vmuxd was started with the right SSH_AUTH_SOCK — for example, when vmuxAgent is in use:

# In your login shell, before vmux starts the daemon
export SSH_AUTH_SOCK="$HOME/.ssh/vmux-agent.sock"

Then any pane that runs ssh, git, or scp will use vmuxAgent for signing — your iPhone holds the private key.

If you launch the daemon via launchd, set the variable in the plist's EnvironmentVariables block instead.

See also

  • vmux attach — how the client takes over your terminal.
  • Configuration file — the right place to set server.environment.
  • vmuxAgent — companion SSH agent that pairs especially well with daemon-launched shells.