Tmux Cleanup Session Script | Automatically Kill Unused Tmux Sessions
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.
Tmux Cleanup Session Script | Automatically Kill Unused Tmux Sessions
Contents
Table of contents
- YouTube video
- If you like my content, and want to support me
- Discord server
- Follow me on social media
- All links in the video description
- How do you manage your passwords?
- Automatically clean up tmux sessions?
- Output of the script
- Script used
- Automatically execute the script on macOS
- Command:
launchctl print
- Change interval in which the script is executed
- Start your 14 day FREE trial
YouTube video
If you like my content, and want to support me
- I create and edit my videos in an M1 mac mini, and it’s starting to stay behind in the editing side of things, tends to slow me down a bit, I’d like to upgrade the machine I use for all my videos to a
mac mini
with these specs:- Apple M4 Pro chip with 14‑core CPU, 20‑core GPU, 16-core Neural Engine
- 24GB unified memory
- 1TB SSD storage
- 10 Gigabit Ethernet
- If you want to help me reach my goal, you can donate here
Discord server
- My discord server is now open to the public, feel free to join and hang out with others
- join the discord server in this link
Follow me on social media
- Twitter (or “X”)
- TikTok
- GitHub
- Threads
- OnlyFans 🍆
- YouTube (subscribe MF, subscribe)
- Ko-Fi
All links in the video description
- The following links will be in the YouTube video description:
- Each one of the videos shown
- A link to this blogpost
How do you manage your passwords?
- I’ve tried many different password managers in the past, I’ve switched from
LastPass
toDashlane
and finally ended up in1password
- You want to find out why? More info in my article:
Automatically clean up tmux sessions?
- 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 typehyper+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
- Script found here: linkarzu/tmuxKillSessions.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/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 command
export PATH="/opt/homebrew/bin:$PATH"
# I make this slightly lower than the LaunchAgent interval
TOO_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
1
tmux ls -F '#{session_name} #{session_activity}'
1
2
3
4
5
6
❯❯❯❯ tmux ls -F '#{session_name} #{session_activity}'
SSH-storage3-k 1741920331
dotfiles_latest-j 1741923284
linkarzu-h 1741923338
linkarzu_github_io- 1741923336
obsidian_main-u 1741922438
- 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# 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 s
INTERVAL_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 exists
if [ ! -f "$SCRIPT_PATH" ]; then
echo "Error: $SCRIPT_PATH does not exist."
else
# If the PLIST file does not exist, create it
if [ ! -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 not
if ! 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
- No, you can create the plist file manually, you can see the code that my
1
cat ~/Library/LaunchAgents/com.linkarzu.tmuxKillSessions.plist
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
linkarzu.@.[25/03/13]
~/Library/LaunchAgents
❯❯❯❯ cat com.linkarzu.tmuxKillSessions.plist
<?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>com.linkarzu.tmuxKillSessions</string>
<key>ProgramArguments</key>
<array>
<string>/Users/linkarzu/github/dotfiles-latest/tmux/tools/linkarzu/tmuxKillSessions.sh</string>
</array>
<key>StartInterval</key>
<integer>7200</integer>
<key>StandardOutPath</key>
<string>/tmp/tmuxKillSessions.out</string>
<key>StandardErrorPath</key>
<string>/tmp/tmuxKillSessions.err</string>
</dict>
</plist>
- Only if you are adding the plist file manually, load it with the command below:
1
launchctl load ~/Library/LaunchAgents/com.linkarzu.tmuxKillSessions.plist
- Why do I add this to my
.zshrc
file? - 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:
- Another big reason why my
zshrc
file is a bit complex, is because it helps me setup a new mac computer relatively easily. - I have a script that I execute when I get a new mac and I go over that in detail in this video:
Command: launchctl print
- If I run this command, it shows me a lot of details about this
launchctl
agent
1
launchctl print gui/$(id -u)/com.linkarzu.tmuxKillSessions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
linkarzu.@.[25/03/13]
~/Library/LaunchAgents
❯❯❯❯ launchctl print gui/$(id -u)/com.linkarzu.tmuxKillSessions
gui/501/com.linkarzu.tmuxKillSessions = {
active count = 0
path = /Users/linkarzu/Library/LaunchAgents/com.linkarzu.tmuxKillSessions.plist
type = LaunchAgent
state = not running
domain = gui/501 [100012]
asid = 100012
minimum runtime = 10
exit timeout = 5
runs = 7
last exit code = 0
spawn type = daemon (3)
jetsam priority = 40
jetsam memory limit (active) = (unlimited)
jetsam memory limit (inactive) = (unlimited)
jetsamproperties category = daemon
jetsam thread limit = 32
cpumon = default
run interval = 7200 seconds
probabilistic guard malloc policy = {
activation rate = 1/1000
sample rate = 1/0
}
properties = inferred program | system service | managed LWCR | tle system
}
- Notice I can see that it has been executed 7 times
runs = 7
- I can also see the interval
run interval = 7200 seconds
and many more details - This quickly helps you to see if the launch agent is working or not
Change interval in which the script is executed
If adding the code to your zshrc
file
- First delete the existing plist file
1
rm ~/Library/LaunchAgents/com.linkarzu.tmuxKillSessions.plist
- Then, re-create the file (in my case I just source my
zshrc
file)
1
source ~/.zshrc
- Then I unload the plist file
1
launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/com.linkarzu.tmuxKillSessions.plist
- Source my
zshrc
file again
1
source ~/.zshrc
If creating the plist file manually
- Just modify the file directly
- Then you’ll probably have to unload it
1
launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/com.linkarzu.tmuxKillSessions.plist
- And then load it again
1
launchctl load ~/Library/LaunchAgents/com.linkarzu.tmuxKillSessions.plist
Start your 14 day FREE trial
This post is licensed under CC BY 4.0 by the author.