[20jun2026] nix-from-scratch on ubuntu
Table of Contents
1. Problem
Nix 2.24.9 (compiled from source, see https://github.com/Rconybea/nix-from-scratch) running sandbox builds sometimes fails with opaque "could not allocate from pty" error.
Problem only occurs in a build sandbox, i.e. from nix-build ...
It's not likely to occur while a package is compiling, but occurs from time
to time when packages run their test suites.
1.1. Investigation I
I had assumed this was a problem with the procedure nix follows to setup a build container.
It looks like the origin of the problem is that the nix-build container doesn't
have permission to operate pseudo-tty devices like /dev/pts/0.
This means within the build sandbox, build cannot perform file-descriptor operations
like open, dup on its own stdin/stdout/stderr.
In particular openpty gives ENOENT in build sandbox.
Although openpty fails, process in build sandbox is started in an environment where
stdin/stdout/stderr are already granted.
So the initial process can read stdin and write stdout just fine.
But for example a Makefile that uses echo to write to console will fail.
1.2. Workaround
For some time I worked around this obstacle. Since stdout writes to console outside the build container,
it can't actually be needed for a successful build, so for example can comment out
Makefile echo statements that encounter the bug.
Similarly, can disable doCheck for nixpkgs test suite failures when pty problems are the cause.
Pragmatic, but unsatisfactory.
1.3. Investigation II
On ubuntu, it turns out this is a conscious default. Ubuntu runs as if fstab contains:
devpts /dev/pts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000
In-container this means the created multiplexor /dev/pts/ptmx also has no permissions,
The multiplexor exists for the sake of containers: each container gets its own multiplexor
at /dev/pts/ptmx (since kernel ~2.6.29), this allows containers to have an independent
pty namespace. The new-mount-namespace multiplexor inherits permissions for /dev/pts
Ubuntu ships with /dev/pts/ptmx having 0000 permissions on principle of least-privilege.
Ordinary (non-container) processes use /dev/ptmx, with 0666 permissions,
so don't rely on /dev/pts/ptmx.
Containers need to use /dev/pts/ptmx, and that requires unlocking the feature.
1.4. Solution
Fix is straightforward: we need to do two things:
Explicitly put pseudo-tty's in the nix sandbox:
# ~/.config/nix/nix.conf extra-sandbox-paths = /dev/pts /dev/ptmx=/dev/pts/ptmx /dev/tty
Set
ptmxmode=0666for/dev/pts, so fstab now contains:devpts /dev/pts devpts gid=5,mode=620,ptmxmode=0666 0 0
We also modified permissions interactively:
sudo mount -o remount,ptmxmode=0666 /dev/pts
1.5. Remarks
Note also that pure nix doesn't want/need pty's for sandbox.
We opt-in to having pty's at all because some test suites expect them.
But given we do this, we need properly-setup /dev/ptmx and to bind-mound
the facility.
Presumably nixos already sets itself up this way. A regular non-nixos user won't notice absent building a package from source code.
Nix could have mounted a fresh instance of devpts.
For example that's what Docker/runc does, along with pointing /dev/ptmx to /dev/pts/ptmx,
but (at least as of 2.24.9) it supports just "bind just the outside stuff you tell me to bind"
approach.