Skip to content

Commit a7f0499

Browse files
lucernaeclaude
andauthored
update: to nixos-25.11
* Add CLAUDE.md with comprehensive codebase documentation for AI assistants Documents repository structure, Nix/Docker architecture, devcontainer samples, CI/CD workflows, image tagging conventions, template options, and key conventions to follow when working in this codebase. https://claude.ai/code/session_01NynovvPwgMmxyjS7NRj6VL * nixos devcontainer: target nixos-25.11, pure nix build, fix systemd - flake.nix: bump nixpkgs input from nixos-25.05 to nixos-25.11 - flake.nix: expose `layeredImage` as a flake output (./result | docker load) - configuration.nix: stateVersion 22.05 -> 25.11 - configuration.nix: disable systemd-udevd/systemd-udev-settle (not needed in containers) - configuration.nix: enable unified cgroup hierarchy (cgroupv2) for better container compat - devcontainer.json: fix postCreateCommand — do not attempt to start /usr/sbin/init manually; systemd is already PID 1 via overrideCommand:false. Gate on `systemctl is-system-running --wait` instead. - build-push-latest-nixos.yaml: replace docker/build-push-action with pure nix build + install Nix via cachix/install-nix-action + run `nix flake update` to pin nixos-25.11 inputs + `nix build .#packages.<arch>.layeredImage` then `./result | docker load` + multi-arch matrix (x86_64-linux, aarch64-linux) following build-base.yaml pattern + add manifests-create job to produce a multi-arch manifest after per-arch pushes https://claude.ai/code/session_01NynovvPwgMmxyjS7NRj6VL * ci: allow latest-build-nixos to trigger on claude/* branches (testing) Temporary: add claude/** to push trigger branches so CI runs on this development branch for testing the pure nix build pipeline. https://claude.ai/code/session_01NynovvPwgMmxyjS7NRj6VL * nixos: remove removed systemd.enableUnifiedCgroupHierarchy option In NixOS 25.05+ (systemd 256+), cgroup v1 support is obsolete. The systemd.enableUnifiedCgroupHierarchy option was removed; cgroupv2 is now the default and only supported mode. Remove this option so the NixOS 25.11 build succeeds. https://claude.ai/code/session_01NynovvPwgMmxyjS7NRj6VL * nixos: replace removed docker-client package with docker for nixos-25.11 pkgs.docker-client was removed in nixpkgs ~25.11; the package was restructured. Use pkgs.docker instead, which provides the full Docker CLI. This fixes the Nix evaluation error in environment.systemPackages. https://claude.ai/code/session_01NynovvPwgMmxyjS7NRj6VL * nixos: remove networking.useHostResolvConf removed in NixOS 25.11 The option networking.useHostResolvConf was removed in NixOS 25.11 with the message "was never used for anything". Remove it to fix the Nix evaluation error when building for nixos-25.11. https://claude.ai/code/session_01NynovvPwgMmxyjS7NRj6VL * chore: update cachix/install-nix-action to v31 * fix(nixos): use runCommand to create config file derivation for streamLayeredImage * fix(nixos): use actual file paths instead of symlinks for config files * feat(devcontainer): add nixos-local test configuration for pre-built NixOS image * fix(nixos): add FHS symlinks for /bin/sh and bash for VS Code compatibility * fix(nixos-local): add workaround script for FHS symlinks to test current image * fix(nixos): add fhsEtc derivation for /etc/passwd and /etc/group - Add fhsEtc to container-layeredImage.nix for VS Code compatibility - Add TROUBLESHOOTING.md with troubleshooting rules and procedures Rules documented: - Rule 1: Final tests must use devcontainer CLI - Rule 2: POC first, no waiting for GitHub Actions - Rule 3: Never copy binaries, use symlinks from nix store - Rule 4: NixOS systemd must be PID 1 * update: test nixos image on gh codespace * update: add dbus and security polkit settings * update: nixos devcontainer fix * fix(nixos): add multi-arch support for Microsoft base image Use conditional SHA256 hashes based on system architecture (x86_64-linux vs aarch64-linux) when pulling the Microsoft devcontainers base image. This enables building the NixOS devcontainer image on both AMD64 and ARM64 platforms. - Updated to use SRI hash format (sha256-...) - Added architecture detection for x86_64-linux and aarch64-linux - Throws error for unsupported systems Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * feat(ci): use native ARM64 runners for aarch64 builds Switch from QEMU emulation to GitHub's native ARM64 runners (ubuntu-24.04-arm) for aarch64-linux builds. This significantly improves build performance and reliability by running each architecture natively. Changes: - Use ubuntu-24.04-arm for aarch64-linux builds - Use ubuntu-24.04 for x86_64-linux builds - Remove QEMU setup step (no longer needed) - Remove extra-platforms config (builds run natively) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * feat(ci): use self-hosted ARM64 runner for NixOS builds Switch aarch64-linux NixOS builds to use self-hosted runner with KVM support, while keeping x86_64-linux on GitHub-hosted runners. Changes: - aarch64-linux: self-hosted runner with labels [self-hosted, Linux, ARM64] - x86_64-linux: ubuntu-24.04 GitHub-hosted runner - QEMU only set up for x86_64 builds (not needed for native aarch64) - extra-platforms only for x86_64 (aarch64 builds natively) Self-hosted runner provides KVM support required for dockerTools.buildImage with runAsRoot during NixOS activation. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * fix(ci): skip Nix installation on self-hosted NixOS runner Self-hosted runner already has Nix installed, so only install Nix on GitHub-hosted runners (x86_64-linux builds). Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * fix(ci): add NixOS system PATH for self-hosted runner GitHub Actions runner doesn't include NixOS system binaries in PATH. Add /run/current-system/sw/bin to PATH for aarch64-linux builds on the self-hosted NixOS runner so nix commands can be found. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * feat(ci): use self-hosted ARM64 runner for build-base workflow Updates: - build-base.yaml: Use self-hosted runner for aarch64-linux builds - Update all workflows from nixos-25.05 to nixos-25.11 - Keep build-push-latest*.yaml as-is with Docker buildx multi-platform Changes to build-base.yaml: - aarch64-linux: self-hosted runner [self-hosted, Linux, ARM64] - x86_64-linux: ubuntu-24.04 GitHub-hosted runner - Add PATH setup for NixOS self-hosted runner - Skip Nix installation on self-hosted runner - Conditional QEMU setup (only for x86_64) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * feat: update flake to use nixos-25.11 instead of nixos-25.05 - Update nixpkgs input to nixos-25.11 - Update base-devcontainer packages to use nixos-25.11 - Set default to nixos-25.11 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * chore: update flake.lock for nixos-25.11 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * feat: update default NixOS version to 25.11 Update default version from 25.05 to 25.11 in: - .envrc: NIXOS_VERSION_STRING and HOME_MANAGER_VERSION_STRING - Dockerfile: NIXOS_VERSION, MAIN_NIX_CHANNEL, HOME_MANAGER_CHANNEL - Dockerfile-flake: NIXOS_VERSION, MAIN_NIX_CHANNEL, HOME_MANAGER_CHANNEL Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * fix: use correct jq path for nix derivation show output in build-base The previous jq expression `.[].outputs.out.path` was broken. Switch to `.derivations | to_entries | .[0].value.outputs.out.path` with a sed guard for the /nix/store/ prefix, matching the pattern used in lucernae/docker-nixpkgs. https://claude.ai/code/session_01GaY1fk2YR24uobXC3CQowT * update: flake.lock * update: add jq in workflow * fix(ci): use nix-shell for build-base registry step Wrap the load and retag step in nix-shell -p to provide required packages (jq, docker) instead of manually installing them with nix profile install. This ensures packages are available in a clean environment without polluting the profile. Also fixed: - Use (.derivations // .) to handle both JSON structures - Improved quoting for shell variables in nix-shell context Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * update: cleanup workflow files * update: flake lock --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 175596f commit a7f0499

27 files changed

Lines changed: 1142 additions & 357 deletions
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# NixOS Devcontainer - Local Test
2+
3+
This directory contains a devcontainer configuration to test the pre-built NixOS image from GHCR.
4+
5+
## Usage
6+
7+
### Option 1: VS Code
8+
9+
1. Open this repository in VS Code
10+
2. Press `Ctrl+Shift+P` (or `Cmd+Shift+P` on Mac)
11+
3. Type "Dev Containers: Open Folder in Container"
12+
4. Select `.devcontainer/nixos-local`
13+
14+
### Option 2: Command Line with Devcontainer CLI
15+
16+
```bash
17+
devcontainer up --workspace-folder . --config .devcontainer/nixos-local/devcontainer.json
18+
```
19+
20+
### Option 3: Docker Compose (Manual)
21+
22+
```bash
23+
cd .devcontainer/nixos-local
24+
docker-compose up -d
25+
docker-compose exec devcontainer bash
26+
```
27+
28+
## Image Details
29+
30+
- **Source**: `ghcr.io/lucernae/devcontainer-nix:nixos--nixos-25.11`
31+
- **Architecture**: Multi-arch (x86_64-linux, aarch64-linux)
32+
- **Channel**: nixos-25.11
33+
- **Features**:
34+
- Full NixOS system with systemd
35+
- Nix flakes support
36+
- Docker client
37+
- Pre-configured with vscode user
38+
39+
## Testing Checklist
40+
41+
Once inside the container, verify:
42+
43+
```bash
44+
# Check NixOS version
45+
nixos-version
46+
47+
# Check Nix version
48+
nix --version
49+
50+
# Check systemd is running
51+
systemctl status
52+
53+
# Test Docker
54+
docker --version
55+
56+
# Check vscode user exists
57+
id vscode
58+
59+
# Test Nix flakes
60+
nix flake --help
61+
```
62+
63+
## Troubleshooting
64+
65+
### Container fails to start
66+
67+
Make sure you're running in privileged mode and have cgroup v2 support:
68+
69+
```bash
70+
docker info | grep -i cgroup
71+
```
72+
73+
### Permission denied errors
74+
75+
The container runs as root by default for NixOS initialization. The `vscode` user is available for development work.
76+
77+
### Image not found
78+
79+
If the image is not found, it may not have been pushed yet. Check the GitHub Actions workflow status or run:
80+
81+
```bash
82+
docker pull ghcr.io/lucernae/devcontainer-nix:nixos--nixos-25.11
83+
```
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
# NixOS Devcontainer Troubleshooting Guide
2+
3+
## Rule 1: Final Test Procedure
4+
5+
**All final tests MUST use devcontainer CLI and observe until fully loaded.**
6+
7+
```
8+
Goal Condition: FHS is created and observable
9+
- /etc/passwd exists
10+
- /etc/group exists
11+
- /bin/sh exists
12+
- systemd is running
13+
```
14+
15+
### Test Procedure
16+
17+
```bash
18+
# 1. Clean up any existing containers
19+
docker rm -f nixos-local-devcontainer-1 2>/dev/null
20+
21+
# 2. Run devcontainer CLI
22+
nix-shell -p devcontainer --run "devcontainer up --workspace-folder . --config .devcontainer/nixos-local/devcontainer.json"
23+
24+
# 3. OBSERVE until container is fully loaded:
25+
# - Wait for "Dev Containers: Finished" or similar success message
26+
# - Check systemd: docker exec nixos-local-devcontainer-1 systemctl is-system-running
27+
# - Check FHS: docker exec nixos-local-devcontainer-1 ls -la /etc/passwd /bin/sh
28+
29+
# 4. Goal condition check:
30+
docker exec nixos-local-devcontainer-1 test -f /etc/passwd && echo "✓ /etc/passwd exists"
31+
docker exec nixos-local-devcontainer-1 test -f /etc/group && echo "✓ /etc/group exists"
32+
docker exec nixos-local-devcontainer-1 test -f /bin/sh && echo "✓ /bin/sh exists"
33+
docker exec nixos-local-devcontainer-1 systemctl is-system-running | grep -q running && echo "✓ systemd running"
34+
```
35+
36+
## Rule 2: Proof of Concept First
37+
38+
**When troubleshooting, do NOT wait for GitHub Actions rebuild.**
39+
40+
1. Use existing GHCR image as base
41+
2. Add modifications in local Dockerfile
42+
3. Test locally with devcontainer CLI first
43+
4. Only after POC works, add to container-layeredImage.nix for permanent fix
44+
45+
### Workflow
46+
47+
```
48+
GitHub Actions (permanent fix) --------> GHCR Image
49+
↑ ↑
50+
| |
51+
| (after POC works) | (for testing)
52+
| |
53+
+-------- container-layeredImage <--+
54+
55+
|
56+
(add fix here)
57+
|
58+
docker-compose.yml
59+
+ Dockerfile (POC)
60+
61+
devcontainer CLI
62+
```
63+
64+
## Rule 3: Never Copy Binaries - Use Symlinks from Nix Store
65+
66+
**The base image already has all binaries in the nix store. Never copy binaries - create symlinks instead.**
67+
68+
### Why:
69+
- Copying binaries bloats the image
70+
- Nix store has all needed binaries (bash, coreutils, etc.)
71+
- Just create symlinks to the nix store paths
72+
73+
### Finding Nix Store Paths
74+
75+
```bash
76+
# Find bash in nix store
77+
docker run --rm ghcr.io/lucernae/devcontainer-nix:nixos--nixos-25.11---x86_64-linux \
78+
find /nix/store -name bash -type f 2>/dev/null | head -5
79+
80+
# Or export and check
81+
docker create --name temp <image>
82+
docker export temp | tar -tf - | grep "bin/bash"
83+
docker rm temp
84+
```
85+
86+
## Rule 4: Original NixOS Systemd Must Be PID 1
87+
88+
**When overriding Docker's entrypoint, the original NixOS systemd entrypoint MUST be PID 1.**
89+
90+
### Why:
91+
- NixOS expects systemd as PID 1
92+
- Systemd handles initialization, services, cgroups
93+
- Blocking the init process breaks the system
94+
95+
### Correct Pattern (Fork and Wait):
96+
97+
```yaml
98+
# WRONG - blocks systemd
99+
entrypoint: ["bash", "-c", "wait_for_ready && /usr/sbin/init"]
100+
101+
# CORRECT - start systemd in background, wait for it
102+
entrypoint: [
103+
"bash", "-c",
104+
"/usr/sbin/init & # start systemd as PID 1
105+
PID1=$!
106+
wait_for_vscode_ready
107+
wait $PID1"
108+
]
109+
```
110+
111+
### Alternative: Use postCreateCommand
112+
113+
Instead of blocking entrypoint, use devcontainer's `postCreateCommand`:
114+
115+
```json
116+
{
117+
"postCreateCommand": "bash -c 'systemctl is-system-running --wait && echo Ready'"
118+
}
119+
```
120+
121+
This runs AFTER container is created and systemd is already running.
122+
123+
## Problem: VS Code Dev Containers fails to connect
124+
125+
### Error: `unable to find user root: no matching entries in passwd file`
126+
127+
**Root Cause:**
128+
- VS Code runs `uname -m` as a probe to check system architecture
129+
- This requires a valid `/etc/passwd` entry for root
130+
- The NixOS activation hasn't run yet when VS Code tries to connect (systemd hasn't fully started)
131+
132+
### Why this happens:
133+
1. Docker starts container with `/usr/sbin/init` (NixOS init)
134+
2. NixOS init runs activation scripts (creates `/etc/passwd`, `/etc/group`, sets up `/bin/sh`)
135+
3. After activation, systemd starts as PID 1
136+
4. **BUT** VS Code tries to connect BEFORE systemd is ready
137+
5. VS Code needs `/etc/passwd` to run its probe commands
138+
139+
## Solutions
140+
141+
### Solution 1: Add placeholder /etc/passwd in image (Permanent Fix)
142+
143+
The VS Code probe runs BEFORE any devcontainer commands (even `onCreateCommand`). There is no built-in way to delay the probe.
144+
145+
**Therefore, the solution is to add placeholder /etc/passwd and /etc/group to the image at build time.**
146+
147+
Add passwd/group files in the NixOS image build (in container-layeredImage.nix):
148+
149+
```nix
150+
fhsEtc = pkgs.runCommand "fhs-etc" { } ''
151+
mkdir -p $out/etc
152+
echo "root:x:0:0:root:/root:/run/current-system/sw/bin/zsh" > $out/etc/passwd
153+
echo "root:x:0:" > $out/etc/group
154+
echo "vscode:x:1000:" >> $out/etc/group
155+
'';
156+
```
157+
158+
Include in `contents` array of streamLayeredImage.
159+
160+
### Solution 2: Start container first, then use devcontainer CLI (Workaround)
161+
162+
This is a practical workaround - start the container first, then use devcontainer CLI:
163+
164+
```bash
165+
# 1. Start container with docker-compose
166+
docker compose up -d
167+
168+
# 2. Wait for systemd to boot
169+
sleep 10
170+
171+
# 3. Now run devcontainer CLI - it will attach to running container
172+
devcontainer up --workspace-folder . --config .devcontainer/nixos-local/devcontainer.json
173+
```
174+
175+
This works because:
176+
1. docker-compose starts the container and waits for systemd to boot
177+
2. When systemd is ready, /etc/passwd and /etc/group exist
178+
3. devcontainer CLI then connects to the already-running container
179+
4. VS Code probe succeeds because FHS files exist
180+
181+
### Solution 3: Use entrypoint wrapper (Alternative)
182+
183+
Use a custom entrypoint that waits for systemd (Rule 4):
184+
185+
```yaml
186+
services:
187+
devcontainer:
188+
entrypoint: [
189+
"bash", "-c",
190+
"/usr/sbin/init & # start systemd as PID 1
191+
PID1=$!
192+
# wait for systemd to be ready
193+
for i in 1 2 3 4 5 6 7 8 9 10; do
194+
sleep 2
195+
if systemctl is-system-running 2>/dev/null | grep -q running; then
196+
break
197+
fi
198+
done
199+
wait $PID1"
200+
]
201+
```
202+
203+
### Solution 2: Add placeholder /etc/passwd to image (Permanent Fix)
204+
205+
Add passwd/group files in the NixOS image build (in container-layeredImage.nix):
206+
207+
```nix
208+
fhsEtc = pkgs.runCommand "fhs-etc" { } ''
209+
mkdir -p $out/etc
210+
echo "root:x:0:0:root:/root:/run/current-system/sw/bin/zsh" > $out/etc/passwd
211+
echo "root:x:0:" > $out/etc/group
212+
echo "vscode:x:1000:" >> $out/etc/group
213+
'';
214+
```
215+
216+
Include in `contents` array of streamLayeredImage.
217+
218+
**IMPORTANT: DO NOT COPY /etc/ in Dockerfile!**
219+
- Copying /etc/ files to the image will break NixOS activation
220+
- The /etc/ must be generated by NixOS activation at runtime
221+
- Placeholder files must be added at Nix expression level (container-layeredImage.nix)
222+
223+
## Current Status
224+
225+
- ✅ systemd boots correctly with `--cgroupns=host`
226+
- ✅ Nix and NixOS work inside the container
227+
- ✅ docker-compose works directly with original GHCR image
228+
- ❌ VS Code Dev Containers fails (needs passwd before systemd ready)
229+
230+
## Debugging
231+
232+
```bash
233+
# Check systemd status (must show "running")
234+
docker exec nixos-local-devcontainer-1 systemctl is-system-running
235+
236+
# Check activation logs
237+
docker logs nixos-local-devcontainer-1
238+
239+
# Check FHS files exist
240+
docker exec nixos-local-devcontainer-1 ls -la /etc/passwd
241+
docker exec nixos-local-devcontainer-1 ls -la /etc/group
242+
docker exec nixos-local-devcontainer-1 ls -la /bin/sh
243+
244+
# Check Nix works
245+
docker exec nixos-local-devcontainer-1 nix --version
246+
docker exec nixos-local-devcontainer-1 nixos-version
247+
```
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// NixOS Devcontainer - Local Test Configuration
2+
// Uses pre-built image from GHCR
3+
{
4+
"name": "NixOS Devcontainer (Local Test)",
5+
6+
"dockerComposeFile": [
7+
"docker-compose.yml"
8+
],
9+
10+
"overrideCommand": false,
11+
"service": "devcontainer",
12+
"workspaceFolder": "/workspace",
13+
14+
"postCreateCommand": "bash -c \"systemctl is-system-running --wait || true && nix-channel --update && echo 'NixOS test container ready!'\"",
15+
16+
"remoteUser": "root",
17+
18+
"customizations": {
19+
"vscode": {
20+
"extensions": [
21+
"ms-azuretools.vscode-docker",
22+
"arrterian.nix-env-selector",
23+
"bbenoist.Nix",
24+
"jnoortheen.nix-ide",
25+
"brettm12345.nixfmt-vscode"
26+
]
27+
}
28+
}
29+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
version: '3'
2+
services:
3+
devcontainer:
4+
# Using the pre-built NixOS image from GHCR
5+
image: ghcr.io/lucernae/devcontainer-nix:nixos--nixos-25.11
6+
privileged: true
7+
tty: true
8+
cgroup: host
9+
10+
volumes:
11+
- ../../:/workspace:cached
12+
- /var/run/docker.sock:/var/run/docker.sock
13+
- /sys/fs/cgroup:/sys/fs/cgroup:rw
14+
15+
tmpfs:
16+
- /run:exec,mode=777
17+
- /run/lock:exec,mode=777
18+
19+
hostname: nixos-devcontainer
20+
21+
environment:
22+
container: "docker"
23+
LD_LIBRARY_PATH: /lib:/run/current-system/sw/lib
24+
NIX_REMOTE: "daemon"
25+
PATH: "/bin:/usr/bin:/usr/sbin:/run/current-system/sw/bin/"
26+
TMPDIR: "/cont/tmp"
27+
VSCODE_SERVER_CUSTOM_GLIBC_LINKER: "true"
28+
29+
cap_add:
30+
- SYS_ADMIN
31+
- CAP_NET_ADMIN
32+
33+
stop_signal: SIGRTMIN+3

0 commit comments

Comments
 (0)