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
147 changes: 134 additions & 13 deletions docs/manual/usage/gpu-non-nixos.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,131 @@
# GPU on non-NixOS systems {#sec-usage-gpu-non-nixos}

To access the GPU, programs need access to OpenGL and Vulkan libraries. While
this works transparently on NixOS, it does not on other Linux systems. A
solution is provided by [NixGL](https://github.com/nix-community/nixGL), which
can be integrated into Home Manager.
this works transparently on NixOS, it does not on other Linux systems. There are
two options:

1. Recommended: modify the host system slightly so that the graphics libraries
can be found where programs from Nixpkgs can find them.
2. Wrap programs from Nixpkgs in an environment which tells them where to find
graphics libraries.

The first option is very clean because the needed modifications to the host OS
are small. However, it does require root/sudo access to the system, which may
not be available. The second approach avoids that. However, it injects libraries
from Nixpkgs into the environment of wrapped programs, which can make it
impossible to launch programs of the host OS from wrapped programs.


## When sudo is available: fixing the host OS {#sec-usage-gpu-sudo}

The {option}`targets.genericLinux.gpu` module is automatically enabled whenever
the option {option}`targets.genericLinux.enable` is set (unless
[NixGL](#sec-usage-gpu-nosudo) is used instead), which is recommended for
non-NixOS Linux distributions in any case. The module can also be explicitly
enabled by setting {option}`targets.genericLinux.gpu.enable`.

This module builds a directory containing GPU libraries. When activating the
home configuration by `home-manager switch`, the host system is examined: for
compatibility with NixOS, these libraries need to be placed in
`/run/opengl-driver`. If this directory does not exist, or contains a different
set of libraries, the activation script will print a warning such as

```text
Activating checkExistingGpuDrivers
GPU drivers require an update, run
sudo /nix/store/HASH-non-nixos-gpu/bin/non-nixos-gpu-setup
```

Because the `/run` directory is volatile and disappears on reboot, libraries
cannot be simply copied or linked there. The `non-nixos-gpu-setup` script
installs a Systemd service which ensures that the drivers are linked to
`/run/opengl-driver` on boot. Home Manager will always check and warn you when
this setup needs to be refreshed.

If you ever wish to uninstall these drivers, all you need to do is

```sh
sudo rm /run/opengl-driver
sudo systemctl disable --now non-nixos-gpu.service
sudo rm /etc/systemd/system/non-nixos-gpu.service
```


### GPU offloading {#sec-usage-gpu-offloading}

You can use the {option}`targets.genericLinux.nixGL.prime.installScript` option.
It installs the `prime-offload` script which is configured through options under
{option}`targets.genericLinux.nixGL.prime`. This functionality is independent
from the rest of NixGL and can be used when
{option}`targets.genericLinux.nixGL.packages` is left `null`, which it should be
when using drivers from `/run/opengl-driver`.


### Nvidia drivers {#sec-usage-gpu-nvidia}

If you need to include the proprietary Nvidia drivers, the process is a bit more
involved. You need to:

1. Determine the exact version used by the host system. Example: `550.163.01`

1. Fetch that version of the drivers from Nvidia and calculate their hash.
Example:

```sh
nix store prefetch-file \
https://download.nvidia.com/XFree86/Linux-x86_64/550.163.01/NVIDIA-Linux-x86_64-550.163.01.run
```

Attention: the version and architecture are present twice. If you are on an
ARM system, replace `x86_64` with `aarch64`.

1. Put this information into your home configuration. Example:

```nix
targets.genericLinux.gpu.nvidia = {
enable = true;
version = "550.163.01";
sha256 = "sha256-74FJ9bNFlUYBRen7+C08ku5Gc1uFYGeqlIh7l1yrmi4=";
};
```

::: {.warning}
The Nvidia driver version **must** match the host system. This means that you
must pay attention when upgrading the system and update the home configuration
as well.
:::


## No root access: wrapping programs {#sec-usage-gpu-nosudo}

The wrapping approach is facilitated by
[NixGL](https://github.com/nix-community/nixGL), which can be integrated into
Home Manager.

::: {.warning}

This approach can cause issues when a wrapped program from Nixpkgs executes a
program from the host. For example, Firefox from Nixpkgs must be wrapped by
NixGL in order for graphical acceleration to work. If you then download a PDF
file and open it in a PDF viewer that is not installed from Nixpkgs but is
provided by the host distribution, there may be issues. Because Firefox's
environment injects libraries from NixGL, they are inherited by the PDF viewer,
and unless they are the same or compatible version as the libraries on the host,
the viewer will not work. This problem manifests more often with Vulkan because
it needs a larger set of injected libraries than OpenGL.

The problem typically manifests with errors similar to

```text
/nix/store/HASH-gcc-12.3.0-lib/lib/libstdc++.so.6: version `GLIBCXX_3.4.31' not found
```

:::

To enable the integration, import NixGL into your home configuration, either as
a channel, or as a flake input passed via `extraSpecialArgs`. Then, set the
`nixGL.packages` option to the package set provided by NixGL.
`targets.genericLinux.nixGL.packages` option to the package set provided by
NixGL.

Once integration is enabled, it can be used in two ways: as Nix functions for
wrapping programs installed via Home Manager, and as shell commands for running
Expand Down Expand Up @@ -35,10 +153,11 @@ different hardware. There is also the `config.lib.nixGL.wrapOffload` alias for
two-GPU systems.

Another convenience is that all wrapper functions are always available. However,
when `nixGL.packages` option is unset, they are no-ops. This allows them to be
used even when the home configuration is used on NixOS machines. The exception
is the `prime-offload` script which ignores `nixGL.packages` and is installed
into the environment whenever `nixGL.prime.installScript` is set. This script,
when `targets.genericLinux.nixGL.packages` option is unset, they are no-ops.
This allows them to be used even when the home configuration is used on NixOS
machines. The exception is the `prime-offload` script which ignores
`targets.genericLinux.nixGL.packages` and is installed into the environment
whenever `targets.genericLinux.nixGL.prime.installScript` is set. This script,
which can be used to start a program on a secondary GPU, does not depend on
NixGL and is useful on NixOS systems as well.

Expand All @@ -52,10 +171,12 @@ demonstration purposes.
```nix
{ config, lib, pkgs, nixgl, ... }:
{
nixGL.packages = nixgl.packages;
nixGL.defaultWrapper = "mesa";
nixGL.offloadWrapper = "nvidiaPrime";
nixGL.installScripts = [ "mesa" "nvidiaPrime" ];
targets.genericLinux.nixGL = {
packages = nixgl.packages;
defaultWrapper = "mesa";
offloadWrapper = "nvidiaPrime";
installScripts = [ "mesa" "nvidiaPrime" ];
};

programs.mpv = {
enable = true;
Expand All @@ -75,7 +196,7 @@ flake. When using channels, the example would instead begin with
```nix
{ config, lib, pkgs, ... }:
{
nixGL.packages = import <nixgl> { inherit pkgs; };
targets.genericLinux.nixGL.packages = import <nixgl> { inherit pkgs; };
# The rest is the same as above
...
```
6 changes: 6 additions & 0 deletions modules/lib/maintainers.nix
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,12 @@
github = "epixtm";
githubId = 168025607;
};
exzombie = {
name = "Jure Varlec";
email = "jure@varlec.si";
github = "exzombie";
githubId = 11456290;
};
fendse = {
email = "46252070+Fendse@users.noreply.github.com";
github = "Fendse";
Expand Down
13 changes: 13 additions & 0 deletions modules/misc/news/2025/10/2025-10-26_08-19-48.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{ pkgs, ... }:

{
time = "2025-10-26T07:19:48+00:00";
condition = pkgs.stdenv.hostPlatform.isLinux;
message = ''
A new module is available: `targets.genericLinux.gpu`

This module provides integration of GPU drivers for non-NixOS systems. It is a
simpler alternative to the existing `targets.genericLinux.nixGL` module. See the
Home Manager user manual for more information.
'';
}
1 change: 0 additions & 1 deletion modules/modules.nix
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ let
./misc/news.nix
./misc/nix-remote-build.nix
./misc/nix.nix
./misc/nixgl.nix
./misc/numlock.nix
./misc/pam.nix
./misc/qt.nix
Expand Down
3 changes: 3 additions & 0 deletions modules/targets/generic-linux.nix
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ in
"data"
]
)
(lib.mkRenamedOptionModule [ "nixGL" ] [ "targets" "genericLinux" "nixGL" ])
./generic-linux/nixgl.nix
./generic-linux/gpu
];

options.targets.genericLinux = {
Expand Down
147 changes: 147 additions & 0 deletions modules/targets/generic-linux/gpu/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# SPDX-FileCopyrightText: 2025 Jure Varlec <jure@varlec.si>
#
# SPDX-License-Identifier: MIT

{
config,
lib,
pkgs,
...
}:

{
options.targets.genericLinux.gpu =
let
inherit (lib)
mkOption
mkEnableOption
types
literalExpression
;
in
{
enable = mkOption {
type = types.bool;
default = config.targets.genericLinux.enable && config.targets.genericLinux.nixGL.packages == null;
defaultText = literalExpression "config.targets.genericLinux.enable && config.targets.genericLinux.nixGL.packages == null";
example = true;
description = "Whether to enable GPU driver integration for non-NixOS systems.";
};

packages = mkOption {
type = types.attrs;
default = pkgs;
defaultText = literalExpression "pkgs";
description = "The Nixpkgs package set where drivers are taken from.";
};

nvidia = {
enable = mkEnableOption "proprietary Nvidia drivers";

version = mkOption {
type = types.nullOr (types.strMatching "[0-9]{3}\\.[0-9]{2,3}\\.[0-9]{2}");
default = null;
example = literalExpression "550.163.01";
description = ''
The exact version of Nvidia drivers to use. This version **must**
match the version of the driver used by the host OS.
'';
};

sha256 = mkOption {
type = types.nullOr (types.strMatching "sha256-.*=");
default = null;
example = literalExpression "sha256-hfK1D5EiYcGRegss9+H5dDr/0Aj9wPIJ9NVWP3dNUC0=";
description = ''
The hash of the downloaded driver file. It can be obtained by
running, for example,

```sh
nix store prefetch-file https://download.nvidia.com/XFree86/Linux-x86_64/@VERSION@/NVIDIA-Linux-x86_64-@VERSION@.run
```

where `@VERSION@` is replaced with the exact driver version.
If you are on ARM, replace Linux-x86_64 with Linux-aarch64.
'';
};
};
};

config =
let
cfg = config.targets.genericLinux.gpu;

# This builds the driver archive downloaded from download.nvidia.com
nvidia =
(cfg.packages.linuxPackages.nvidiaPackages.mkDriver {
version = cfg.nvidia.version;
sha256_64bit = cfg.nvidia.sha256;
sha256_aarch64 = cfg.nvidia.sha256;
useSettings = false;
usePersistenced = false;
}).override
{
libsOnly = true;
kernel = null;
};

drivers = cfg.packages.callPackage ./gpu-libs-env.nix {
addNvidia = cfg.nvidia.enable;
nvidia_x11 = nvidia; # Only used if addNvidia is enabled
};

setupPackage = cfg.packages.callPackage ./setup {
non-nixos-gpu-env = drivers;
};

in
lib.mkIf cfg.enable {
assertions = lib.optionals cfg.nvidia.enable [
{
assertion = !isNull cfg.nvidia.version;
message = ''
Nvidia proprietary driver is enabled, version must be given.
Please set targets.genericLinux.gpu.nvidia.version.
'';
}
{
assertion = !isNull cfg.nvidia.sha256;
message = ''
Nvidia proprietary driver is enabled, driver hash must be given.
Please set targets.genericLinux.gpu.nvidia.sha256.
'';
}
];

warnings = lib.optional (config.targets.genericLinux.nixGL.packages != null) ''
Both targets.genericLinux.gpu and targets.genericLinux.nixGL are enabled.
This is an unsupported configuration. Only mix the two if you know what
you are doing!
'';

home.packages = [ setupPackage ];

home.activation.checkExistingGpuDrivers =
let
# Absolute path is needed for use with sudo which doesn't have the user's
# home environment.
setupPath = "${lib.getExe setupPackage}";
in
lib.hm.dag.entryAnywhere ''
existing=$(readlink /run/opengl-driver || true)
new=${drivers}
verboseEcho Existing drivers: ''${existing}
verboseEcho New drivers: ''${new}
if [[ -z "''${existing}" ]] ; then
warnEcho "This non-NixOS system is not yet set up to use the GPU"
warnEcho "with Nix packages. To set up GPU drivers, run"
warnEcho " sudo ${setupPath}"
elif [[ "''${existing}" != "''${new}" ]] ; then
warnEcho "GPU drivers require an update, run"
warnEcho " sudo ${setupPath}"
fi
'';
};

meta.maintainers = with lib.maintainers; [ exzombie ];
}
28 changes: 28 additions & 0 deletions modules/targets/generic-linux/gpu/gpu-libs-env.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# SPDX-FileCopyrightText: 2025 Jure Varlec <jure@varlec.si>
#
# SPDX-License-Identifier: MIT

{
lib,
buildEnv,
mesa,
libvdpau-va-gl,
intel-media-driver,
nvidia-vaapi-driver,
linuxPackages,
nvidia_x11 ? linuxPackages.nvidia_x11,
addNvidia ? false,
}:

buildEnv {
name = "non-nixos-gpu";
paths = [
mesa
libvdpau-va-gl
intel-media-driver
]
++ lib.optionals addNvidia [
nvidia_x11
nvidia-vaapi-driver
];
}
Loading
Loading