Sunday, 11 October 2026

Complete Guide: How to Uninstall LM Studio on Ubuntu 24.04

A step-by-step guide to removing LM Studio from Ubuntu 24.04 with a Bash uninstaller script. It stops the app, cleans up its data, settings, cache, AppImage and system-wide files, and asks whether to keep your downloaded AI models. A dry-run mode previews every change first.

Computer screens display technical interfaces, including text reading 'UNINSTALLING LM STUDIO...' and 'UBUNTU 24.04'.
A digital graphic showing an uninstall process for LM Studio within the Ubuntu operating system.

Introduction

LM Studio is an incredible tool for running local LLMs, but because it is typically distributed as an AppImage or manual download, it doesn’t always leave a clean trail. It scatters configuration files, cached blobs, and gigabytes of AI models across your home directory.

In this tutorial, we will use a specialized Bash script to perform a surgical uninstallation, ensuring your Ubuntu system remains lean and clutter-free.


1. Understanding the Uninstallation Logic

Before running a script with sudo privileges, it is vital to understand its “search and destroy” logic. Here is what happens behind the scenes:

  • Process Management: The script first stops any running instances (lm-studio, lmstudio), so the app can’t write its files back while they are being removed. In a dry run it only lists them.
  • App Data: It removes LM Studio’s own folder, ~/.lmstudio, and its folders in the standard Linux (XDG) locations: ~/.config, ~/.cache and ~/.local/share. It also deletes the AppImage if you saved it in ~/Applications, ~/Downloads, ~/Desktop or ~/bin.
  • Model Preservation: Because AI models are massive, the script shows the size of your models folder and asks if you want to keep it for future use or wipe it to reclaim disk space. It asks before anything is deleted, and if you keep the models, everything around them is still removed.
  • System Scrubbing: If run with root privileges, it cleans up system-wide locations like /opt, /usr/local/bin, and removes desktop menu shortcuts.

2. How to Use the Uninstaller Script

Step 1: Create the Script

Open your terminal and create a new bash file:

nano uninstall_lm_studio.sh

Paste the full script from section 5 into the editor, then save and exit (Ctrl+O, Enter, Ctrl+X).

Step 2: Set Permissions

You must give the system permission to execute the script:

chmod +x uninstall_lm_studio.sh

Step 3: Choose Your Execution Mode

The script supports three different ways to run, depending on your needs. Each one opens the same menu (see below):

  • The Safety Check (Dry Run): See what would be deleted, and which LM Studio processes would be stopped, without touching anything. ./uninstall_lm_studio.sh --dry-run
  • Standard Uninstall: Clean up your current user profile (option 1, then pick your account). ./uninstall_lm_studio.sh
  • System-Wide Wipe: Remove LM Studio for all users and root (option 2). sudo ./uninstall_lm_studio.sh

3. Navigating the Menu Options

OptionUse Case
1) Specific UserCleans up one account. Your own works without sudo; anyone else’s needs it.
2) System-wideUse this if you used sudo to install LM Studio or want it gone from the entire machine. Needs sudo.
3) Search and ListSearches the whole system for anything named like LM Studio, lists it (and can save the list to a file), then offers to delete it all. Run it with sudo to see every folder.
4) ExitCloses the script without doing anything.

4. Manual Verification (Optional)

If you prefer to double-check the work manually, these are the locations the script cleans up. If they are gone, your uninstallation was successful:

  • ~/.lmstudio (Models, the lms command-line tool and app data in current versions)
  • ~/.config/LM Studio (User settings)
  • ~/.cache/lm-studio (Temporary files; older versions also keep models here)
  • ~/.local/share/applications/lm-studio.desktop (Menu icon, if you created one)
  • The LM-Studio-….AppImage file itself, wherever you saved it

If you used the lms command-line tool, lms bootstrap also added ~/.lmstudio/bin to your PATH. The script points that line out but leaves your shell files alone; find it with grep -n lmstudio ~/.bashrc ~/.zshrc ~/.profile and delete it.

Pro-Tip: If you chose to KEEP your models during uninstallation, you can find them later in ~/.lmstudio/models (or ~/.cache/lm-studio/models if you used an older version). This is helpful if you want to move them to an external drive before a final wipe.


5. The Full Uninstaller Script

Save this as uninstall_lm_studio.sh (Step 1 above):

#!/usr/bin/env bash
# LM Studio Uninstaller for Ubuntu 24.04
# Author: Windows-HQ.com
# 
# Usage: ./uninstall_lm_studio.sh [--dry-run]   (run with sudo for the system-wide option)

set -euo pipefail

# ── Colours ──────────────────────────────────────────────────────────────────
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
BLUE='\033[0;34m'; CYAN='\033[0;36m'; MAGENTA='\033[0;35m'; NC='\033[0m'

# ── Global flags ─────────────────────────────────────────────────────────────
DRY_RUN=false
for arg in "$@"; do [[ "$arg" == "--dry-run" ]] && DRY_RUN=true; done

# ── Temp-file cleanup ─────────────────────────────────────────────────────────
SEARCH_TMP=""
cleanup() { if [[ -n "$SEARCH_TMP" && -f "$SEARCH_TMP" ]]; then rm -f "$SEARCH_TMP"; fi; }
trap cleanup EXIT

# ── Helpers ───────────────────────────────────────────────────────────────────

banner() {
    clear
    echo -e "${CYAN}╔══════════════════════════════════════════════════════════════╗${NC}"
    echo -e "${CYAN}║${NC}         ${MAGENTA}LM Studio Uninstaller for Ubuntu 24.04${NC}              ${CYAN}║${NC}"
    if $DRY_RUN; then
        echo -e "${CYAN}║${NC}              ${YELLOW}⚠  DRY-RUN MODE — nothing will be deleted${NC}          ${CYAN}║${NC}"
    fi
    echo -e "${CYAN}╚══════════════════════════════════════════════════════════════╝${NC}"
    echo
}

# Safe remove: honours --dry-run and logs every deletion; returns 1 if it fails
safe_rm() {
    local path="$1"
    if [[ ! -e "$path" && ! -L "$path" ]]; then return 0; fi
    if $DRY_RUN; then
        echo -e "  ${YELLOW}[dry-run]${NC} would remove: $path"
    else
        echo "  Removing: $path"
        if ! rm -rf -- "$path"; then
            echo -e "  ${RED}Could not remove${NC}: $path"
            return 1
        fi
    fi
}

# Confirm with a default-No prompt; returns 0 on yes, 1 on no
confirm() {
    local prompt="$1"
    local reply
    read -r -p "  $prompt (y/N): " reply
    [[ "${reply,,}" == "y" ]]
}

# Kill processes owned by a user (or any user when called as root). Called before
# anything is removed, so a closing app can't write its files back. With
# --dry-run it only reports what it would stop.
kill_lm_processes() {
    local user="${1:-}"
    local patterns=("lm-studio" "LM-Studio" "lmstudio" "LMStudio")
    local -a owner=()
    if [[ -n "$user" ]]; then owner=(-u "$user"); fi
    for pat in "${patterns[@]}"; do
        if $DRY_RUN; then
            pgrep "${owner[@]}" -f "$pat" >/dev/null 2>&1 \
                && echo -e "  ${YELLOW}[dry-run]${NC} would kill '$pat' processes${user:+ for $user}" || true
        else
            pkill "${owner[@]}" -f "$pat" 2>/dev/null \
                && echo "  Killed '$pat' processes${user:+ for $user}" || true
        fi
    done
}

# ── Core: remove all LM Studio artefacts under one home directory ─────────────
#
# Parameters:
#   $1  home directory
#   $2  username (for display)
#   $3  "ask" | "keep" | "remove"  — how to handle model directories
#
uninstall_for_user() {
    local home_dir="$1"
    local username="$2"
    local model_policy="${3:-ask}"
    local removed=0
    local path

    echo -e "\n${GREEN}Uninstalling for user: $username${NC}"

    # ── App data: ~/.lmstudio (current versions) and the XDG dirs ──────────────
    #    (~/.config/LM Studio, ~/.cache/lm-studio, ...); find -iname covers
    #    every capitalisation
    local -a targets=()
    if [[ -e "$home_dir/.lmstudio" ]]; then targets+=("$home_dir/.lmstudio"); fi
    local xdg_roots=("$home_dir/.local/share" "$home_dir/.config" "$home_dir/.cache")
    for root in "${xdg_roots[@]}"; do
        [[ -d "$root" ]] || continue
        while IFS= read -r -d '' path; do
            targets+=("$path")
        done < <(find "$root" -mindepth 1 -maxdepth 1 \
                      \( -iname "lm?studio*" -o -iname "lmstudio*" \) -print0 2>/dev/null)
    done

    # ── Models: ~/.lmstudio/models now, ~/.cache/lm-studio/models in older ─────
    #    versions. Decide before anything is removed.
    local -a model_dirs=()
    for path in "${targets[@]}"; do
        if [[ -d "$path/models" ]]; then model_dirs+=("$path/models"); fi
    done
    local keep_models=false
    if (( ${#model_dirs[@]} > 0 )); then
        local model_size
        model_size=$(du -shc "${model_dirs[@]}" 2>/dev/null | tail -n 1 | cut -f1)
        echo -e "\n  ${YELLOW}Found model directory${NC}: ${model_dirs[*]} (${model_size})"
        case "$model_policy" in
            remove) ;;
            keep)   keep_models=true; echo "  Keeping models (policy=keep)" ;;
            ask)
                if confirm "Remove downloaded models for $username?"; then
                    :
                else
                    keep_models=true
                    echo "  Keeping models"
                fi
                ;;
        esac
    fi

    # ── Remove the app data; kept models stay where they are ───────────────────
    local item
    for path in "${targets[@]}"; do
        if $keep_models && [[ -d "$path/models" ]]; then
            while IFS= read -r -d '' item; do
                [[ "$item" == "$path/models" ]] && continue
                safe_rm "$item" || true
            done < <(find "$path" -mindepth 1 -maxdepth 1 -print0 2>/dev/null)
        else
            safe_rm "$path" || true
        fi
        (( removed++ )) || true
    done

    # ── Desktop entries ────────────────────────────────────────────────────────
    while IFS= read -r -d '' path; do
        safe_rm "$path" || true
        (( removed++ )) || true
    done < <(find "$home_dir/.local/share/applications" -maxdepth 1 \
                  \( -iname "lm?studio*.desktop" -o -iname "lmstudio*.desktop" \) -print0 2>/dev/null)

    # ── Local binaries ─────────────────────────────────────────────────────────
    while IFS= read -r -d '' path; do
        safe_rm "$path" || true
        (( removed++ )) || true
    done < <(find "$home_dir/.local/bin" -maxdepth 1 \
                  \( -iname "lm?studio*" -o -iname "lmstudio*" \) -print0 2>/dev/null)

    # ── The AppImage, wherever it was saved ────────────────────────────────────
    while IFS= read -r -d '' path; do
        safe_rm "$path" || true
        (( removed++ )) || true
    done < <(find "$home_dir/Applications" "$home_dir/Downloads" "$home_dir/Desktop" "$home_dir/bin" \
                  -maxdepth 1 \( -iname "lm?studio*.appimage" -o -iname "lmstudio*.appimage" \) -print0 2>/dev/null)

    # ── The PATH line 'lms bootstrap' added (left alone, but pointed out) ──────
    local rc
    for rc in "$home_dir/.bashrc" "$home_dir/.zshrc" "$home_dir/.profile" "$home_dir/.bash_profile"; do
        if [[ -f "$rc" ]] && grep -q '\.lmstudio/bin' "$rc" 2>/dev/null; then
            echo -e "  ${YELLOW}Note${NC}: $rc still adds ~/.lmstudio/bin to PATH (from 'lms bootstrap'); delete that line if you like."
        fi
    done

    if (( removed == 0 )); then
        echo -e "  ${YELLOW}Nothing found for $username${NC}"
    else
        echo -e "  ${GREEN}Done (${removed} item(s) processed)${NC}"
    fi
}

# ── Iterate every regular user from /etc/passwd ───────────────────────────────
each_human_user() {
    # Callback receives (home, username, extra_args...)
    local callback="$1"; shift
    while IFS=: read -r username _ uid _ _ home _; do
        (( uid >= 1000 && uid < 65534 )) || continue
        [[ "$username" == "nobody" ]] && continue
        [[ -d "$home" ]] || continue
        "$callback" "$home" "$username" "$@"
    done < /etc/passwd
}

# ── Option 1: single selected user ───────────────────────────────────────────
uninstall_selected_user() {
    echo -e "\n${CYAN}Select a user to uninstall LM Studio${NC}"
    echo "────────────────────────────────────────"

    local -a users=() homes=()
    local i=1
    while IFS=: read -r username _ uid _ _ home _; do
        (( uid >= 1000 && uid < 65534 )) || continue
        [[ "$username" == "nobody" ]] && continue
        [[ -d "$home" ]] || continue
        users+=("$username"); homes+=("$home")
        printf "  ${GREEN}%d)${NC} %-20s  %s\n" "$i" "$username" "$home"
        (( i++ )) || true
    done < /etc/passwd

    if (( ${#users[@]} == 0 )); then
        echo -e "${RED}No regular users found.${NC}"
        read -r -p "Press Enter to continue…"; return
    fi

    local cancel=$i
    echo "  $cancel) Cancel"
    echo
    local choice
    read -r -p "Choice (1–$cancel): " choice
    if ! [[ "$choice" =~ ^[0-9]+$ ]] || (( choice < 1 || choice > cancel )); then
        echo -e "${RED}Invalid input.${NC}"
        read -r -p "Press Enter to continue…"; return
    fi
    (( choice == cancel )) && { echo -e "${GREEN}Cancelled.${NC}"; return; }

    local idx=$(( choice - 1 ))
    echo -e "\n${YELLOW}Uninstalling for: ${users[$idx]}${NC}"
    kill_lm_processes "${users[$idx]}"
    uninstall_for_user "${homes[$idx]}" "${users[$idx]}" ask

    read -r -p $'\nPress Enter to continue…'
}

# ── Option 2: system-wide ─────────────────────────────────────────────────────
uninstall_system_wide() {
    echo -e "\n${RED}⚠  SYSTEM-WIDE UNINSTALLATION${NC}"
    echo -e "${YELLOW}This removes LM Studio for ALL users, root, and system locations.${NC}"
    confirm "Continue?" || { echo -e "${GREEN}Cancelled.${NC}"; return; }

    # Ask once about models; apply to every user silently
    local model_policy
    echo
    if confirm "Remove downloaded AI models for all users?"; then
        model_policy="remove"
    else
        model_policy="keep"
    fi

    echo -e "\n${CYAN}Stopping running processes…${NC}"
    kill_lm_processes ""

    echo -e "\n${CYAN}Uninstalling for all regular users…${NC}"
    each_human_user uninstall_for_user "$model_policy"

    echo -e "\n${CYAN}Uninstalling for root…${NC}"
    uninstall_for_user /root root "$model_policy"

    echo -e "\n${CYAN}Removing system-wide locations…${NC}"
    local sys_roots=(/opt /usr/local /usr/share/applications)
    for root in "${sys_roots[@]}"; do
        [[ -d "$root" ]] || continue
        while IFS= read -r -d '' path; do
            safe_rm "$path" || true
        done < <(find "$root" -maxdepth 1 \
                      \( -iname "lm?studio*" -o -iname "lmstudio*" \) \
                      -print0 2>/dev/null)
    done

    echo -e "\n${CYAN}Removing symlinks from PATH locations…${NC}"
    for bin_dir in /usr/local/bin /usr/bin; do
        [[ -d "$bin_dir" ]] || continue
        while IFS= read -r -d '' path; do
            safe_rm "$path" || true
        done < <(find "$bin_dir" -maxdepth 1 -type l \( -iname "lm?studio*" -o -iname "lmstudio*" \) -print0 2>/dev/null)
    done

    if ! $DRY_RUN && command -v update-desktop-database &>/dev/null; then
        update-desktop-database 2>/dev/null || true
    fi

    echo -e "\n${GREEN}System-wide uninstallation complete.${NC}"
    read -r -p $'\nPress Enter to continue…'
}

# ── Option 3: search entire filesystem ───────────────────────────────────────
search_all_files() {
    echo -e "\n${CYAN}Scanning filesystem for LM Studio files…${NC}"
    echo -e "${YELLOW}This may take several minutes.${NC}\n"

    SEARCH_TMP=$(mktemp)

    # Directories excluded from the search
    local -a prune_paths=(
        /proc /sys /dev /run /snap /boot /lost+found /tmp /var/tmp
        /var/cache/apt /var/lib/apt /var/lib/dpkg /var/cache/debconf
    )

    # Build a single find command using -prune for excluded dirs,
    # and -iname with -o for all name patterns — NO eval required
    local prune_expr=()
    for p in "${prune_paths[@]}"; do
        prune_expr+=( -path "$p" -o -path "$p/*" -o )
    done
    # The trailing -o leaves an open OR; close it with a never-true expression
    prune_expr+=( -path "/_no_such_path_" )

    # Collect all matching paths into the temp file (nullchar-delimited → sorted)
    find / \( "${prune_expr[@]}" \) -prune -o \
        \( -iname "lm-studio*" -o -iname "lm studio*" -o \
           -iname "lmstudio*" -o -iname ".lmstudio*" \) -print0 2>/dev/null \
        | sort -zu > "$SEARCH_TMP"

    # Tally and display
    local -a dirs=() files=() links=() other=()
    while IFS= read -r -d '' path; do
        if   [[ -L  "$path" ]]; then links+=("$path")
        elif [[ -d  "$path" ]]; then dirs+=("$path")
        elif [[ -f  "$path" ]]; then files+=("$path")
        else                         other+=("$path")
        fi
    done < "$SEARCH_TMP"

    local total=$(( ${#dirs[@]} + ${#files[@]} + ${#links[@]} + ${#other[@]} ))

    echo -e "${BLUE}══════════════════════════════════════════════════════════════${NC}"
    echo -e "${MAGENTA}Directories (${#dirs[@]}):${NC}"
    for p in "${dirs[@]}";  do printf "  %s\n" "$p"; done
    [[ ${#dirs[@]}  -eq 0 ]] && echo "  (none)"

    echo -e "\n${MAGENTA}Files (${#files[@]}):${NC}"
    for p in "${files[@]}"; do
        local sz; sz=$(du -sh "$p" 2>/dev/null | cut -f1)
        printf "  %-60s  %s\n" "$p" "$sz"
    done
    [[ ${#files[@]} -eq 0 ]] && echo "  (none)"

    echo -e "\n${MAGENTA}Symlinks (${#links[@]}):${NC}"
    for p in "${links[@]}"; do printf "  %s -> %s\n" "$p" "$(readlink "$p")"; done
    [[ ${#links[@]} -eq 0 ]] && echo "  (none)"

    echo -e "${BLUE}══════════════════════════════════════════════════════════════${NC}"
    echo -e "${GREEN}Total: $total item(s) found  " \
            "(dirs: ${#dirs[@]}, files: ${#files[@]}, symlinks: ${#links[@]})${NC}"
    echo -e "${BLUE}══════════════════════════════════════════════════════════════${NC}"

    if confirm "Save these results to a file?"; then
        local out
        out="lm_studio_search_$(date +%Y%m%d_%H%M%S).txt"
        {
            echo "LM Studio file search — $(date)"
            echo "DIRECTORIES:"; printf "  %s\n" "${dirs[@]:-}"
            echo "FILES:";       printf "  %s\n" "${files[@]:-}"
            echo "SYMLINKS:"
            for p in "${links[@]}"; do printf "  %s -> %s\n" "$p" "$(readlink "$p")"; done
            echo "Total: $total"
        } > "$out"
        echo -e "${GREEN}Saved to: $out${NC}"
    fi

    if (( total > 0 )); then
        echo
        if confirm "Remove all found LM Studio items?"; then
            echo -e "${YELLOW}Removing…${NC}"
            local fail=0
            # Process deepest paths first so parent dirs aren't removed before children
            readarray -td '' sorted_paths < <(printf '%s\0' \
                "${dirs[@]:-}" "${files[@]:-}" "${links[@]:-}" "${other[@]:-}" \
                | sort -rzu)
            for path in "${sorted_paths[@]}"; do
                safe_rm "$path" || (( fail++ )) || true
            done
            (( fail > 0 )) && echo -e "${YELLOW}${fail} item(s) could not be removed.${NC}"
            echo -e "${GREEN}Done.${NC}"
        fi
    fi

    read -r -p $'\nPress Enter to continue…'
}

# ── Main menu ─────────────────────────────────────────────────────────────────
while true; do
    banner
    echo -e "${CYAN}Select an option:${NC}\n"
    echo -e "  ${GREEN}1)${NC} Uninstall for a specific user"
    echo -e "  ${GREEN}2)${NC} Uninstall system-wide (all users + root)  ${RED}[requires sudo]${NC}"
    echo -e "  ${GREEN}3)${NC} Search and list all LM Studio files"
    echo -e "  ${RED}4)${NC} Exit"
    echo
    read -r -p "Choice (1–4): " choice

    case "${choice// /}" in
        1) uninstall_selected_user ;;
        2)
            if (( EUID != 0 )); then
                echo -e "\n${RED}System-wide uninstallation requires root.${NC}"
                echo -e "${YELLOW}Re-run with: sudo $0${NC}"
                read -r -p $'\nPress Enter to continue…'
            else
                uninstall_system_wide
            fi
            ;;
        3)
            (( EUID != 0 )) && \
                echo -e "\n${YELLOW}Running without sudo — some directories may be inaccessible.${NC}\n"
            search_all_files
            ;;
        4) echo -e "\n${GREEN}Goodbye.${NC}"; exit 0 ;;
        *) echo -e "\n${RED}Invalid choice.${NC}"; sleep 1 ;;
    esac
done

Conclusion

By using a structured script instead of manually hunting for files, you ensure that Ubuntu 24.04 stays optimized. Whether you’re troubleshooting a bad install or moving to a different LLM runner, this method is the safest way to ensure a clean slate.