Skip to content

Support installing multi-user on OpenRC and with BusyBox cp#14756

Open
holysoles wants to merge 3 commits intoNixOS:masterfrom
holysoles:master
Open

Support installing multi-user on OpenRC and with BusyBox cp#14756
holysoles wants to merge 3 commits intoNixOS:masterfrom
holysoles:master

Conversation

@holysoles
Copy link
Copy Markdown

@holysoles holysoles commented Dec 9, 2025

Motivation

When trying to install nix via the install scripts on a system that uses BusyBox for common utilities, the installation fails due to invalid cp usage.

Context

#13480 details this issue, but to recap, the current install scripts do not work on (base) Alpine Linux:

A recent commit (87299e4, July 7 2025) removed -p in favor of --preserve=ownership,timestamps on non-Darwin platforms. This fails on Alpine’s BusyBox cp, which only supports the short -p flag to preserve mode, ownership and timestamps.

The implementation strategy follows how testing for FreeBSD/Darwin based installs are handled: a function that returns true/false in the multi-user install script, and inline logic during the single-user install script.

To address this, I've added tests for if cp is being provided by BusyBox (checks if cp is a symlink to /bin/busybox). If it is, we use the cp command that uses -RPp.

Closes #13480


Add 👍 to pull requests you find important.

The Nix maintainer team uses a GitHub project board to schedule and track reviews.

@holysoles holysoles requested a review from edolstra as a code owner December 9, 2025 21:14
}

is_cp_busybox() {
if [ "$(readlink $(which cp))" = "/bin/busybox" ]; then
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Wouldn't it be more robust to check the output of cp --help. It outputs something like if it's busybox:

cp --help
BusyBox v1.36.1 () multi-call binary.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

We could test a condition like this

"$(cp --help 2>&1 | awk '{print $1; exit}')" = "BusyBox"

However, I believe my implementation is more robust. While I agree calling other two programs introduces room for error, I don't think the help text of a program should be scripted against. Assuming readlink and which succeed, the current approach will always work, whereas BusyBox could change their help text and break the install script.

I am open to changing this though if needed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

In that case maybe check the basename and not the absolute path? Hardcoding /bin doesn't seem ideal

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

You make an excellent point, though the command is less readable now..

if you think your suggestion is more readable/maintainable I'm happy to swap approaches

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

the current approach will always work

Not if you’re somewhere that uses busybox compiled to individual binaries instead of a multi-call binary

No idea what the best approach would be here tho

@xokdvium
Copy link
Copy Markdown
Contributor

Would also be nice if we could get installer tests for alpine in hydra so this kind of stuff doesn't happen again. The tests are here https://github.com/NixOS/nix/blob/master/tests/installer/default.nix. Just needs a proper alpine image to test against

@holysoles holysoles force-pushed the master branch 2 times, most recently from 786c86b to 9aaa32d Compare December 10, 2025 01:43
@holysoles
Copy link
Copy Markdown
Author

Would also be nice if we could get installer tests for alpine in hydra so this kind of stuff doesn't happen again. The tests are here https://github.com/NixOS/nix/blob/master/tests/installer/default.nix. Just needs a proper alpine image to test against

I'll give this a shot

@holysoles
Copy link
Copy Markdown
Author

I was able to get an installer test for Alpine working. Unfortunately to do things right there was some scope creep.

The first challenge was a couple packages aren't pre-installed (tar, xz, sudo, shadow). I tried for awhile to get those added during when we're initializing the Vagrant boxes for the installer test, but after some issues I gave up to avoid a significant change to the nix file. What I ended up doing is creating a repo to generate an alpine Vagrant box. The builds are run fully on GitHub actions so its portable. I have it setup to publish to here. At the moment I only have an alpine 3.18 box since that was the latest (healthy) generic box, but it should be possible to make newer releases from isos too.

I confirmed that the installer test fails before the fix commit, and passes after, and does not appear to cause regressions. I was also able to fix the existing issue of shell profile files not being loaded by ssh.

I ended up implementing openrc installation support in the installer, testing the daemon installer without it was clunky. I have that workaround in the commit history though if we don't want to add openrc support right now.

@holysoles holysoles changed the title fix: support installing with busybox cp again Support installing multi-user on OpenRC and with busybox cp Dec 13, 2025
@holysoles holysoles changed the title Support installing multi-user on OpenRC and with busybox cp Support installing multi-user on OpenRC and with BusyBox cp Dec 13, 2025
@xokdvium
Copy link
Copy Markdown
Contributor

Awesome! Tysm

@holysoles
Copy link
Copy Markdown
Author

Sorry I didn't run the formatting check before my earlier push, nix develop -c ./maintainers/format.sh passes now

@holysoles
Copy link
Copy Markdown
Author

could someone please retry the failed CI jobs? it looks like they all failed with a 502 error when pulling artifacts

error fetching artifacts: HTTP 502: Server Error (https://api.github.com/repos/NixOS/nix/actions/runs/20186833608/artifacts?per_page=100)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

installers: busybox cp in Alpine fails due to unsupported --preserve flags

4 participants