Tmux Sessionizer Workflow
This is the terminal workflow I use daily for development. One launcher gives me quick access to:
- local project directories
- SSH machines
- network mounts
From there, I jump straight into Neovim and OpenCode in the right context.
Why this makes terminal navigation easy
Instead of manually typing cd, ssh, and mount commands all day, I hit one keybinding, fuzzy-find the target, and tmux switches me into the right session.
- Pick a local folder -> tmux session opens in that directory
- Pick an SSH host -> tmux creates/switches to a remote session
- Pick a mount -> it mounts (if needed), then opens that location
This keeps context switching fast while coding, editing, and hopping between local and remote machines.
1) Make the script callable
Save the script as:
~/.local/bin/tmux-sessionizer
Make it executable:
chmod +x ~/.local/bin/tmux-sessionizer
2) Terminal keybinding (Alt+f)
In Bash, add this to ~/.bashrc:
bind -x '"\ef":"~/.local/bin/tmux-sessionizer"'
Reload shell config:
source ~/.bashrc
Now Alt+f launches the sessionizer directly from your terminal.
3) tmux config is important
To trigger sessionizer from inside tmux (including SSH sessions), add this to ~/.tmux.conf:
bind-key -n C-f run-shell "tmux neww ~/.local/bin/tmux-sessionizer"
Reload tmux config:
tmux source-file ~/.tmux.conf
With this in place, the launcher is always available whether you are in local panes or remote shells.
Script (IP addresses redacted)
#!/usr/bin/env bash
# Define SSH servers (embedded directly in script)
declare -A ssh_servers=(
["Karto-5"]="ssh -A -J azureuser@<jump-host-ip> zac@localhost -p 35725"
["Karto-7"]="ssh -A -J azureuser@<jump-host-ip> zac@localhost -p 35727"
["Karto-8"]="ssh -A -J azureuser@<jump-host-ip> zac@localhost -p 35728"
["Readar-DB"]="ssh -L 5432:localhost:5432 zac@<db-host-ip>"
["Transip-Webportal"]="ssh zac@<web-host-ip>"
["Vultr"]="ssh root@<vps-host-ip>"
["Truenas"]="ssh truenas_admin@<truenas-lan-ip>"
["RPi-5"]="ssh zac@<rpi-lan-ip>"
)
# Define network mounts
declare -A network_mounts=(
["Dropbox"]="/mnt/dropbox|rclone mount \"Readar_Dropbox:/\" /mnt/dropbox --daemon --vfs-cache-mode writes"
["NAS"]="/mnt/NAS|sshfs karto@<nas-lan-ip>:/NAS /mnt/NAS -o reconnect,ServerAliveInterval=15,ServerAliveCountMax=3,ConnectTimeout=10"
["NAS2"]="/mnt/NAS2|sshfs karto@<nas-lan-ip>:/NAS2 /mnt/NAS2 -o reconnect,ServerAliveInterval=15,ServerAliveCountMax=3,ConnectTimeout=10"
)
if [[ $# -eq 1 ]]; then
selected=$1
else
fifo=$(mktemp -u /tmp/tmux_dirs.XXXXXX)
mkfifo "$fifo"
# Get SSH servers with [SSH] prefix
ssh_list=""
for server in "${!ssh_servers[@]}"; do
ssh_list+="[SSH] $server"$'\n'
done
# Get network mounts with [MOUNT] prefix
mount_list=""
for mount in "${!network_mounts[@]}"; do
mount_list+="[MOUNT] $mount"$'\n'
done
# Run find in background, append SSH and mount lists
(echo -e "$ssh_list$mount_list"; find ~/Documents/3DPrinting/3DPrinting ~/Documents/Work/ ~/.config/ ~/.local/ ~/.cache/ ~/.local/src/ ~/.local/share/ ~/ ~/Documents/ /mnt/ -mindepth 1 -maxdepth 2 -type d 2>/dev/null) > "$fifo" &
# Read from fifo with fzf
selected=$(fzf < "$fifo")
# Clean up
rm "$fifo"
fi
if [[ -z $selected ]]; then
exit 0
fi
# Check if this is an SSH selection
if [[ $selected == \[SSH\]* ]]; then
# Extract server name (remove "[SSH] " prefix)
server_name="${selected#\[SSH\] }"
selected_name=$(echo "$server_name" | tr . _)
ssh_command="${ssh_servers[$server_name]}"
tmux_running=$(pgrep tmux)
# Create or switch to SSH session
if [[ -z $TMUX ]] && [[ -z $tmux_running ]]; then
tmux new-session -s "$selected_name" "$ssh_command"
exit 0
fi
if ! tmux has-session -t="$selected_name" 2> /dev/null; then
tmux new-session -ds "$selected_name" "$ssh_command"
fi
# Switch to the session
if [[ -z $TMUX ]]; then
tmux attach-session -t "$selected_name"
else
tmux switch-client -t "$selected_name"
fi
# Check if this is a mount selection
elif [[ $selected == \[MOUNT\]* ]]; then
# Extract mount name (remove "[MOUNT] " prefix)
mount_name="${selected#\[MOUNT\] }"
mount_info="${network_mounts[$mount_name]}"
# Split mount info into path and command
mount_path="${mount_info%%|*}"
mount_command="${mount_info#*|}"
# Mount if directory is empty
if [ -z "$(ls -A "$mount_path" 2>/dev/null)" ]; then
eval "$mount_command"
fi
# Set selected to the mount path and continue with directory logic
selected="$mount_path"
fi
# Directory selection (original behavior)
if [[ $selected != \[SSH\]* ]]; then
selected_name=$(basename "$selected" | tr . _)
tmux_running=$(pgrep tmux)
if [[ -z $TMUX ]] && [[ -z $tmux_running ]]; then
tmux new-session -s "$selected_name" -c "$selected"
exit 0
fi
if ! tmux has-session -t="$selected_name" 2> /dev/null; then
tmux new-session -ds "$selected_name" -c "$selected"
fi
# Switch to the session
if [[ -z $TMUX ]]; then
tmux attach-session -t "$selected_name"
else
tmux switch-client -t "$selected_name"
fi
fi
Development flow
Typical loop:
Alt+f-> pick project folder- Open Neovim in that tmux session
- Use OpenCode from the same working context
- Jump to SSH host or mounted storage when needed
- Return to local sessions instantly with tmux switching
It is a simple setup, but it removes a lot of friction from daily development.