Skip to content

Instantly share code, notes, and snippets.

@ernstki
Last active October 17, 2025 19:00
Show Gist options
  • Save ernstki/f3e279e8a050c2df94e9fcfd69d67c2f to your computer and use it in GitHub Desktop.
Save ernstki/f3e279e8a050c2df94e9fcfd69d67c2f to your computer and use it in GitHub Desktop.
List space used by Flatpak ~/.var/app directories for apps that are no longer installed
#!/usr/bin/env python3
##
## flatcrap - lists orphaned Flatpak data in ~/.var/app
##
## Author: Kevin Ernst <ernstki -at- mail.uc.edu>
## Date: 16 October 2025
## License: MPL 2.0
## Homepage: https://gist.github.com/ernstki/f3e279e8a050c2df94e9fcfd69d67c2f
##
## Originally based on `main.py` from Flatsweep by giantpinkrobots[1], but
## that code was pretty rough (LLM-generated?), thus it was rewritten piece
## by piece until only the license remains.
##
## Note that there *are* important things in ~/.var/app/<appname> (settings
## and data), so if you think you may *ever* use a Flatpak app again, it
## would be best to leave them there unless you're confident it's been saved
## elsewhere.
##
## [1]: https://github.com/giantpinkrobots/flatsweep
##
import os
import glob
import logging
import collections
from os import listdir
if os.getenv('FLATCRAP_DEBUG'):
logging.getLogger().setLevel(logging.DEBUG)
elif os.getenv('FLATCRAP_VERBOSE'):
logging.getLogger().setLevel(logging.INFO)
CLEANUP_CANDIDATES = [
"/var/lib/flatpak/app",
os.path.expanduser("~/.local/share/flatpak/app"),
]
class FlatCrap:
def __init__(self, cleanup_candidates=None):
var_app_list = []
installed_apps = set()
var_app_dir = os.path.expanduser("~/.var/app")
self.orphaned_data_dirs = {}
if cleanup_candidates is None:
cleanup_candidates = CLEANUP_CANDIDATES
for p in cleanup_candidates:
if os.path.exists(p):
installed_apps |= set(listdir(p))
logging.info("List of installed flatpaks: %s", installed_apps)
if not os.access(var_app_dir, os.R_OK | os.W_OK | os.X_OK):
logging.warning("User has no '~/.var/app' directory or it's "
"unreadable. Nothing to do.")
return
for path in os.scandir(var_app_dir):
if not path.is_dir():
logging.warning("Extraneous file ~/.var/app/%s", path.name)
continue
logging.debug("Found ~/.var/app/%s", path.name)
var_app_list.append(path.name)
for app in var_app_list:
if app in installed_apps:
logging.debug("Not considering %s as it's still installed", app)
continue
orphaned_app_path = os.path.join(var_app_dir, app, '**')
logging.info("Considering '%s' as %s is not installed",
orphaned_app_path, app)
orphaned_app_data_size = 0
for path in glob.glob(orphaned_app_path, recursive=True):
if not os.path.isfile(path):
continue
orphaned_app_data_size += os.stat(path).st_size
orphan = { app: orphaned_app_data_size }
logging.info("Orphaned data: %s", orphan)
self.orphaned_data_dirs.update(orphan)
self._sort_orphaned_data_by_size()
def _sort_orphaned_data_by_size(self, reverse=True):
_sorted = sorted(self.orphaned_data_dirs.items(), key=lambda x: x[1],
reverse=reverse)
# Since Python 3.7, I think CPython dicts remember insertion order, but
# that's "an implementation detail" so we can't rely on it
self.orphaned_data_dirs = collections.OrderedDict(_sorted)
@classmethod
def humansize(cls, bytesize):
""" Divide by 1024 until we get a number < 1000 and return that """
exponent = 0
units = ['bytes', 'KB', 'MB', 'GB']
while bytesize > 1024:
exponent += 1
bytesize /= 1024
return bytesize, units[exponent]
@property
def orphaned_data_size(self):
return sum([self.orphaned_data_dirs[s]
for s in self.orphaned_data_dirs])
if __name__ == '__main__':
fs = FlatCrap()
print("""
The following Flatpak apps are no longer installed. If you don't plan on using
them again, the corresponding directories inside '~/.var/app' could be removed:
""")
for d in fs.orphaned_data_dirs:
print("- {} ({:0.1f} {})"
.format(d, *fs.humansize(fs.orphaned_data_dirs[d])))
print("\nTotal orphaned data size in ~/.var/app: {:0.1f} {}\n"
.format(*fs.humansize(fs.orphaned_data_size)))
@ernstki
Copy link
Author

ernstki commented Oct 17, 2025

Installation

If you already use other Python programs on your system, you will probably have a ~/.local/bin and it will probably already be in your path. So this will work.

SCRIPT=https://gist.github.com/ernstki/f3e279e8a050c2df94e9fcfd69d67c2f/raw/flatpak-orphans.py
mkdir -p ~/.local/bin && pushd ~/.local/bin
wget -O flatpak-orphans $SCRIPT || curl -Lo flatpak-orphans $SCRIPT
chmod a+x flatpak-orphans
popd

Log out and back in or source your login scripts if it's not immediately in your search path (check with which flatcrap). Otherwise, it's good to know how to modify PATH variable on a Unix/Linux system anyway.

Example output

$ flatpak-orphans

The following Flatpak apps are no longer installed. If you don't plan on using
them again, the corresponding directories inside '~/.var/app' could be removed:

- io.elementary.screenshot (9.6 MB)
- com.gitlab.newsflash (9.4 MB)
- io.elementary.music (8.4 MB)
- re.sonny.Playhouse (7.6 MB)
- com.github.alexkdeveloper.relaxator (7.5 MB)
- com.github.alexkdeveloper.raddiola (5.7 MB)
- fyi.zoey.Boop-GTK (4.9 MB)
- org.qownnotes.QOwnNotes (4.7 MB)
- com.github.johnfactotum.Foliate (2.1 MB)
- com.github.johnfactotum.QuickLookup (1.7 MB)
- org.remmina.Remmina (154.4 KB)
- com.github.jmoerman.goforit (1.2 KB)
- org.mozilla.firefox (0.0 bytes)

Total leftover data size in ~/.var/app: 61.6 MB

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