You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long. 11KB

  1. ---
  2. title: "Nix Installation on macOS Catalina"
  3. date: 2020-06-14T12:39:29+03:00
  4. draft: true
  5. tags: [100 Days, nixology]
  6. ---
  7. This is kind of a part 1.5 to [Getting Started with Nix](/posts/getting-started-with-nix), we're going into a bit more of details regarding installation of Nix on macOS. This is also an execuse to dig in a little and write about Apple's changes to the filesystem in macOS Catalina, and other APFS concepts.
  8. This can totally be skipped if you're not on any Linux based system, or if you don't care much about how APFS handles things under the hood and would rather to just blindly follow the instructions given by the Nix installer!
  9. ## APFS
  10. APFS is Apple's filesystem on iOS (since iOS 10.3) and macOS (since 10.13 High Sierra). Conceptually APFS divides your disc into 2 layers, a container layer and a file system (volumes) layer.
  11. The container layer exists within the disk's partitioning scheme and is the more high level of the two, it provides checkpoints for the actions executed in the drive, ensures those actions are atomic, and provides crash protection, among other things. It also contains volume metadata and snapshots, as well its encryption state.
  12. The file-system layer, or the volume, contains the actual data structures that store information, you know your directory structures, file metadata, and file content. In APFS, Volumes can be assigned specific roles, one of 16 roles to be exact (as of macOS Catalina 10.15). This allows the system to treat each volume in a more optimized manner depending on its role, examples on the roles are preboot, virtual memory, or recovery volumes, those three were one of the original volume roles in APFS.
  13. Partitions in APFS contain exactly one container, and containers contain inside them the volumes (file system layers) that host your actual data. Containers allow the volumes inside them to "share" the space allocated to the container, and in a sense allows each volume to dynamically expand or shrink as needed, within the confides of the container itself. However containers themselves requires more manual labor and are more risky to resize, as they can only be expanded in one direction and only into adjacent free space.
  14. As an example of the quality of life improvements introduced by containers allowing inner volumes to resize dynamic, [taken from Apples APFS reference](, a drive can be configured with two bootable volumes — one with a shipping version of macOS and one with a beta version — as well as a user data volume. All three of these volumes share free space, meaning you donʼt have to decide ahead of time how to divide space between them.
  15. After the release of macOS Catalina, Apple introduces a fancy new concept to APFS called volume groups. Volume groups are a logical grouping of multiple volumes within a container, it allows the system to treat them in a special way, and creates the illusion that these two separate volumes are one.
  16. Following is a diagram of APFS, courtesy of [](
  17. ![APFS concepts](
  18. Volume groups are directly observable when you upgrade to macOS Catlina, as you will notice if you open the Disk Utility that the volume your OS used to have is split in two, "Macintosh HD" and "Macintosh HD - Data". Even though from a user precpetive they are treated as one single entity, both in Finder and in the terminal.
  19. ![Disk Utility showing the volumes split](/images/nix-catalina/apfs/disk-utility-volumes.png)
  20. ### Read only root system volume
  21. > [macOS Catalina runs in a dedicated, read-only system volume — which means it is completely separate from all other data and helps improve the reliability of macOS.](
  22. As means to provide an extra layer of security, the OS itself is installed in a volume with the system read only role, `APFS_VOL_ROLE_SYSTEM`. This makes it practically impossible for an attacker to overwrite or manipulate the OS installation!
  23. ### The data volume
  24. Not everything can reside in a volume that is readonly, that's why there exists a nother volume that contains all of the user data, as well as any system portions that require write access. This volume is always mounted but is effectively hidden from the users, in Finder it is practically mashed together with the system volume as to appear as one volume.
  25. In order to make the volumes split transparent APFS supports what's called **firmlinks**. A firmlink conceptually similar symlink, except it only works on folders. It creates a "bi-directional wormwhole" that transparently merges folders across volumes and handle, transparently, the transition between one volume to the other when moving in the directory structure!
  26. An great example of this is the `/Users` folder, it appears to reside in the root of the filesystem, but in reality it exists in the data volume, the `/Users` we see is a firmlink between the system readonly volume and the actual folder in the data volume!
  27. You can list all system firmlinks by viewing the `/usr/share/firmlinks` file. In this diargram taken from the Apple WWDC2019 presentation on APFS changes, you can see an example of how firmlinks are placed.
  28. ![APFS firmlinks example](/images/nix-catalina/apfs/firmlinks-in-action-wwdc-presentation-slides.png)
  29. ### Writing to root volume
  30. Alright, alright, that's like cool I guess or whatever, but how do I write to the root of my system? How do I add a new folder under `/`? Welp, you kinda can't, or rather you can but you have to go through something called `synthetic.conf`.
  31. You see, since the root volume is now readonly, you literally cannot write to it, unless you go jumping through hoops to reenable writing to it by disabling SIP, rebooting, re`mount`ing to volume with write access, doing the write stuff. Oh, and none of that persists between reboots, so ... yaay! Let's just ignore that this is even an option, and focus on the next best thing, `synthetic.conf`.
  32. So remember firmlinks from before right, macOS provides a mechanism for creating user controlled firmlinks. Those "synthetic links" can be created by adding entries to the `/etc/synthetic.conf`. Those entries follow the same formate found in the system firmlinks in `/usr/share/firmlinks`, except those are meant to be managed by the user for whatever purposes there are!
  33. Alright let's do just that, let's create a folder in the system volume were we can write stuff freely! It's relatively simple to do so, first we need to add an entry to the `/etc/synthetic.conf` file declaring the folder we wanna create, say `/blah`, now the syntax of synthetic.conf is simple, it's a tab separated list of files, the following is the syntax of this file, for more details you can open a terminal and run `man synthetic.conf` it's a very short on point manual.
  34. <!-- TODO: notice we aren't using `/` at the start because it is always relative to root -->
  35. Ok, so we can just run this command `echo 'blah Users/<your-user-name>/blah' | sudo tee -a /etc/synthetic.conf` and magically we'll create this file/filder under root if missing and link it up with the writable folder under our user! One reboot later, and baam! We have ourselves a writable folder under `/blah`.
  36. We can now write to root, will not really but close enough for pretty much all pruposes!
  37. ```
  38. # if we list folder in the root and grep for blah here's our synthetic link
  39. $ ls / | grep blah
  40. lrwxr-xr-x 1 root wheel 14 Jun 20 23:20 blah -> Users/oji/blah
  41. # trying to create a file under it fails since the actual folder doesn't exist
  42. $ touch /blah/foobarr
  43. touch: /blah/foobarr: No such file or directory
  44. $ mkdir ~/blah
  45. $ touch /blah/foobarr
  46. $ ls /blah
  47. lrwxr-xr-x 1 root wheel 14 Jun 20 23:20 /blah -> Users/oji/blah
  48. $ ls /blah/
  49. total 0
  50. -rw-r--r-- 1 oji staff 0 Jun 27 16:10 foobarr
  51. $ ls ~/blah/
  52. total 0
  53. -rw-r--r-- 1 oji staff 0 Jun 27 16:10 foobarr
  54. ```
  55. Things to note here are the strict use of **tabs** in the synthetic.conf file, and the fact that files aren't preceeded by `/` in their entries, adding `/` would cause them to not be created. I assume this is because they are always assumed to be relative to the root folder.
  56. Also notice how these user created firmlinks are behaving like symlink here. In truth I kinda lied when I called them user created firmlink, firmlinks are exclusively reserved only for the OS those are "synthetic symbolic link" they are virtual symlinks that are managed by the OS as if
  57. they are firmlinks, but they behave more similarly to normal symlinks to the end users, aside from the fact they can only be created/destroyed at boot time.
  58. ## Installing Nix
  59. Ok, what the hell does any of that have to do with Nix? Well Nix uses the folder `/nix` for it's installation, as well as everything else it ever writes to disk. As you can tell this means that Nix needs write access to the now read-only system volume.
  60. The officially endorsed way for installing Nix on macOS is by utilizing the `synthetic.conf` to add a firmlink under `/nix` that points to a user writable volume. This can be done manually if you want, or automatically by utilizing the
  61. ### Script internals
  62. <!-- The default behaviour the installer supports. -->
  63. <!-- Other options and their tradeoffs are listed here -->
  64. ### System state restore and encryption
  65. <!-- keeping encryption while supporting system restore ? -->
  66. ## More readings
  67. - [Nix manual, macOS Installation](
  68. - [APFS Reference](
  69. - [Working with APFS Volume Groups](
  70. - [macOS Catalina Boot Volume Layout](
  71. - [Creating root-level directories and symbolic links on macOS Catalina](
  72. - [What’s New in Apple Filesystems presentation slides](
  73. - [Creating root-level directories and symbolic links on macOS Catalina](
  74. - [macOS Catalina: Read-Only Boot Volume Precludes Use of Top-Level File System Namespace / Paths](