Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 69 additions & 3 deletions nixos/modules/services/desktops/flatpak.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,86 @@ in other cases, you will need to add something like the following to your
}
```

Then, you will need to add a repository, for example,
## Declarative Flatpak management {#module-services-flatpak-declarative-manage}
NixOS now supports integrated management of Flatpak packages. You can specify
Flatpak packages to be installed or removed through your {file}`configuration.nix`:
```nix
{
services.flatpak = {
enable = true;
remotes = [
{
name = "flathub";
url = "https://flathub.org/repo/flathub.flatpakrepo";
}
{
name = "gnome";
url = "https://sdk.gnome.org/repo/flatpak/gnome-nightly.flatpakrepo";
}
];
packages = [ "org.blender.Blender" "net.ankiweb.Anki" ];
removeUnmanagedPackages = true;
removeUnmanagedRemotes = true;
update = {
auto = {
enable = true;
onCalendar = "weekly";
};
duringBuild = false;
};
};
}
```

This example configuration will:
- Enable Flatpak
- Add the specified Flatpak remotes (Flathub and GNOME nightly in this example)
- Ensure the specified packages are installed (Blender and Anki in this example)
- Remove any Flatpak packages not listed in the `packages` option (if `removeUnmanagedPackages` is true)
- Remove any Flatpak remotes not listed in the `remotes` option (if `removeUnmanagedRemotes` is true)
- Enable automatic weekly updates of Flatpak packages
- Disable package updates when building your system

Important notes:
- Package versions are managed by Flatpak's servers, not nixpkgs.
- The `removeUnmanagedPackages` option defaults to `false`.
- The `removeUnmanagedRemotes` option defaults to `false`.
- The `update.auto.enable` option defaults to `false`. When set to `true`, it allows automatic updates of installed Flatpak packages.
- The `update.auto.onCalendar` option uses systemd calendar syntax. If not set, no timer will be created even if `update.auto.enable` is `true`.
- The `update.duringBuild` option defaults to `false`. When set to `true`, it updates Flatpak packages during system rebuild.
- The `remotes` option defaults to including Flathub, the main repository for Flatpak applications.

Be aware that while Flatpak packages are managed through NixOS, they operate in a somewhat separate environment. If you encounter issues with a Flatpak package:
- First, check if the issue is related to NixOS configuration, such as missing portals or system dependencies.
- If the problem seems to be with the Flatpak package itself, then it's best to check with the upstream project.

Remember that the NixOS Flatpak integration might also have its own quirks, so consider if the problem could be related to how NixOS is managing Flatpak when troubleshooting.

## Manual Flatpak management {#module-services-flatpak-manual-manage}
If you prefer to manage Flatpak packages manually, you can still do so.

First, you will need to add a repository, for example,
[Flathub](https://github.com/flatpak/flatpak/wiki),
either using the following commands:

```ShellSession
$ flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
$ flatpak update
```
or by opening the
[repository file](https://flathub.org/repo/flathub.flatpakrepo) in GNOME Software.

Finally, you can search and install programs:
Then, you can search and install programs:

```ShellSession
$ flatpak search bustle
$ flatpak install flathub org.freedesktop.Bustle
$ flatpak run org.freedesktop.Bustle
```
Again, GNOME Software offers graphical interface for these tasks.

GNOME Software and KDE Discover offer a graphical interface for these tasks.

Note: When using NixOS-integrated package management:
- Manually installed packages may be removed if `removeUnmanagedPackages` is set to `true` and the package is not listed in the `packages` option.
- Manually added remotes may be removed if `removeUnmanagedRemotes` is set to `true` and the remote is not listed in the `remotes` option.
- If automatic updates are disabled, you will need to manually update your Flatpak packages using the `flatpak update` command.
192 changes: 188 additions & 4 deletions nixos/modules/services/desktops/flatpak.nix
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# flatpak service.
{
config,
lib,
Expand All @@ -8,8 +7,49 @@

let
cfg = config.services.flatpak;
in

flatpakCommand = "${cfg.package}/bin/flatpak";
Comment thread
zyhe16 marked this conversation as resolved.

manageFlatpaks = pkgs.writeShellScript "manage-flatpaks" ''
set -eou pipefail

${lib.optionalString (cfg.remote != null) ''
if ! ${flatpakCommand} remotes | grep -q "^${cfg.remote.name}"; then
echo "Adding Flatpak remote: ${cfg.remote.name}"
${flatpakCommand} remote-add --if-not-exists "${cfg.remote.name}" "${cfg.remote.url}"
else
echo "Flatpak remote already exists: ${cfg.remote.name}"
fi
''}

echo "Installing specified Flatpak packages..."
for pkg in ${toString cfg.packages}; do
if ! ${flatpakCommand} info "$pkg" &>/dev/null; then
echo "Installing Flatpak package: $pkg"
${flatpakCommand} install --assumeyes "$pkg"
else
echo "Flatpak package already installed: $pkg"
fi
done

${lib.optionalString cfg.removeUnmanagedPackages ''
echo "Removing unmanaged Flatpak packages..."
installed_pkgs=$(${flatpakCommand} list --app --columns=application)
for pkg in $installed_pkgs; do
if ! echo "${toString cfg.packages}" | grep -q "$pkg"; then
echo "Removing Flatpak package: $pkg"
${flatpakCommand} uninstall --assumeyes "$pkg"
fi
done
''}

${lib.optionalString cfg.update.duringBuild ''
echo "Updating all Flatpak packages..."
${flatpakCommand} update --assumeyes
''}
'';

in
{
meta = {
doc = ./flatpak.md;
Expand All @@ -22,12 +62,90 @@ in
enable = lib.mkEnableOption "flatpak";

package = lib.mkPackageOption pkgs "flatpak" { };

packages = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
example = [
"org.blender.Blender"
"net.ankiweb.Anki"
];
description = "List of Flatpak packages to install or update.";
};

removeUnmanagedPackages = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to remove Flatpak packages not listed in 'packages'.";
};

update = {
auto = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to automatically update Flatpak packages.";
};
onCalendar = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = "weekly";
description = ''
When to perform automatic updates. Uses systemd calendar format. If null, no timer will be created.
See systemd.time for more information on the calendar event syntax.
'';
};
};
duringBuild = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to update Flatpak packages during system rebuild.";
};
};
Comment on lines +99 to +104

@Zocker1999NET Zocker1999NET Feb 15, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I’m not wrong, this option is wrongly named and IMO also does currently not do what you probably intended to do.

  1. duringBuild implies updating the flatpak packages during building of the system configuration. However, building != activating. In the current implementation, a better name would be duringActivation.

    • Building is what e.g. nixos-rebuild build does. That does not actually change your system, it just builds the config & stores the result into the Nix store.
    • The difference triggered by this option is in the activation of the configuration. E.g. nixos-rebuild switch does activate the new system config (after also building it, if required).
  2. And that’s the bigger problem. Activation does not only happen on switching from one config to the next. On every boot, the config selected from the bootloader is also activated (as documented in the option system.activationScripts). Hence the proper description would be "Whether to update Flatpak packages on each reboot or switch.".

If the second point is unattended, a check needs to implemented to only execute the update when the config is actually switched. In my own configs, I have an example activationScript which is only executed on switches, see this line.


remotes = lib.mkOption {
type = lib.types.listOf (
lib.types.submodule {
options = {
name = lib.mkOption {
type = lib.types.str;
description = "Name of the Flatpak remote.";
};
url = lib.mkOption {
type = lib.types.str;
description = "URL of the Flatpak remote.";
};
};
}
);
default = [
{
name = "flathub";
url = "https://flathub.org/repo/flathub.flatpakrepo";
}
];
example = [
{
name = "flathub";
url = "https://flathub.org/repo/flathub.flatpakrepo";
}
{
name = "gnome";
url = "https://sdk.gnome.org/repo/flatpak/gnome-nightly.flatpakrepo";
}
];
description = "List of Flatpak remotes to add. By default, includes Flathub.";
};

removeUnmanagedRemotes = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to remove Flatpak remotes not listed in 'remotes'.";
};
};
};

###### implementation
config = lib.mkIf cfg.enable {

assertions = [
{
assertion = (config.xdg.portal.enable == true);
Expand All @@ -54,6 +172,72 @@ in
"/var/lib/flatpak/exports"
];

# Activation script for installation and optionally updating during build
system.activationScripts.flatpak-setup = ''
Comment thread
zyhe16 marked this conversation as resolved.
set -eou pipefail

# Get list of configured remote names
configured_remotes=""
for remote in ${toString cfg.remotes}; do
remote_name=$(echo $remote | jq -r .name)
configured_remotes="$configured_remotes $remote_name"
done

${lib.optionalString cfg.removeUnmanagedRemotes ''
# Remove remotes that are not in the config
for existing_remote in $(${flatpakCommand} remotes | cut -f1); do
if ! echo "$configured_remotes" | grep -q " $existing_remote "; then
echo "Removing unconfigured Flatpak remote: $existing_remote"
${flatpakCommand} remote-delete --force "$existing_remote"
fi
done
''}

# Add or update configured remotes
for remote in ${toString cfg.remotes}; do
remote_name=$(echo $remote | jq -r .name)
remote_url=$(echo $remote | jq -r .url)
if ! ${flatpakCommand} remotes | grep -q "^$remote_name"; then
echo "Adding Flatpak remote: $remote_name"
${flatpakCommand} remote-add --if-not-exists "$remote_name" "$remote_url"
else
echo "Flatpak remote already exists: $remote_name"
# Update the remote URL in case it changed
${flatpakCommand} remote-modify --url "$remote_url" "$remote_name"
fi
done

echo "Installing specified Flatpak packages..."
${manageFlatpaks}
'';

# Systemd service for automatic updates
systemd.services.flatpak-update = lib.mkIf cfg.update.auto.enable {
description = "Flatpak package updates";
wantedBy = [ "multi-user.target" ];
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
path = [ cfg.package ];
script = ''
${flatpakCommand} update --assumeyes
'';
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
};

systemd.timers.flatpak-update =
lib.mkIf (cfg.update.auto.enable && cfg.update.auto.onCalendar != null)
{
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = cfg.update.auto.onCalendar;
Persistent = true;
Unit = "flatpak-update.service";
};
};

# It has been possible since https://github.com/flatpak/flatpak/releases/tag/1.3.2
# to build a SELinux policy module.

Expand Down