Do you normally end up with around 15, 20 or more tmux sessions that you manually have to clean up? In this article I show you how I automatically clean up the tmux sessions I haven't used using a bash script, this means that you can run the script in macOS and also in Linux.
I use 1 tmux session per project. What is a project? It is each one of my GitHub repos
What this means is that with a single keymap I can switch between my different projects, or example, hyper+t+j takes me to my dotfiles, if I type hyper+t+u it takes me to my notes
Why do I have a project per tmux session? Because I can quickly switch to any of them, immediately open Neovim, start working on that project, and push my changes to GitHub when I’m done
The problem is that after a few days or weeks, I end up with around 15, or 20 tmux sessions. So I have to manually close them
To address this, I created a script that automatically kills the tmux sessions that haven’t been accessed in a certain amount of time
To be honest, I didn’t “create” the script, I modified a script that I found here
This script was created by the dhulihan GitHub user
Output of the script
Below you can see an example of the script in action
1
2
3
4
5
6
7
8
9
❯❯❯❯ cat /tmp/tmuxKillSessions.out
2025-03-12 23:53:21 - Killed session: dotfiles_latest-j (Inactive for 199min)
2025-03-12 23:53:21 - Killed session: karabiner_rules (Inactive for 199min)
2025-03-12 23:53:21 - Killed session: linkarzu-h (Inactive for 199min)
2025-03-12 23:53:21 - Killed session: obsidian_main-u (Inactive for 199min)
2025-03-13 09:04:59 - Killed session: karabiner_rules (Inactive for 204min)
2025-03-13 11:31:34 - Killed session: scripts_public- (Inactive for 224min)
2025-03-13 14:05:16 - Killed session: dotfiles_latest-j (Inactive for 176min)
2025-03-13 14:05:16 - Killed session: obsidian_main-u (Inactive for 132min)
Notice above that it has killed the sessions that have not been accessed for the amount of time specified in parentheses, I.E. (Inactive for 132min)
Notice that this file is stored in the tmp directory, which means it will be automatically deleted after a reboot
Script used
The script can be found below, just keep in mind that this may change in the future if I decide to update it, so to find the latest version, you can go to my dotfiles
#!/usr/bin/env bash# I (linkarzu) did not come up with this script, It's a slightly modified# version of https://gist.github.com/dhulihan/4c65e868851660fb0d8bfa2d059e7967# by github user dhulihan# If I do not add this, the script will not find tmux or any other apps in# the /opt/homebrew/bin dir. So it will not run the tmux ls commandexport PATH="/opt/homebrew/bin:$PATH"# I make this slightly lower than the LaunchAgent intervalTOO_OLD_THRESHOLD_MIN=110
TMUX_LOG_PATH="/tmp/tmuxKillSessions.log"NOW=$(($(date +%s)))
tmux ls-F'#{session_name} #{session_activity}' | while read-r LINE;do
SESSION_NAME=$(echo$LINE | awk'{print $1}')LAST_ACTIVITY=$(echo$LINE | awk'{print $2}')LAST_ACTIVITY_MINS_ELAPSED=$(((NOW - LAST_ACTIVITY)/60))# # print all sessions# echo "${SESSION_NAME} is ${LAST_ACTIVITY_MINS_ELAPSED}min"if[["$LAST_ACTIVITY_MINS_ELAPSED"-gt"$TOO_OLD_THRESHOLD_MIN"]];then
TIMESTAMP=$(date'+%Y-%m-%d %H:%M:%S')echo"$TIMESTAMP - Killed session: $SESSION_NAME (Inactive for ${LAST_ACTIVITY_MINS_ELAPSED}min)" | tee-a$TMUX_LOG_PATH
tmux kill-session -t${SESSION_NAME}# In case you want to test the script without killing sessions, comment the 2 lines above and uncomment below# echo "${SESSION_NAME} is ${LAST_ACTIVITY_MINS_ELAPSED}min inactive and would be killed."fi
done
Notice that the command that does the trick is this one
It shows you the name of the session, and it’s last activity time, then we just compare if the last activity is higher than our threshold TOO_OLD_THRESHOLD_MIN (notice that mine is set to 110 minutes) we kill the session
You could manually execute this script, but that wouldn’t make any sense, so instead, we’ll configure macOS to execute the script every 2 hours
If you’re on Linux, all you need to do is to setup a cron job and you’re good to go
In macOS it’s a bit trickier but it can be achieved creating a launch agent.
Automatically execute the script on macOS
Remember, as I mentioned above, we’ll do this using a LaunchAgent
I don’t like creating it manually, so I just add the code below to my .zshrc file, and if the file does not exist, it will create it. If the plist file is not loaded, it will load it
# Automate tmux session cleanup every X hours using a LaunchAgent# This will create plist file to run the script every X hours# and log output/errors to /tmp/$PLIST_LABEL.out and /tmp/$PLIST_LABEL.err# NOTE: If you modify the INTERVAL_SEC below, make sure to also change it in# the ~/github/dotfiles-latest/tmux/tools/linkarzu/tmuxKillSessions.sh script## 1 hour = 3600 sINTERVAL_SEC=7200
PLIST_ID="tmuxKillSessions"PLIST_NAME="com.linkarzu.$PLIST_ID.plist"PLIST_LABEL="${PLIST_NAME%.plist}"PLIST_PATH="$HOME/Library/LaunchAgents/$PLIST_NAME"SCRIPT_PATH="$HOME/github/dotfiles-latest/tmux/tools/linkarzu/$PLIST_ID.sh"# Ensure the script file existsif[!-f"$SCRIPT_PATH"];then
echo"Error: $SCRIPT_PATH does not exist."else# If the PLIST file does not exist, create itif[!-f"$PLIST_PATH"];then
echo"Creating $PLIST_PATH..."cat<<EOF >"$PLIST_PATH"
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>$PLIST_LABEL</string>
<key>ProgramArguments</key>
<array>
<string>$SCRIPT_PATH</string>
</array>
<key>StartInterval</key>
<integer>$INTERVAL_SEC</integer>
<key>StandardOutPath</key>
<string>/tmp/$PLIST_ID.out</string>
<key>StandardErrorPath</key>
<string>/tmp/$PLIST_ID.err</string>
</dict>
</plist>
EOF
fi
fi# Check if the plist is loaded, and load it if notif! launchctl list | grep-q"$PLIST_LABEL";then
echo"Loading $PLIST_PATH..."
launchctl load "$PLIST_PATH"echo"$PLIST_PATH loaded."fi
Do you need to add this to your .zshrc file?:
No, you can create the plist file manually, you can see the code that my ~/.zshrc file generated in ~/Library/LaunchAgents/com.linkarzu.tmuxKillSessions.plist
If you pay attention to the file, there’s a lot of verification steps I run, and I initialize a lot of the tools that I use. It’s a mess, I have to separate it in multiple files to make it easier to understand, but I set it up so long ago and it just works as of now, so why bother
Again, remember that the latest version of the code shown above is always going to be in my dotfiles, for now, my zshrc file can be found here:
Do you want to promote yourself in my channel? I’m not talking about a company like notion, brilliant, and all those other ones we’re using to seeing. I’m talking about you as a person, do you have a project, course, youtube channel or product and trying to reach an audience?
If interested, pricing and all the details can be found in this other page
You’re a fraud, why do you ask for money, isn’t YouTube Ads enough?
I explain all of this in the “about me page” link below: