↓Skip to main content

TMUX-ing

·3 mins

Written with the help of LMStudio + Qwen3 4B Thinking 2507.

Ever since I started working back in 2023, I’ve struggled to understand how to manage multiple client platforms, different repositories, and now different AWS configurations. I knew people were using tmux, but it never stuck for me.

I recently started working on a new client, and the question arose again: how do I manage multiple clients while keeping my workflow clean? The solution was tmuxinator!

Why and Goals #

  • Never accidentally run a command against the wrong client’s AWS account.
  • One command to drop into a fully set up session for any client.
  • Copy/paste that just works (mouse selection → clipboard, no extra keys).
  • Each new client: copy a template, fill in a few values, and you’re done.

Folder structure #

First I had to clean my ~/Developer structure:

~/Developer/
├── clients/
│   ├── client-y/ ← git repos live here
│   │   ├── .aws/config     ← client-scoped AWS config (SSO session + profile)
│   │   └── .envrc          ← direnv: sets AWS_CONFIG_FILE / AWS_PROFILE
│   └── <other-clients>/
│       └── ... same pattern
├── personal/
├── tooling/
│   ├── scripts/
│   └── ansible/

This is obviously an example, but you get the idea.

Each client gets its own .aws/config and .envrc rather than one shared ~/.aws/config — keeps clients fully isolated, and nothing sensitive to that client leaks if the folder is ever archived or shared.

2. AWS access — aws-vault + SSO, scoped per client #

Install vault stuffs:

brew install aws-vault

(This pulls from the actively maintained ByteNess/aws-vault fork — the original 99designs/aws-vault is abandoned!!!! Do not use it! Homebrew already points to the maintained one.)

Per-client config - ~/Developer/clients/<client>/.aws/config:

[sso-session <client>-sso]
sso_start_url = https://<client-org>.awsapps.com/start
sso_region = eu-west-1
sso_registration_scopes = sso:account:access

[profile <client>]
sso_session = <client>-sso
sso_account_id = <account-id>
sso_role_name = <role-name>
region = eu-west-1
output = json

Per-client .envrc (direnv), in ~/Developer/clients/<client>/.envrc:

export AWS_CONFIG_FILE="$PWD/.aws/config"
export AWS_PROFILE=<client>

Run direnv allow once inside that folder after creating/editing it.

You might not have envrc installed, install it using brew:

brew install direnv

Prerequisite for direnv to work at all — confirm this line exists in ~/.zshrc (without it, .envrc files never load, silently):

eval "$(direnv hook zsh)"

Gitignore — if the client folder (or subfolders) is a git repo, add:

.aws/

3. tmuxinator — per-client session templates #

Each client gets a YAML file in ~/.tmuxinator/<client>.yml describing the windows/panes to open automatically.

Standard template (editor / git / aws / shell):

name: <client>
root: ~/Developer/clients/<client>/repos

windows:
  - editor:
      layout: main-vertical
      panes:
        - command1
        - command2
        - command3

Each command will open in its own window inside tmux, if you want to have an empty windows add this line:

- # This is an empty windows, you can use for whatever you want. 

One-off / task-specific templates are also fine — e.g. XXX-ansible-.yml uses a main-horizontal layout (one large pane on top, others in a row below) for a specific repo, with panes running watch "git status" and a diff check, no AWS window needed.

Start a session:

tmuxinator start <client>

5. Shell shortcuts — ~/.zshrc #

# Start (or attach to) a tmuxinator project by name
tm() {
  tmuxinator start "$@"
}

# Tab completion: `tm <TAB>` lists all tmuxinator project names
_tm() {
  local -a projects
  projects=(${(f)"$(tmuxinator list -n 2>/dev/null)"})
  compadd -a projects
}
compdef _tm tm

# Run a command with the current directory's AWS profile via aws-vault
awsx() {
  aws-vault exec "${AWS_PROFILE:?AWS_PROFILE not set}" -- "$@"
}

Usage: tm client-y, tm client-y, tab-completion works out of the box once ~/.zshrc is reloaded (source ~/.zshrc).