@@ -5,8 +5,10 @@ A Nix flake that builds an **OCI image** (via
55Apple's [`container`](https://github.com/apple/container) CLI on macOS.
66
77It's a plain, single-process image — no NixOS, no systemd — defined in
8−[`flake.nix`](./flake.nix). It runs `fish` by default; you choose what it ships
9−via a per-project `container.nix`.
8+[`flake.nix`](./flake.nix). `c start` drops you into a
9+[`herdr`](https://herdr.dev) session whose panes use your host login shell
10+(detected and bundled by `c init`). You choose what else it ships via a
11+per-project `container.nix`.
1012
1113## Prerequisites
1214
@@ -14,32 +16,43 @@ via a per-project `container.nix`.
1416- Apple's [`container`](https://github.com/apple/container) CLI installed, with
1517 services started: `container system start` (first run downloads a Linux
1618 kernel).
17−- A **Linux builder** — the image *contents* are Linux binaries, which can't be
18− built on macOS. Use a [remote builder][builders] or, on Apple Silicon, the
19− `nix-darwin` `linux-builder`. With one configured, `nix` dispatches Linux
20− builds to it automatically (most packages also come prebuilt from the binary
21− cache, so the builder is only needed for the uncached bits).
19+- A **Linux builder** *only if you use packages that aren't in the binary
20+ cache*. The image is assembled entirely on macOS — the root filesystem is just
21+ a host-built symlink tree over the **substituted** Linux store paths — so a
22+ fully-cached package set needs no builder. Compiling a Linux package that
23+ `cache.nixos.org` doesn't have does, though: use a [remote builder][builders]
24+ or, on Apple Silicon, the `nix-darwin` `linux-builder`.
2225
2326[builders]: https://nix.dev/manual/nix/latest/advanced-topics/distributed-builds
2427
2528## Packages
2629
27−`init` builds the image from a **`container.nix`** in the current directory — a
28−`{ pkgs, nur }` function returning the package list (`nur` is
29−[NUR](https://github.com/nix-community/NUR), for packages outside nixpkgs). It
30−is **required**; there is no default set.
30+`init` builds the image from a **`container.nix`** in the current directory — an
31+attrset with a `packages` function (`{ pkgs, nur }` returning the package list;
32+`nur` is [NUR](https://github.com/nix-community/NUR), for packages outside
33+nixpkgs) and an optional `mounts` list. It is **required**; there is no default.
3134
3235```nix
3336# ./container.nix
34−{ pkgs, nur }: with pkgs; [
35− fish # the default cmd is /bin/fish, so include it (or change config.cmd)
36− coreutils
37− git
38− go
39− nur.repos.charmbracelet.crush
40−]
37+{
38+ packages = { pkgs, nur }: with pkgs; [
39+ git
40+ go
41+ nur.repos.charmbracelet.crush
42+ ];
43+ # Extra "host:container" bind mounts, on top of $PWD → /workspace. A leading
44+ # ~/ in the host path expands to $HOME. Optional.
45+ mounts = [
46+ "~/data:/data"
47+ ];
48+}
4149```
4250
51+Your **host login shell** (detected by `c init`, bash fallback), **coreutils**,
52+[**herdr**](https://herdr.dev) (the session `c start` launches), **Nix** itself
53+(with CA certs and `nix-command`/`flakes` enabled, single-user), and the ncurses
54+**terminfo** database are always included — no need to list them.
55+
4356Point at a different file with `-c`/`--config`:
4457
4558```sh
@@ -55,17 +68,17 @@ that holds a `container.nix`; the container is named after that directory.
5568# build the image, load it, and create the container (named after $PWD):
5669nix run github:ilyakooo0/nix-container -- init
5770
58−# start it and attach — drops you into fish:
71+# start it and attach — drops you into a herdr session:
5972nix run github:ilyakooo0/nix-container -- start
6073```
6174
6275- `init` builds the OCI archive in a temp dir, loads it into `container`,
6376 deletes the temp file, then (re)creates the container. It mounts the current
64− directory at `/workspace` (the container's working directory) and your
65− `~/.config` at `/root/.config`.
77+ directory at `/workspace` (the container's working directory).
6678- `--ssh` forwards your host SSH agent socket into the container (so `git` over
6779 SSH works with your keys).
68−- Add more mounts by passing them through (mounts can only be set at creation):
80+- Add more mounts persistently via `mounts` in `container.nix` (above), or
81+ ad-hoc by passing them through to `create` (mounts can only be set at creation):
6982
7083 ```sh
7184 nix run github:ilyakooo0/nix-container -- init -v $HOME/data:/data
@@ -86,18 +99,19 @@ nix run github:ilyakooo0/nix-container -- init
8699## Customize further
87100
88101Fork or clone to change what isn't per-project — `config.cmd`/`env` or the image
89−name — in [`flake.nix`](./flake.nix). The flake also exposes build helpers, both
90−taking a `{ pkgs, nur }: [ ... ]` function:
102+name — in [`flake.nix`](./flake.nix). The flake also exposes build helpers:
91103
92−- `lib.<system>.mkImage` → an OCI image.
93−- `lib.<system>.copyWith` → a skopeo copy app, for building/pushing the image
94− anywhere skopeo can write (e.g. a registry):
104+- `lib.<system>.copyWith` → a skopeo copy app for a `{ pkgs, nur }: [ ... ]`
105+ function, for building/pushing the image anywhere skopeo can write (e.g. a
106+ registry):
95107
96108 ```sh
97109 drv=$(nix build --no-link --print-out-paths --impure --expr \
98− '(builtins.getFlake "github:ilyakooo0/nix-container").lib.${builtins.currentSystem}.copyWith (import ./container.nix)')
110+ '(builtins.getFlake "github:ilyakooo0/nix-container").lib.${builtins.currentSystem}.copyWith (import ./container.nix).packages')
99111 $drv/bin/copy-to docker://ghcr.io/me/img:latest
100112 ```
113+- `lib.<system>.mkImage <name> <shell>` → the underlying OCI image (`copyWith`
114+ defaults these to `nix-container` / `bash`).
101115
102116The flake uses `flake-utils` over `{aarch64,x86_64}-{darwin,linux}`; the host
103117system maps to the matching Linux target automatically (an Intel Mac builds an
@@ -105,9 +119,12 @@ system maps to the matching Linux target automatically (an Intel Mac builds an
105119
106120## How it works
107121
108−- **Contents** are built from `pkgsLinux` → the Linux builder.
109−- **Assembly** (`nix2container` + the `skopeo` copy) runs on your Mac, so a
110− single `init` produces a loadable archive.
122+- **Contents** are Linux store paths — downloaded prebuilt from the binary cache
123+ (only built on a Linux builder if not cached).
124+- **Assembly** runs entirely on your Mac: `copyToRoot` is a *host-arch* `buildEnv`
125+ symlink tree over those Linux paths (symlinks are arch-neutral), and
126+ `nix2container` + `skopeo` package it — so for a cached set, `init` produces a
127+ loadable Linux image with no Linux build at all.
111128- Apple's `container` runs each container in its own lightweight Linux VM and
112129 consumes standard OCI images — no `--privileged` or systemd needed.
113130