nixos/
├── modules/
│ ├── services/
│ │ └── openrc/
│ │ ├── default.nix # Main service
│ │ ├── library-setup.nix # Library
│ │ ├── runtime.nix # Runtime config
│ │ ├── runtime-setup.nix # Runtime env
│ │ └── service-aggregation.nix # Service
│ └── system/
│ └── boot/
│ └── openrc-init.nix # Core boot
-
Read-Only Store Compatibility
- Problem: OpenRC expects writable /etc/init.d
- Solution: Symlink farm in /run/openrc with Nix store backing
- Relevant Files:
service-aggregation.nix
,runtime-setup.nix
-
Service Management
- Problem: systemd-style declarative services → OpenRC scripts
- Solution: Nix-generated init scripts with dependency resolution
- Relevant Files:
default.nix
(services.openrc options)
-
Library Paths
- Problem: OpenRC binaries expect libraries in /lib
- Solution: Copy libraries to /lib at runtime + ldconfig in writable store
- Relevant Files:
library-setup.nix
# Creates writable OpenRC structure
mkdir -p /run/openrc/{init.d,conf.d,rc/sh,rc/bin}
# Critical environment variables
export RC_LIBEXECDIR=/run/openrc/rc
export PATH="${openrcPkg}/bin:${coreutils}/bin:$PATH"
# Init script patching
sed -i "1s|#!/bin/sh|#!${bash}/bin/bash|" "$target"
# Aggregates services from Nix store
rsync -a ${openrcPkg}/etc/openrc/init.d/ $out/etc/openrc/init.d/
# User-defined services
install -Dm755 ${cfg.script} $out/etc/openrc/init.d/${name}
# Library copy from Nix store to /lib
cp -av ${package}/lib/lib{einfo,rc}.so* /lib/
# Writable ld.so.cache generation
TMPDIR=/run/ldconfig ldconfig -C /run/ldconfig/ld.so.cache
* openrc: unable to exec `/nix/store/.../libexec/rc/sh/init.sh`: No such file or directory
* gendepends.sh: No such file or directory
Root Cause: OpenRC hardcodes paths to its libexec scripts. The Nix build puts these in the store path, but OpenRC's init process looks for them in runtime paths.
Workarounds:
- Add explicit symlinks in service aggregation:
# service-aggregation.nix
ln -s ${openrcPkg}/libexec/rc/sh/init.sh $out/etc/openrc/init.d/
ln -s ${openrcPkg}/libexec/rc/sh/gendepends.sh $out/etc/openrc/init.d/
- Patch OpenRC source to use runtime paths:
# openrc-nixos-init.patch
- rc_confdir = "/libexec/rc";
+ rc_confdir = "/run/openrc/rc";
- Add to runtime-setup.nix:
# Copy critical RC scripts to runtime directory
cp -v ${openrcPkg}/libexec/rc/sh/*.sh /run/openrc/rc/sh/
chmod 755 /run/openrc/rc/sh/*
ERROR: libeinfo.so.1: cannot open shared object file
Workaround: Add explicit library cache regeneration step:
# library-setup.nix
echo "Force-regenerating library cache..."
LD_LIBRARY_PATH=/lib /run/current-system/sw/bin/ldconfig
Starting default runlevel... [hangs]
(pkgs/openrc/)
diff --git a/meson.build b/meson.build
+rootprefix = get_option('prefix')
-bindir = get_option('prefix') / get_option('bindir')
+bindir = rootprefix / get_option('bindir')
Purpose: Separates build-time paths from runtime paths using rootprefix
Impact: Enables Nix store path isolation while maintaining FHS compatibility
diff --git a/tools/meson_runlevels.sh b/tools/meson_runlevels.sh
-ln -snf "${init_d_dir}/$x" "${DESTDIR}${sysinitdir}/$x"
+ln -snf "${DESTDIR}${init_d_dir}/$x" "${DESTDIR}${sysinitdir}/$x"
Purpose: Fixes symlink creation with DESTDIR during installation Why Needed: Prevents broken symlinks in final Nix package
diff --git a/src/openrc-init/openrc-init.c b/src/openrc-init/openrc-init.c
-execlp("openrc", "openrc", runlevel, NULL);
+execlp("@OPENRC@/bin/openrc", "openrc", runlevel, NULL);
Purpose: Hardcodes Nix store path for init process Critical For: Stage 2 initialization reliability
diff --git a/init.d/bootmisc.in b/init.d/bootmisc.in
-need localmount
+use localmount
-migrate_to_run /var/lock /run/lock
+migrate_to_run /var/lock /run/openrc/lock
Purpose: Adapts service scripts for NixOS directory structure Key Change: Lock file relocation prevents conflicts
(pkgs/openrc/default.nix)
mesonFlags = [
"-Drootprefix=${placeholder "out"}"
"--localstatedir=/run/openrc"
"-Dselinux=disabled"
"-Dnewnet=false"
];
- Path Substitution:
substituteInPlace src/openrc-init/openrc-init.c \
--replace "@PATH@" "${lib.makeBinPath [ coreutils bash ]}" \
--replace "@OPENRC@" "$out"
- Library Installation:
# Install OpenRC libraries with versioned symlinks
install -Dm755 "$libfile" "$out/lib/$(basename $libfile)"
ln -sf "$(basename $libfile)" "$out/lib/$base_libname.so"
- Runtime Directory Setup:
postInstall = ''
mkdir -p $out/{bin,sbin,lib,libexec/rc/{bin,sh}}
install -Dm644 sh/functions.sh $out/libexec/rc/sh/functions.sh
'';
nix-store --query --references $(which openrc) | grep openrc
nix-shell -p patchelf --run "patchelf --print-rpath ${pkgs.openrc}/lib/lib*"
# In NixOS VM:
ls -l /run/openrc/init.d/functions.sh
readlink /etc/init.d/functions.sh
- Modify patch files in
pkgs/openrc/
- Rebuild with debug symbols:
nix-build -E 'with import <nixpkgs> {}; openrc.overrideAttrs (o: {
NIX_CFLAGS_COMPILE = "-O0 -g";
patches = [ ./patches/new-patch.patch ];
})'
openrc-nixos-paths.patch
→ Meson configurationopenrc-nixos-runlevels.patch
→ Runlevel setupopenrc-nixos-init.patch
→ Init process executionopenrc-nixos-scripts.patch
→ Service compatibility
# Verify library presence
ls -l ${pkgs.openrc}/lib/lib{einfo,rc}.so*
# Check init script paths
grep -r '/run/openrc' ${pkgs.openrc}/share/openrc/init.d/
# In system configuration
boot.initrd.systemd.enable = false;
boot.initrd.openrc.enable = true;
services.openrc.enable = true;
# default.nix
system.activationScripts.openrc = ''
${openrcPkg}/bin/rc-update -u
'';
-
Service Dependency Resolution
- File:
service-aggregation.nix
- Key Function: Service ordering in runlevels
- File:
-
Init Script Compatibility
- File:
runtime-setup.nix
- Key Function: Script patching logic
- File:
-
Boot Process Integration
- File:
system/boot/openrc-init.nix
- Key Function: Stage 2 initialization
- File: