OFFSET偏移
← All articlesOFFSET / FIELD NOTESNO. 132

Distrobox on NixOS: package environments and host paths

Configure a rootless backend on a declarative host and diagnose boundaries between Nix profiles, graphics, and traditional userspaces.

English translation · Code and commands follow the Chinese original.

IN THESE READING PATHSDistrobox: userspace, desktop integration, and recoverable environments ↗
About 15 min readStart reading

The command interfaces in this article have been checked against the official Distrobox 1.8.2.5 scripts. Image tags and distribution package names are examples; before using them, confirm that the image is still supported, its repositories are available, and the versions match your system. The examples have not been tested by running them.

NixOS manages software through declarative configuration and a read-only Nix store; the root filesystem is not entirely unwritable. Distrobox adds a traditional distribution's userspace on top of this foundation, making it useful for applications that need the native apt/pacman ecosystem or assume an FHS layout.

1. Why does NixOS need Distrobox?#

1.1 The two sides of immutability

A central idea in NixOS is declarative system management through /nix/store/: all software is isolated under hashed paths, with symbolic links assembling the final system view. This provides:

  • Reproducibility: a lock file pins Nix inputs; together with the configuration, it lets you rebuild the declared components. User data, container state, and external resources must be restored separately
  • Atomic upgrades/rollbacks: each rebuild creates a new system generation
  • Coexisting versions: different versions of the same software can exist without interfering with one another

The trade-offs are also clear:

Problem What it looks like
FHS incompatibility NixOS lacks traditional paths such as /lib, /usr/lib, and /bin/bash. Many precompiled programs fail to start because an ELF loader or dynamic library path is missing. The common error is No such file or directory, rather than segmentation faults across the board
High packaging effort To use a piece of software, you either wait for it to enter nixpkgs or write a derivation yourself
Friction for quick experiments Operations taken for granted on traditional distributions, such as apt install / pacman -S, are unavailable on NixOS
Dynamic linking headaches The standard library paths expected by binary software do not fit NixOS. patchelf can help, but it is not a universal solution

1.2 nix-shell solves some problems, but not all

NixOS provides nix-shell -p and nix shell nixpkgs#xxx for temporary environments:

# 临时启动一个带 python 和 gcc 的环境
nix-shell -p python3 gcc

# 或者用 flakes 语法
nix shell nixpkgs#python3 nixpkgs#gcc

Ordinary nix-shell -p / nix shell supplies environment variables and executable paths for Nix packages; it does not turn the filesystem into a traditional FHS layout. If software is missing from nixpkgs, you can still write a derivation, and precompiled software can also use nix-ld, autoPatchelfHook, or buildFHSEnv. Closed-source programs are therefore not “almost impossible” to run; these approaches simply have different maintenance costs from traditional distribution packages.

Distrobox's value lies in reusing existing userspaces and package management workflows such as Debian/Ubuntu, Arch/AUR, and Fedora. The right approach depends on actual compatibility, maintenance frequency, and the level of reproducibility you need, rather than a count of outdated packages.

1.3 How Distrobox fills the gap

Distrobox runs a complete traditional distribution's userspace inside a container while passing key host resources into it:

+---------------------------------------------------+
|  NixOS Host (declarative packages)                      |
|  - Kernel, drivers, DE/WM (Hyprland)              |
|  - Core CLI tools (fish, git, neovim via nix)     |
|  - Container runtime (Podman)                     |
|                                                    |
|  +-- Distrobox Container (Arch Linux)             |
|  |   - AUR packages (yay/paru)                    |
|  |   - FHS-compliant filesystem (/usr/lib, /lib)  |
|  |   - Conventional package manager (pacman)      |
|  |   - Shared $HOME with host                     |
|  |                                                |
|  +-- Distrobox Container (Ubuntu 22.04)           |
|  |   - Binary-only software (.deb packages)       |
|  |   - Legacy toolchains with FHS assumptions     |
|  |   - Shared Wayland socket for GUI apps         |
+---------------------------------------------------+

Synergy: NixOS provides the stability of an immutable foundation and reproducible system configuration; Distrobox provides flexible, mutable userspace and traditional package management. They complement each other rather than being mutually exclusive.

Distrobox first needs a working container backend. On NixOS, verify the rootless Podman configuration, container creation, guest initialization, and desktop integration separately.


2. Comparing installation methods#

There are two routes to installing Distrobox on NixOS: declarative Nix installation and installation with a curl script.

2.1 Method one: declarative Nix installation (recommended)

This approach follows the NixOS philosophy by managing Distrobox within your system or user Nix configuration.

System-wide installation (configuration.nix)

# /etc/nixos/configuration.nix
{ config, pkgs, ... }:

{
  # 启用 Podman + Docker 兼容层
  virtualisation.podman = {
    enable = true;
    dockerCompat = true;        # 创建 docker -> podman 符号链接
    defaultNetwork.settings.dns_enabled = true;
  };

  # 安装 Distrobox(后端用 Podman)
  environment.systemPackages = with pkgs; [
    distrobox
    podman
  ];

  # Rootless 容器所需的 subuid/subgid 映射
  users.users.user = {
    subUidRanges = [
      { startUid = 100000; count = 65536; }
    ];
    subGidRanges = [
      { startGid = 100000; count = 65536; }
    ];
  };
}

Apply the configuration in two steps: first check the build with dry-build, then have the operator confirm the switch. The switch below changes the host's state:

nixos-rebuild dry-build
# 确认构建及配置后再应用:
# sudo nixos-rebuild switch

Home Manager installation

# ~/nixos-config/home/packages.nix 或对应的 home.nix
{ config, pkgs, ... }:

{
  home.packages = with pkgs; [
    distrobox
    podman
  ];
}

Check the Home Manager build first, then let the operator decide whether to apply it. This only installs user packages; it does not replace host Podman/UID configuration:

home-manager build
# home-manager switch

Flake development shell installation (temporary use)

If you do not want a persistent installation, a flake can provide a temporary environment:

# flake.nix
{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    flake-utils.url = "github:numtide/flake-utils";
  };

  outputs = { self, nixpkgs, flake-utils }:
    flake-utils.lib.eachDefaultSystem (system:
      let pkgs = nixpkgs.legacyPackages.${system};
      in {
        devShells.default = pkgs.mkShell {
          packages = with pkgs; [ distrobox podman ];
          shellHook = ''
            echo "Distrobox version: $(distrobox --version | head -1)"
          '';
        };
      });
}
nix develop
Comparison Declarative Nix curl script
Version source nixpkgs (determined by the locked revision) upstream latest
Update method nix flake update + rebuild Manually rerun curl
Reproducibility flake.lock pins Nix input versions A floating installation is not pinned; pinning the source can also provide version control
Fit with NixOS principles Fully aligned Does not follow the principle of "all changes through Nix"
Uninstallation Remove from configuration + rebuild Manually delete binaries and configuration
Use cases Everyday use and production environments Trying new upstream features and urgent fixes

Recommended approach Prefer declarative Nix installation. If the Distrobox version in nixpkgs is too old and you urgently need an upstream feature, use a flake to pin a specific nixpkgs revision or an overlay to introduce the newer version, rather than bypassing Nix outright.

2.2 Method two: curl script installation (non-declarative, not recommended)

curl -s https://raw.githubusercontent.com/89luca89/distrobox/main/install \
  | sh -s -- --prefix ~/.local

Violates a core NixOS principle Distrobox installed this way is not managed by Nix; nixos-rebuild and home-manager switch are unaware of it. Although a rebuild will not overwrite it (because Nix does not manage ~/.local), it also lacks the version pinning and reproducibility of declarative configuration.

Manual installation can be useful for temporary troubleshooting or comparing upstream versions, but record its source, avoid mixing installations in PATH, and bring it into declarative configuration once verified.


3. NixOS-specific configuration steps#

NixOS has a very different filesystem layout from standard Linux distributions, so configuring Distrobox on NixOS requires extra attention to a few key points.

3.1 Rootless Podman configuration

Rootless Podman is central to the NixOS + Distrobox combination: it lets ordinary users run containers without elevating their privileges.

# /etc/nixos/configuration.nix
{ config, pkgs, ... }:

{
  virtualisation.podman = {
    enable = true;

    # 创建 docker -> podman 符号链接,兼容 docker 命令
    dockerCompat = true;

    # 为 rootless 容器启用 DNS
    defaultNetwork.settings.dns_enabled = true;
  };

  # 为用户分配 subordinate UID/GID 范围
  # 这是 rootless 容器的核心前提:容器内的 root (UID 0)
  # 被映射到宿主机的非特权 UID 范围
  users.users.user = {
    subUidRanges = [
      {
        startUid = 100000;   # 从属 UID 范围示例,实际映射取决于 userns/keep-id
        count = 65536;       # 映射 65536 个 UID(标准 /etc/subuid 长度)
      }
    ];
    subGidRanges = [
      {
        startGid = 100000;
        count = 65536;
      }
    ];
  };
}

subuid/subgid ranges must not overlap allocations for other users; do not simply copy a particular startUid. Inspect the generated /etc/subuid, /etc/subgid, and actual uid_map/gid_map to check. Joining a “podman” group is not a basic prerequisite for rootless Podman; access to devices or shared sockets is a separate permissions layer.

Verify after rebuilding:

nixos-rebuild dry-build
# 确认后应用:sudo nixos-rebuild switch
# 停止相关容器后再迁移,可能影响当前用户所有容器
podman system migrate           # 确保 rootless 模式迁移完成
podman run --rm hello-world     # 验证 rootless 容器可用

3.2 Distrobox configuration file

Create a global Distrobox configuration that explicitly selects Podman as the container backend:

mkdir -p ~/.config/distrobox
cat > ~/.config/distrobox/distrobox.conf << 'EOF'
container_manager="podman"
# 0/1 设置只用于当前版本支持的选项
container_always_pull=0
EOF

The 1.8.2.5 scripts have no skip_workspace_init or container_stop_ok settings; putting them in the configuration will not implement those behaviors. Do not enable init globally. Set it in create/assemble only for containers that need long-running services.

You can also manage this configuration file declaratively through Nix (recommended):

# 在 Home Manager 中:
xdg.configFile."distrobox/distrobox.conf".text = ''
  container_manager="podman"
  container_always_pull=0
'';

3.3 Exposing Nix profile paths

Sharing HOME and PATH does not guarantee that every Nix profile symlink, ELF loader, and runtime dependency is available at the same path in the guest. First inspect the actual paths with type -a, readlink -f, and file. Prefer host-exec to run tools that need host services on the host. If you really need to execute Nix packages directly in the guest, use your diagnostic findings to mount the entire /nix and required profiles read-only:

# 通过 Home Manager 设置额外的挂载路径
xdg.configFile."distrobox/distrobox.conf".text = ''
  container_manager="podman"
  container_additional_volumes="/nix:/nix:ro /etc/profiles/per-user:/etc/profiles/per-user:ro"
'';

Security note Mounting /nix read-only (:ro) is essential: the container's package manager (apt/pacman/dnf) cannot and should not modify the host's Nix store.

3.4 Wayland and GPU acceleration

In a Wayland session, Distrobox automatically detects and forwards the Wayland socket.

AMD / Intel GPU

Mesa graphics can generally work through shared devices and sockets, but verify the guest libraries and device permissions:

# 确保图形栈已启用
{ config, pkgs, ... }:
{
  hardware.graphics.enable = true;   # 启用 Mesa、/dev/dri 等
}
# 创建容器后验证 GPU 直通
distrobox create --name gpu-test --image ubuntu:22.04
distrobox enter gpu-test
sudo apt update && sudo apt install -y mesa-utils
glxinfo | grep "OpenGL renderer"
# 预期输出包含真实的 GPU 型号,而非 "llvmpipe"

NVIDIA GPU

The original notes describe NixOS CDI/FHS integration issues from mid-2025; they cannot support a conclusion about every current nixpkgs revision. NVIDIA driver options (including hardware support for open kernel modules), the CDI generator, and the container runtime must be checked against the pinned nixpkgs and actual driver. The following illustrates the configuration structure; it is not a complete configuration you can apply to arbitrary hardware.

If you use an NVIDIA GPU, the basic configuration is as follows:

{ config, pkgs, ... }:
{
  hardware.nvidia = {
    # open = true;             # 按 GPU 世代和驱动支持选择
    modesetting.enable = true;   # 按桌面/驱动要求启用
  };
  hardware.nvidia-container-toolkit.enable = true;
  services.xserver.videoDrivers = [ "nvidia" ];
}

Let the appropriate NixOS module manage CDI generation and updates, then inspect the generated device list. Do not replace declarative configuration with hand-written files under /etc; diagnostic output can go in a temporary file. The container creation example applies only after the CDI devices exist:

systemctl status nvidia-container-toolkit-cdi-generator.service
# 按当前 nixpkgs 文档检查模块生成的 CDI;路径仍应与锁定 revision 核对
nix run nixpkgs#jq -- -r '.devices[].name' < /var/run/cdi/nvidia-container-toolkit.json
# 已安装相应工具时也可使用 nvidia-ctk cdi list
# 诊断输出(是否需权限由驱动/版本决定):
# nvidia-ctk cdi generate --output=/tmp/nvidia-cdi-diagnostic.yaml
distrobox create --name cuda-box --image nvidia/cuda:12.4.0-devel-ubuntu22.04 \
  --additional-flags "--device nvidia.com/gpu=all --security-opt=label=disable"

3.5 NixOS-specific quirks and caveats

Quirk Cause How to handle it
No /bin/bash on the host NixOS places bash at /nix/store/*/bin/bash The container has its own shell, so usage is unaffected
$HOME is shared inside and outside the container Distrobox mounts HOME by default Do not use sudo inside the container to modify Nix-managed files under HOME
Cannot execute host Nix binaries inside the container Different library paths Check symlinks and the ELF loader as needed; mount the entire /nix read-only if necessary. Use host-exec for service-related tools
distrobox-host-exec behavior Execute host commands from the container The corresponding program must be in the host's $PATH; on NixOS, you may need to specify the full path
Shell integration The host can use fish while the container uses any shell distrobox-enter supports fish/bash/zsh well

4. NixOS + Distrobox best practices#

4.1 Division of responsibility: what belongs in Nix and what belongs in Distrobox

NixOS 层(声明式软件与系统配置)
├── 系统核心:kernel, systemd, bootloader
├── 驱动层:GPU drivers, firmware, 硬件管理
├── DE/WM:Hyprland, waybar, rofi
├── 核心 CLI:git, fish, neovim, tmux, fzf
├── 开发语言运行时(可选):rustup, go, python(通过 nix shell)
└── 容器运行时:podman, distrobox

Distrobox 层(可变)
├── AUR 包:yay, paru, 小众工具
├── 二进制闭源软件:微信(deepin-wine)、Teams、FPGA 工具
├── FHS 依赖的开发环境:某些嵌入式交叉编译工具链
├── 快速实验:临时测试某个工具而不污染 Nix 环境
└── 特定发行版工具:需要特定版本 glibc 或库的旧程序

Decision rules When choosing between Nix and Distrobox, ask yourself these questions in order:

  1. Is this tool in nixpkgs? → Yes → Use Nix
  2. Does it need frequent updates/experimentation? → Yes → Consider Distrobox
  3. Is it closed-source binary software, or does it strictly require FHS? → Yes → Use Distrobox
  4. Is it a core system component (driver, kernel module)? → Yes → You must use Nix

4.2 Recommended workflows

Workflow one: AUR support

Use the Arch Linux AUR ecosystem on NixOS:

# 1. 创建 Arch 容器,预装构建工具链
distrobox create --name aur-space --image archlinux:latest \
  --additional-packages "base-devel git"
distrobox enter aur-space

# 2. 在容器内安装 AUR helper
sudo pacman -Syu --needed git base-devel
git clone https://aur.archlinux.org/paru.git
cd paru
# 先审查 PKGBUILD 与下载源,再以普通 guest 用户构建
makepkg -si

# 3. 正常使用 AUR
paru -S some-aur-package

Workflow two: run binaries that depend on FHS

# 为需要经典 Linux 文件系统的程序创建 Ubuntu 容器
distrobox create --name fhs-runner --image ubuntu:22.04
distrobox enter fhs-runner

# 在容器内安装依赖
sudo apt update && sudo apt install -y libssl3 libgtk-3-0

# 运行宿主机的预编译二进制(通过 HOME 共享)
./some-binary-that-expects-fhs

Workflow three: isolated development environments by project

# Python 项目 —— Fedora 有较新的 Python 版本
distrobox create --name proj-ml --image fedora:latest \
  --additional-packages "python3 python3-pip python3-devel gcc"

# Rust 项目 —— Arch 滚动更新适合 Rust 工具链
distrobox create --name proj-rust --image archlinux:latest \
  --additional-packages "rustup mold gcc"

# C/C++ 遗留项目 —— Ubuntu LTS 稳定性最好
distrobox create --name proj-legacy-cpp --image ubuntu:22.04 \
  --additional-packages "build-essential cmake gdb"

Workflow four: export applications from Distrobox to the host

# 在容器中安装 GUI 应用
distrobox enter aur-space
paru -S freecad

# 导出 .desktop 文件到宿主机
distrobox-export --app freecad

# 宿主机上现在可以直接从启动器打开 FreeCAD
# Wayland socket 自动转发,无需额外配置

4.3 Boundaries and pitfalls

Things to avoid

Operation to avoid Reason
Using sudo inside Distrobox to modify files under $HOME HOME is shared with the host, so this may damage Nix-managed configuration files
Unintentionally installing a second Nix installation in a shared path Testing a separate Nix installation has legitimate uses, but the store, daemon, and HOME boundaries must be clear
Putting everything in one "all-purpose container" Mixes package environment responsibilities, making problems difficult to pinpoint
Modifying the host's /etc from inside Distrobox Even if technically possible through a writable mount, this bypasses configuration management. Some generated files may be overwritten on rebuild, while other changes may persist
Using Distrobox instead of Nix to install system tools Core tools such as Git and editors should use Nix to benefit from declarative management

Things to do

Recommended practice Reason
Create separate containers by purpose (language, project, or tool category) Environment separation and clean removal
Export frequently used applications with distrobox-export Seamless integration with the host desktop experience
Run distrobox upgrade --all regularly Keep software inside containers up to date
Manage configuration centrally with ~/.config/distrobox/distrobox.conf Avoid repeating settings every time you create a container
Set --init only when init services are needed Ordinary development containers do not need the added complexity of service management

4.4 Common container image choices

Scenario Recommended image Reason
AUR packages and rolling updates archlinux:latest The richest pacman + AUR ecosystem
Compatibility and binary software ubuntu:22.04 or ubuntu:24.04 The most standard FHS layout and best commercial software support
Newer toolchains fedora:latest Recent package versions without being too aggressive
Minimal environments and script automation alpine:latest Smallest footprint; suitable for CI/scripts
Scientific computing and data science nvidia/cuda:*-devel-ubuntu* Preinstalled CUDA toolchain

5. Troubleshooting#

5.1 potentially insufficient UIDs or GIDs

Symptom: container creation fails with Error: potentially insufficient UIDs or GIDs available in user namespace.

Possible causes: no ranges allocated, insufficient or overlapping ranges, or an existing rootless pause process still using the old mappings.

Solution:

# 在 configuration.nix 中确认以下配置已存在:
users.users.user.subUidRanges = [
  { startUid = 100000; count = 65536; }
];
users.users.user.subGidRanges = [
  { startGid = 100000; count = 65536; }
];

Run podman system migrate after rebuilding.

5.2 $HOME configuration conflicts inside the container

Symptom: after entering the container, shell configuration (such as .bashrc or .config/fish/) behaves unexpectedly, or Nix-installed CLI tools report errors.

Cause: Distrobox shares $HOME by default, so the host's shell configuration and the container's shell may interfere with one another.

Solution: add a check to the container's ~/.bashrc or ~/.profile:

# ~/.bashrc 或对应的 shell 配置
if [ -n "$CONTAINER_ID" ]; then
  # 在 Distrobox 容器内 —— 使用容器内路径
  export PATH="/usr/local/bin:/usr/bin:/bin:$HOME/.local/bin"
else
  # 在宿主机 —— 保持 Nix 路径
  # (此处内容不动)
fi

5.3 Wayland GUI applications fail to start

Symptom: after export with distrobox-export --app, a GUI application shows a black screen or is unresponsive.

Troubleshooting steps:

# 1. 确认宿主机 Wayland 环境变量
echo $WAYLAND_DISPLAY
echo $XDG_RUNTIME_DIR

# 2. 在容器内验证 Wayland socket 可见
distrobox enter your-box
ls -la $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY

# 3. xeyes 验证 X11/XWayland,不单独证明原生 Wayland 通路
sudo apt install -y x11-apps
xeyes  # 应该弹出窗口

If the socket is not visible, check ~/.config/distrobox/distrobox.conf for a mistakenly configured mount that obscures it.

5.4 The container cannot access the network

Symptom: ping or apt update fails inside the container, but the host's network works normally.

Troubleshooting: Distrobox uses the host network by default, so first check the guest's resolv.conf and the host's DNS. Podman network DNS is the main focus only when using unshare-netns/a custom network.

# 检查 Podman 网络
podman network ls
podman network inspect podman
# 默认 host 网络的 Distrobox 不走这个独立网络;需先检查实际 NetworkMode

# 确保 NixOS 配置中 DNS 已启用
# configuration.nix:
#   virtualisation.podman.defaultNetwork.settings.dns_enabled = true;

5.5 NVIDIA GPU is not visible inside the container

Symptom: nvidia-smi reports "NVIDIA-SMI has failed".

Historical manual diagnostic approach: devices and driver libraries must match. Symlinks in the NixOS driver directory may point onward to /nix/store, so mounting only /run/opengl-driver is insufficient. First verify --nvidia or the generated CDI configuration; the manual mounts below are not a universal fix:

# 备选方案:手动挂载 NVIDIA 库
distrobox create --name nvidia-box --image ubuntu:22.04 \
  --additional-flags "
    --device /dev/nvidia0
    --device /dev/nvidiactl
    --device /dev/nvidia-modeset
    --device /dev/nvidia-uvm
    --volume /run/opengl-driver:/run/opengl-driver:ro
  "

# 在容器内
export LD_LIBRARY_PATH=/run/opengl-driver/lib:$LD_LIBRARY_PATH

AMD/Intel users do not need NVIDIA-specific device configuration, but should still verify actual workloads such as Mesa graphics and OpenCL/ROCm separately. A visible graphics window does not mean a compute framework has acceleration.


6. Summary#

NixOS and Distrobox complement each other; neither replaces the other:

  • NixOS declaratively manages the system foundation: the kernel, drivers, desktop environment, and core toolchain, with declarative configuration providing reproducibility and rollback.
  • Distrobox provides a mutable application layer: traditional package management, closed-source binary software, and the AUR ecosystem. It separates guest package management from the host's Nix package management; shared HOME and host command invocation can still change host state.

For NixOS users, learning to move comfortably between Nix environments and Distrobox containers combines the stability of declarative system management with the flexibility of traditional Linux distributions. This is a distinctive advantage of NixOS over other immutable distributions (Fedora Silverblue and openSUSE MicroOS): rather than being "locked in", you are free to choose the tools that manage each layer.

Further reading



References & further reading

  1. Distrobox security implications ↗
  2. Distrobox create ↗
  3. Distrobox enter ↗
  4. Nixpkgs NVIDIA toolkit module ↗
  5. Nixpkgs CUDA container documentation ↗
  6. Nixpkgs Podman module ↗
  7. Distrobox 1.8.2.5 create implementation ↗
  8. NVIDIA CDI ↗

Adapted from technical notes. Examples use generic names.

Continue along a reading path

Distrobox: userspace, desktop integration, and recoverable environments

  1. 01Distrobox fundamentals: userspace and host integration
  2. 02Distrobox installation and backend configuration
  3. 03Distrobox image and container lifecycles
  4. 04What does Distrobox actually export?
  5. 05Distrobox desktop discovery and background-service integration
  6. 06Distrobox declarative manifests and advanced workflows
  7. 07Distrobox on NixOS: package environments and host paths
  8. 08Distrobox backups: images, package lists, and shared data
  9. 09Distrobox internals: packages, configuration, and command resolution
CONTINUE READING / NEXT ARTICLEDistrobox backups: images, package lists, and shared data

Preserve the guest environment in three complementary ways and clarify separate recovery responsibilities for shared directories and application data.