OFFSET偏移
← All articlesOFFSET / FIELD NOTESNO. 154

Wine and gaming environments on NixOS

Manage pinned software, Steam tool registration, Flatpak runtimes, and mutable prefixes as separate layers.

English translation · Code and commands follow the Chinese original.

IN THESE READING PATHSProton gaming, deployment, and troubleshooting ↗
About 6 min readStart reading

This chapter distinguishes software and system configuration managed by Nix from mutable runtime prefixes, downloads, and account data. Check the example options against the project's pinned nixpkgs; they are not universal configuration for every NixOS version.


Declarative vs. Dynamic: The Core Tension#

NixOS aims for reproducibility: pinned inputs and declarative configuration can determine build dependencies and managed system configuration, but builds, activation, and runtime state are different layers. Rebuilding does not automatically restore saves, downloads, accounts, and Prefixes to an identical state. Meanwhile, the default behavior of Wine/Proton is as follows:

  • winetricks downloads DLLs and runtimes from the network
  • Proton-GE fetches new versions from GitHub Releases
  • Registry changes inside a Prefix do not go through Nix

The strategy: separate the "engine layer" from the "environment layer". Manage the "engine" declaratively with Nix and the "environment" dynamically.

引擎层 (Engine)  →  Nix 声明式  →  锁定版本与声明,另外验证构建和激活
环境层 (Prefix)  →  动态管理    →  记录安装与变更,可备份/版本化部分状态

Nix Configuration for Steam and Proton#

Basic Module (Recommended Approach)

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

{
  # 允许本示例使用的 Steam unfree 包;NVIDIA 等需另按实际配置
  nixpkgs.config.allowUnfreePredicate = pkg:
    builtins.elem (lib.getName pkg) [
      "steam"
      "steam-unwrapped"
      "steam-original"
      "steam-run"
    ];

  programs.steam = {
    enable = true;
    remotePlay.openFirewall = false; # 只有明确需要 Remote Play 才单独评估开放
    localNetworkGameTransfers.openFirewall = false; # 按实际局域网传输需求配置
    protontricks.enable = true;

    # 注入 Proton-GE
    extraCompatPackages = with pkgs; [
      proton-ge-bin
    ];
  };

  # GameMode 优化调度
  programs.gamemode.enable = true;

  # 32 位字体缓存(部分游戏需要)
  fonts.fontconfig.cache32Bit = true;
}

Additional Runtime Packages for Steam

Some games need additional system libraries:

programs.steam.package = pkgs.steam.override {
  extraPkgs = pkgs': with pkgs'; [
    xorg.libXcursor
    xorg.libXi
    xorg.libXinerama
    xorg.libXScrnSaver
    libpng
    libpulseaudio
    libvorbis
    stdenv.cc.cc.lib
    libkrb5
    keyutils
  ];
};

Gamescope Microcompositor

Valve's microcompositor supports HDR, upscaling, and frame-rate limiting:

programs.gamescope = {
  enable = true;
  capSysNice = false;  # 示例不授予该 capability;按调度需求与安全边界选择
};

programs.steam.gamescopeSession.enable = true;

Managing the Wine Engine#

System-level Wine

environment.systemPackages = with pkgs; [
  wineWowPackages.full     # 功能集合;full 不表示自动含 staging 补丁
  # wineWowPackages.stable  # 仅 stable 版
  # wineWowPackages.staging # staging 版(含额外补丁)
  winetricks               # 依赖管理(仅用于手动调试)
];

The architectures and feature sets provided by wineWowPackages depend on how the pinned nixpkgs packages Wine. Check the stable, staging, and full definitions individually; the name full does not establish which patches or new WoW64 support are included. Steam's requirements for 32-bit graphics libraries also differ from those of ordinary Wine packages.

Set Environment Variables Per Application

Do not set WINEPREFIX or WINEARCH globally, or different applications, protontricks, and managers may accidentally share directories or use incompatible architectures. Choose debugging output for the situation as well: a global WINEDEBUG=-all hides evidence during troubleshooting.

# 临时针对一个独立前缀;不是 system-wide sessionVariables。
WINEPREFIX="$HOME/Games/MyTool" WINEDEBUG=+seh,+loaddll wine MyTool.exe

For a fixed desktop entry, a wrapper script can set an application-specific environment. Before migrating the architecture of an existing prefix, back it up and verify the procedure against the runner documentation.

Bottles Flatpak and Graphics Runtimes#

Installing Flatpak

Declare the Flatpak service in NixOS first; software acquisition and application data retain their own update and persistence scopes.

services.flatpak.enable = true;

The following example installs the application after the service has been activated through the project's workflow. This article does not execute it automatically:

flatpak install flathub com.usebottles.bottles

NVIDIA Graphics Extensions

NVIDIA requires Flatpak userspace GL extensions that match the host's kernel driver. First inspect the actual host driver, application runtime, and extensions. Do not blindly expose /run/opengl-driver or /sys, or force host ICD paths. Inspect the configuration and listings:

flatpak info com.usebottles.bottles
flatpak info --show-permissions com.usebottles.bottles
flatpak override --user --show com.usebottles.bottles
flatpak list --runtime

Check host-driver updates and user-runtime updates separately. After the extensions match, verify the actual GPU and components through the Bottle's graphics logs, rather than merely checking that driver files exist.

AMD/Intel Graphics Extensions

AMD/Intel typically obtain graphics libraries through the Flatpak runtime's Mesa/GL extensions, but still require device permissions and the necessary architectures. Do not hardcode the host's /usr/share/vulkan/icd.d/... into the sandbox environment: it may not exist there or may reference incompatible libraries.

Running vulkaninfo --summary on the host tests only the host. DXVK/VKD3D logs, HUDs, or tool results inside the Bottle describe that particular runtime environment. wine dxdiag offers clues, but does not by itself prove the actual hardware path or support for all Vulkan features.

Declarative Flatpak Overrides

Once permission requirements are established, user overrides can be included in the user's Home Manager configuration, avoiding a conflict between GUI management and declarative files. The following grants access only to one example game directory:

xdg.dataFile."flatpak/overrides/com.usebottles.bottles".text = ''
  [Context]
  filesystems=/mnt/games;
'';

This fragment belongs in the relevant user's Home Manager module. Verify xdg.dataHome against the path Flatpak actually reads. Inspect existing overrides before importing it and preserve permissions that are still needed. After activation, check with flatpak override --user --show, then actually launch the application. This article has not built or activated this configuration, and a successful declaration does not establish GPU availability.

Three-layer Strategy and Best Practices#

┌─────────────────────────────────┐
│      引擎层 (Nix 声明式)         │
│  wineWowPackages.full           │
│  proton-ge-bin (→ Steam)        │
│  版本锁定在 Flake.lock          │
└────────────┬────────────────────┘
             │ Runner 调用
┌────────────▼────────────────────┐
│      管理层 (Bottles Flatpak)    │
│  为每个 Windows 应用创建独立 Bottle│
│  图形运行时、依赖安装、状态管理    │
│  随 Flathub 更新                │
└────────────┬────────────────────┘
             │ 紧急尝鲜
┌────────────▼────────────────────┐
│    临时层 (ProtonUp-Qt)          │
│  仅在 nixpkgs 滞后时使用        │
│  验证后等待 nixpkgs 更新        │
└─────────────────────────────────┘

Principles of Layered Management

  1. Nix manages engines: pin the available software and module configuration, and record build, activation, and actual PATH/runner selection. This does not promise reproducibility of all runtime state.
  2. Steam manages game compatibility data: extraCompatPackages registers additional tools with Steam; it does not automatically install the same GE package into Bottles' runner directory.
  3. Bottles manages application state: use a separate environment for each application and record dependencies and runners. Flatpak updates, downloaded components, and Prefix backups still require separate maintenance.
  4. Diagnose the actual graphics stack: use matching Flatpak extensions and minimal permissions, without forcibly exposing host-driver directories.
  5. Dynamic tools have their own state: if you use download tools such as ProtonUp-Qt, document their versions, paths, verification, and relationship to the Nix configuration. Do not claim they are pinned by flake.lock.
  6. Tune the system based on evidence: do not open firewall access for remote connections by default, raise max_map_count/file-max across the board, or bypass system configuration by editing managed /etc files.

Configure sysctl and Resource Limits as Needed

Inspect current values first, then use logs to confirm the bottleneck:

cat /proc/sys/vm/max_map_count
cat /proc/sys/fs/file-max
ulimit -Sn
ulimit -Hn

fs.file-max is the system-wide file-handle limit, while ulimit -n is a process-related limit; they are not the same parameter. Only after confirming that the relevant limit causes a failure should you choose a target value supported by project evidence and declare it through a Nix module. Do not copy extremely large numbers as universal recommendations.

# 仅说明选项位置;validatedMapLimit 必须由实际诊断与项目配置提供。
# boot.kernel.sysctl."vm.max_map_count" = validatedMapLimit;

The source material's NixOS 25.05/24.11 scope may differ from the currently pinned inputs. This review of upstream Steam/Wine/fontconfig modules confirms only mechanisms and option names; it does not replace evaluation, building, activation, or game testing of the target flake.

Sources and Verification Boundaries#

The following references define the relevant mechanisms and support scope. Installation, building, activation, prefix state, and actual application behavior must be verified separately in the target environment. The example commands are not accompanied by any claim that they have run successfully on a host.

References & further reading

  1. Flatpak Graphics Extensions ↗
  2. Flatpak Sandbox Permissions ↗
  3. nixpkgs Steam Module ↗
  4. nixpkgs Wine Packaging Definitions ↗

Adapted from technical notes. Examples use generic names.

Continue along a reading path

Proton gaming, deployment, and troubleshooting

  1. 01umu, Wine and Proton: understanding compatibility tools and choosing Bottles runners
  2. 02Proton: the gaming runtime
  3. 03Wine and gaming environments on NixOS
  4. 04Troubleshooting Wine and Proton
  5. 05Wine and Proton configuration boundaries
CONTINUE READING / NEXT ARTICLETroubleshooting Wine and Proton

Build a verifiable diagnostic process across the graphics stack, prefixes, media, audio, and logs.