Skip to content

Instantly share code, notes, and snippets.

@XDflight
Last active July 1, 2026 15:05
Show Gist options
  • Select an option

  • Save XDflight/5f3509eb84fc282b88059c909036f5bc to your computer and use it in GitHub Desktop.

Select an option

Save XDflight/5f3509eb84fc282b88059c909036f5bc to your computer and use it in GitHub Desktop.
Remove cache and old extensions under "~/.vscode-server/" on your Linux server

If you often connect to your Linux server using VSCode, the "~/.vscode-server/" folder (and sometimes the ~/.cache/ folder too) can get very large because VSCode:

  1. Does NOT clean its download cache after installing extensions;
  2. Does NOT delete old extensions after updating them;
  3. Does NOT remove old VSCode servers after installing a new version.

If your server storage space is limited, you might consider cleaning "~/.vscode-server/" (and ~/.cache/) regularly using the bash script I wrote. Simply run the following command:

curl -sL https://gist.githubusercontent.com/XDflight/5f3509eb84fc282b88059c909036f5bc/raw/fe2a3e7dab19160c17efc7025d138f5350964c0f/clean_vscode-server.sh | bash -s

This command will automatically download and run the script for you.

I programmed this thing because CMU only provides each student 2GB of AFS cloud storage in normal circumstances, unless maybe the courses you take requested for more, and since I often use VSCode to connect to Virtual Andrew via SSH to do homework, my "~/.vscode-server/" gets too big now and then that I have to clean it manually to keep my storage usage within the quota. This script automates the process to make my life easier.

#!/bin/bash
# Discussions at:
# - https://gist.github.com/XDflight/5f3509eb84fc282b88059c909036f5bc
# - https://www.reddit.com/r/vscode/comments/1ocqftt/a_tool_that_helps_you_free_up_vscodeserver_if_you/
rm -rf ~/.cache 2> /dev/null
if [ ! -d ~/.vscode-server ]; then
echo "\"~/.vscode-server\" does not exist."
exit 0
fi
pkill -u "$USER" -f '\.vscode-server'
shopt -s extglob nullglob
rm -rf ~/.vscode-server/data/CachedExtensionVSIXs
rm -f ~/.vscode-server/.cli.*.log
if [[ -f ~/.vscode-server/cli/servers/lru.json ]]; then
SERVER_VERSION=$(grep -oE '[0-9a-f]{40}' ~/.vscode-server/cli/servers/lru.json | head -n1)
echo "[\"Stable-$SERVER_VERSION\"]" > ~/.vscode-server/cli/servers/lru.json
rm -rf ~/.vscode-server/cli/servers/Stable-!($SERVER_VERSION)
rm -f ~/.vscode-server/code-!($SERVER_VERSION)
else
rm -rf ~/.vscode-server/cli
rm -f ~/.vscode-server/code-*
fi
rm -rf ~/.vscode-server/extensions/*.vsctmp
declare -A exts
for lsout in $(cd ~/.vscode-server/extensions && ls -rvd */); do
ext_fullname=${lsout::-1}
ext_version=$(grep -oP '\-\d+\.\d+\.\d+' <<< "$ext_fullname")
# Skip extensions whose directory names do not end in semver
[[ -z "$ext_version" ]] && continue
ext_name=${ext_fullname/$ext_version}
ext_version=${ext_version:1}
if [[ -n "${exts[$ext_name]+isset}" ]]; then
rm -rf ~/.vscode-server/extensions/$lsout
else
exts[$ext_name]=$ext_version
fi
done
shopt -u extglob nullglob
@sebastian-power

Copy link
Copy Markdown

peak

@kipavy

kipavy commented Jun 9, 2026

Copy link
Copy Markdown

Thanks (shame on VSCode Team)

@saiki-k

saiki-k commented Jun 15, 2026

Copy link
Copy Markdown

Thanks for sharing this script.

While testing it on a couple of Ubuntu machines (including a fresh OCI VM), I ran into a few edge cases and made some changes. Posting them here in case they’re useful, and also for a sanity check in case I misunderstood any part of the original intent.

Changes made

1. Fixed lru.json existence check

Original:

if [[ ! -f "~/.vscode-server/cli/servers/lru.json" ]]; then

I think there are two issues here:

  • Quoting ~ prevents home expansion, so this checks for a literal path starting with ~
  • The condition seems reversed: the script tries to read lru.json only when it is considered absent

I changed it to:

if [[ -f ~/.vscode-server/cli/servers/lru.json ]]; then

This makes the logic consistent with the subsequent grep.


2. Enabled nullglob

Original:

shopt -s extglob

Changed to:

shopt -s extglob nullglob

This matters when ~/.vscode-server/extensions contains no extension directories.

On a fresh machine I had:

extensions.json

and nothing else.

Without nullglob, the extension cleanup path can eventually hit:

bad array subscript

3. Replaced ls-based extension iteration

Original:

for lsout in $(cd ~/.vscode-server/extensions && ls -rvd */); do

Changed to:

for dir in ~/.vscode-server/extensions/*/; do

Reasons:

  • avoids parsing ls
  • avoids command substitution
  • avoids word splitting
  • works cleanly with nullglob

4. Added guard for non-semver extension directory names

The extension cleanup assumes directory names look like:

publisher.extension-1.2.3

Version extraction uses:

grep -oP '\-\d+\.\d+\.\d+'

If a directory does not match that pattern, ext_version becomes empty and later array indexing can fail.

I added:

[[ -z "$ext_version" ]] && continue

so unexpected directory names are skipped instead of breaking the script.


5. Minor cleanup

  • removed legacy lsout variable
  • quoted associative array keys
  • restored shell options at exit with:
shopt -u extglob nullglob

Not strictly necessary for standalone execution, but keeps things tidy.

Would appreciate a review, especially around the lru.json logic change. That was the only part where I wasn’t fully sure whether the original behavior was intentional.

Modified script

#!/bin/bash

# Downloaded from https://gist.github.com/XDflight/5f3509eb84fc282b88059c909036f5bc
# Discussion at https://www.reddit.com/r/vscode/comments/1ocqftt/a_tool_that_helps_you_free_up_vscodeserver_if_you/

# Edited on 20260615

rm -rf ~/.cache 2>/dev/null

if [[ ! -d ~/.vscode-server ]]; then
    echo "\"~/.vscode-server\" does not exist."
    exit 0
fi

pkill -u "$USER" -f '\.vscode-server'

shopt -s extglob nullglob

rm -rf ~/.vscode-server/data/CachedExtensionVSIXs
rm -f ~/.vscode-server/.cli.*.log

if [[ -f ~/.vscode-server/cli/servers/lru.json ]]; then
    SERVER_VERSION=$(grep -oE '[0-9a-f]{40}' ~/.vscode-server/cli/servers/lru.json | head -n1)
    echo "[\"Stable-$SERVER_VERSION\"]" > ~/.vscode-server/cli/servers/lru.json
    rm -rf ~/.vscode-server/cli/servers/Stable-!($SERVER_VERSION)
    rm -f ~/.vscode-server/code-!($SERVER_VERSION)
else
    rm -rf ~/.vscode-server/cli
    rm -f ~/.vscode-server/code-*
fi

rm -rf ~/.vscode-server/extensions/*.vsctmp

declare -A exts

for dir in ~/.vscode-server/extensions/*/; do
    ext_fullname=$(basename "$dir")

    ext_version=$(grep -oP '\-\d+\.\d+\.\d+' <<< "$ext_fullname")

    # Skip extensions whose directory names do not end in semver
    [[ -z "$ext_version" ]] && continue

    ext_name=${ext_fullname/$ext_version}
    ext_version=${ext_version:1}

    if [[ -n "${exts[$ext_name]+isset}" ]]; then
        rm -rf "$dir"
    else
        exts["$ext_name"]=$ext_version
    fi
done

shopt -u extglob nullglob

@XDflight

Copy link
Copy Markdown
Author

Thanks for your contribution :)

Most of the fixes look good to me. The only change I’m concerned about is the following one:

3. Replaced ls-based extension iteration

Original:

for lsout in $(cd ~/.vscode-server/extensions && ls -rvd */); do

Changed to:

for dir in ~/.vscode-server/extensions/*/; do

This changes the iteration order. In this case, the extension version directories need to be processed in reverse version order, so we should preserve the original ls -rvd behavior here.

The script was updated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment