Declarative flatpak manager for NixOS inspired by declarative-flatpak and nix-darwin's homebrew module. NixOs and home-manager modules are provided for system wide or user flatpaks installation.
We use Git tags and branches to manage versions:
0.7.0→ Current stable release.latest→ Always points to the most recent stable version (0.7.0as of now).main→ Unstable, development branch.
This project is released as a flake, and is published on flakehub and flakestry.
This project was inspired by Martin Wimpress' Blending NixOS with Flathub for friends and family talk at NixCon 2023.
For implementation details and info about how this project came to be, see my Flatpaks The Nix Way talk at NixCon 2025. slides.
nix-flatpak follows a
convergent mode
approach to package management (described in
this thread):
the target system state description is not exhaustive, and there's room for
divergence across builds and rollbacks. For a number of desktop applications I
want to be able to track the latest version, or allow them to auto update. For
such applications, a convergent approach is a reasonable tradeoff wrt system
reproducibility. YMMV.
Flatpak applications are installed by systemd oneshot service triggered at system activation. Depending on the number of applications to install, this could increase activation time significantly.
Releases are tagged with semantic versioning. Versions
below 1.0.0 are considered early, development, releases. Users can track a
version by passing its release tag as ref
...
nix-flatpak.url = "github:gmodena/nix-flatpak/?ref=v0.7.0";
...The latest tag will always point to the most recent release.
...
nix-flatpak.url = "github:gmodena/nix-flatpak/?ref=latest";
...The main branch is considered unstable, and might break installs.
...
nix-flatpak.url = "github:gmodena/nix-flatpak/";
...Flakes are the recommended and officially supported installation method, but
under the hood nix-flatpak is just a Nix module.
nix-flatpak is not available in channels and needs to be manually downloaded
and imported. Nixpkgs users can use the
fetchFromGithub
fetcher:
pkgs.fetchFromGitHub {
owner = "gmodena";
repo = "nix-flatpak";
rev = "v0.7.0";
hash = "sha256-7ZCulYUD9RmJIDULTRkGLSW1faMpDlPKcbWJLYHoXcs=";
};
The package hash can be generated with:
nix-prefetch-github gmodena nix-flatpak --rev <rev>Example:
let
# Fetch nix-flatpak
nix-flatpak = pkgs.fetchFromGitHub {
owner = "gmodena";
repo = "nix-flatpak";
rev = "v0.7.0";
hash = "sha256-7ZCulYUD9RmJIDULTRkGLSW1faMpDlPKcbWJLYHoXcs=";
};
in
imports = [
# Import the nix-flatpak NixOS module and install applications system wide.
# HomeManager users should import `${nix-flatpak}/modules/home-manager.nix`
# where appropriate.
"${nix-flatpak}/modules/nixos.nix"
];
# ... your config
# Configure nix-flatpak
services.flatpak = {
enable = true;
packages = [
"org.mozilla.firefox"
];
};A minimal config that can be built and tested in a NixOS VM (via nix-build)
can be found at
configuration.nix.
You can find an example configuration in testing-base/flatpak.nix.
Enable flatpak in configuration.nix:
services.flatpak.enable = true;Import the module (nixosModules.nix-flatpak or
homeManagerModules.nix-flatpak). Using flake, installing nix-flatpak as a
NixOs module would look something like this:
{
inputs = {
# ...
nix-flatpak.url = "github:gmodena/nix-flatpak"; # unstable branch. Use github:gmodena/nix-flatpak/?ref=<tag> to pin releases.
};
outputs = { nix-flatpak, ... }: {
nixosConfigurations.<host> = nixpkgs.lib.nixosSystem {
modules = [
nix-flatpak.nixosModules.nix-flatpak
./configuration.nix
];
};
};
}See
flake.nix
in testing-base
for examples of setting up nix-flatpak as a NixOs and HomeManager module.
Depending on how config and inputs are derived homeManagerModules import can
be flaky. Here's an example of how homeManagerModules is imported on my nixos
systems config in
modules/home-manager/desktop/nixos/default.nix.
flake-inputs is a special extra arg set in the repo flake.nix
mkNixosConfiguration.
By default nix-flatpak will add the flathub remote. Remotes can be manually
configured via the services.flatpak.remotes option:
services.flatpak.remotes = [{
name = "flathub-beta"; location = "https://flathub.org/beta-repo/flathub-beta.flatpakrepo";
}];Note that this declaration will override the default remote config value
(flathub). If you want to keep using flathub, you should explicitly declare
it in the services.flatpak.remotes option.
Alternatively, it is possible to merge declared remotes with the default one
with lib.mkDefaultOption.
services.flatpak.remotes = lib.mkOptionDefault [{
name = "flathub-beta";
location = "https://flathub.org/beta-repo/flathub-beta.flatpakrepo";
}];Declare packages to install with:
services.flatpak.packages = [
{ appId = "com.brave.Browser"; origin = "flathub"; }
"com.obsproject.Studio"
"im.riot.Riot"
];You can pin a specific commit setting commit=<hash> attribute.
Rebuild your system (or home-manager) for changes to take place.
Flatpakref
files can be installed by setting the flatpakref attribute to :
services.flatpak.packages = [
{ flatpakref = "<uri>"; sha256="<hash>"; }
];A sha256 hash is required for the flatpakref file. This can be generated with
nix-prefetch-url <uri>. Omitting the sha256 attribute will require an
impure evaluation of the flake.
When installing an application from a flatpakref, the application remote will
be determined as follows:
- If the package does not specify an origin, use the remote name suggested by
the flatpakref in
SuggestRemoteName. - If the flatpakref does not suggest a remote name, sanitize the flatpakref
Namekey with the same algo flatpak implements in create_origin_remote_config().
Bundle files (.flatpak) can be installed directly by pointing bundle at a
URI and providing a matching sha256:
services.flatpak.packages = [
rec {
appId = "<appId>";
sha256 = "<hash>";
bundle = "${pkgs.fetchurl {
url = "<bundle-uri>";
inherit sha256;
}}";
}
];You can also install a local bundle from disk:
services.flatpak.packages = [
{
bundle = "file:///path/to/local/app.flatpak";
appId = "<appId>";
sha256 = "<hash>";
}
];By default nix-flatpak will only manage (install/uninstall/update) packages
declared in the services.flatpak.packages and repositories declared in
services.flatpak.remotes. Flatpak packages and repositories installed by the
command line of app stores won't be affected.
Set services.flatpak.uninstallUnmanaged = true to alter this behaviour, and
have nix-flatpak manage the lifecycle of all flatpaks packages and
repositories.
Note that services.flatpak.uninstallUnmanaged will only affect a given
system of user installation target. If nix-flatpak is installed as a
HomeManager module all packages/remotes will be managed in a user
installation. Packages/remotes installed system-wide won't be affected by
services.flatpak.uninstallUnmanaged.
Similarly, when nix-flatpak is installed as a NixOs module, only system-wide
config will be affected.
Set
services.flatpak.update.onActivation = true;to enable updates at system activation. The default is false so that repeated
invocations of nixos-rebuild switch are idempotent. Applications pinned to a
specific commit hash will not be updated.
Periodic updates can be enabled by setting:
services.flatpak.update.auto = {
enable = true;
onCalendar = "weekly"; # Default value
};Auto updates trigger on system activation.
Under the hood, updates are scheduled by realtime systemd timers. onCalendar
accepts systemd's update.auto.onCalendar expressions. Timers are persisted
across sleep / resume cycles. See
https://wiki.archlinux.org/title/systemd/Timers for more information.
Flatpaks are stored out of nix store at /var/lib/flatpak and
${HOME}/.local/share/flatpak/ for system (nixosModules) and user
(homeManagerModules) installation respectively. Flatpaks installation are not
generational: upon a system rebuild and rollbacks, changes in packages
declaration will result in downloading applications anew.
Keeping flatpaks and nix store orthogonal is an explicit design choice, dictate by my use cases:
- I want to track the latest version of all installed applications.
- I am happy to trade network for storage.
YMMV.
If you want an alternative approach with transactional package installation, declarative-flatpak might be a better fit.
Flatpak overrides allow you to modify application permissions and environment
variables. nix-flatpak provides two ways to manage overrides: inline
settings and external files.
WARNING: using these settings will overwrite system/user flatpak override
files and could cause data loss. Backup your files before enabling .settings
and/or .files.
Use services.flatpak.overrides.settings to declare overrides directly in your
nix configuration:
{
services.flatpak.overrides.settings = {
global = {
# Force Wayland by default
Context.sockets = ["wayland" "!x11" "!fallback-x11"];
Environment = {
# Fix un-themed cursor in some Wayland apps
XCURSOR_PATH = "/run/host/user-share/icons:/run/host/share/icons";
# Force correct theme for some GTK apps
GTK_THEME = "Adwaita:dark";
};
};
"com.visualstudio.code".Context = {
filesystems = [
"xdg-config/git:ro" # Expose user Git config
"/run/current-system/sw/bin:ro" # Expose NixOS managed software
];
sockets = [
"gpg-agent" # Expose GPG agent
"pcsc" # Expose smart cards (i.e. YubiKey)
];
};
"org.onlyoffice.desktopeditors".Context.sockets = ["x11"]; # No Wayland support
};
}Backwards compatibility: The legacy (nix-flatpak <= 0.7) format
services.flatpak.overrides = { "app.id" = {...}; } (without the .settings
wrapper) is still supported, but will be removed in the future.
Use services.flatpak.overrides.files to load overrides from external INI
files. This is useful for sharing override configurations across machines,
managing complex overrides separately from your nix configuration and/or using
existing Flatpak override files. See
[https://github.com/gmodena/nix-flatpak/issues/82][!82].
The file name must match the application ID (e.g., com.visualstudio.code).
Files should be in standard Flatpak override INI format:
[Context]
sockets=wayland;!x11;
[Environment]
GTK_THEME=Adwaita:darkWhen both settings and files are specified for the same application, they
are merged. Values from settings take precedence over values from files, and
both are merged with any externally applied overrides already on disk.
{
services.flatpak.overrides = {
# Base configuration from file
files = [ "/path/to/overrides.d/com.visualstudio.code" ];
# Additional settings merged on top
settings."com.visualstudio.code".Context.sockets = ["gpg-agent"];
};
}The writeMode option controls how nix-flatpak writes override files to disk.
It accepts two values:
"merge"(default): at activation time, existing override files on disk are read and merged with the nix-managed settings. Direct user edits to keys not declared in nix are preserved across generations."replace": the complete override file for each application is computed at evaluation time as a pure store derivation. At activation time the precomputed file is copied to the overrides directory.nix-flatpakfully owns the file. Any direct user edits are overwritten on the next activation.
{
services.flatpak.overrides.writeMode = "merge"; # default
}When overrides.files contains plain string paths (paths outside the Nix
store), nix-flatpak must read them from the filesystem at evaluation time,
which is an impure operation. This applies to both write modes, because the
state file always parses overrides.files at eval time regardless of
writeMode.
Such configurations require passing --impure to nixos-rebuild:
{
services.flatpak.overrides = {
files = [
"/home/user/dotfiles/overrides/com.visualstudio.code"
];
};
}sudo nixos-rebuild switch --impureTo avoid --impure, use Nix path literals instead of strings for
overrides.files. Path literals are copied into the Nix store during
evaluation, making the entire configuration reproducible:
{
services.flatpak.overrides = {
files = [
./overrides/com.visualstudio.code
./overrides/org.gnome.Terminal
];
};
}With writeMode = "replace" and store-path files, the merged override content
for each application is fully determined at evaluation time. No reading of
on-disk override files occurs at activation. This is the most reproducible
configuration.
Note: writeMode = "replace" with only settings (no files) is always pure
and never requires --impure.
Bugs of incorrect use of this option can result in data loss. Make sure your overrides are backed up before enabling this option.
By default, when you remove an override from your nix configuration, the
corresponding file in the flatpak overrides directory is preserved on disk. This
behaviour is controlled by the pruneUnmanagedOverrides option:
{
services.flatpak.overrides.pruneUnmanagedOverrides = false; # Default
}When pruneUnmanagedOverrides = true, nix-flatpak scans the actual overrides
directory and removes any files that are not declared in the current
configuration (either in settings or files). This ensures a clean state
where only managed overrides exist.
If you previously had pruneUnmanagedOverrides = false, switching to
pruneUnmanagedOverrides = true will delete all override files removed from the
current config that were present in the previous generation. If override files
accumulated over previous generations, a manual cleanup of all unmanaged files
will be necessary.
nix-flatpak uses an internal state file to track managed overrides across
activations. Releases > 0.7.0 support overrides.settings and
overrides.files write a v2 state file. If you downgrade to an older release
that only understands the v1 flat overrides format, the older release will
misinterpret its meta-keys (settings, files, _fileSettings,
pruneUnmanagedOverrides, writeMode) as Flatpak app ids.
On the first activation after the downgrade, the older release will create
spurious override files with those names in your flatpak overrides directory
(/var/lib/flatpak/overrides or ~/.local/share/flatpak/overrides). These
files have no effect (Flatpak ignores filenames that are not valid
reverse-domain app IDs) but they are left on disk as clutter.
Your real app override files are not affected. To clean up the stray files, run:
# For a user (home-manager) installation
for name in settings files _fileSettings pruneUnmanagedOverrides writeMode; do
rm -f "${XDG_DATA_HOME:-$HOME/.local/share}/flatpak/overrides/$name"
done
# For a system (NixOS) installation
for name in settings files _fileSettings pruneUnmanagedOverrides writeMode; do
sudo rm -f "/var/lib/flatpak/overrides/$name"
doneOn flaky network connections the nix-flatpak installer script may fail.
The restartOnFailure option controls the behavior of the
flatpak-managed-install service when it encounters a failure. As of 0.6.0 it is
enabled by default, and wraps systemd APIs. It's configuration can be modified
by setting:
restartOnFailure = {
enable = true;
restartDelay = "60s";
exponentialBackoff = {
enable = false;
steps = 10;
maxDelay = "1h";
};
};See module/options.nix and systemd's own documentation for more details.
A couple of things to be aware of when working with nix-flatpak.
Users have reported an infinite recursion stacktrace when importing an home-manager module outside of where home-manager itself was imported.
To work around this issue, you should avoid nesting home-manager modules. This means that when you are structuring your configuration, make sure that you do not have home-manager modules imported within each other in a way that could lead to circular imports.
You can follow the discussions and updates on issue #25 to stay informed about any resolutions or workarounds.
For further support questions and common issues on how to integrate and set up
nix-flatpak on your system, please consult the
Q&A discussions page.