brew-nix: I just ditched Homebrew entirely

Nix Technical

I have been experimenting on using Nix as a configuration language for a year now. Nix, as a configuration, does not limit to NixOS, but also allows configuring Apple (Mac) machines possible via additional nix-darwin flake. As so many influencers switch to Nix, I decided to jump onto the wagon too, albeit not with the total understanding of the language. I stitched the scripts from random places barely holding together to create a somewhat functional Nix configuration, just so I can continue working as normal. I would say that I did not understanding the language entirely (even today), and I probably missed the full potential of the language.

The Current State of Homebrew

link

Homebrew is a package manager on MacOS. Basically, we can install apps via terminal using brew command directly. So, no downloading packages apps from random websites and doing drag-and-drop actions; Homebrew will do those things for us. Available applications ranges from open source software to popular closed-source app from tech giants. I would say, at minimum, everyone with Mac should have this program installed. The automation of installing graphical apps alone is already impressive for me.

To list the already-installed apps by Homebrew, we can enter the command list.

$ brew list
==> Casks
nikitabobko/tap/aerospace       firefox                         localsend
alacritty                       gimp                            stolendata-mpv
audacity                        inkscape                        wireshark
bitwarden                       jellyfin-media-player           zen
blender                         josm                            zotero
chromium                        libreoffice                     

Typical CLI apps will be called “Formulae”, and graphical apps will be called “Casks”.

So, when I have to install desktop apps to my machine, I can refer to Homebrew’s Casks most fo the time. From the open-source image editor GIMP to popular apps like Zoom, I can just type to install from terminal easily.

Homebrew to nix-darwin

link

When it was the time for me to express those applications in Nix expression, I listed the applications like above, and then paste it somewhere in the Nix options. In this case, nix-darwin has the option to enable Homebrew integration, and also list the casks that I want to install. So, if I add another application in the Nix list, nix-darwin will trigger Homebrew automatically, and Homebrew will install only the added app into the machine.

homebrew = {
    enable = true;
    onActivation = {
        autoUpdate = true;
        cleanup = "uninstall";
        upgrade = true;
    };
    casks = [
        "alacritty"
        "audacity"
        "bitwarden"
        # and so on ...
    ];
};

At this point, 99 percent of users will be satisfied on the result. Everything works as normal, (overkilled) nix config is working. However …

Problems

link
  1. I have to install Homebrew by myself separately: Although there is an option integrated into nix-darwin, it will not install Homebrew itself for you. I have to install Homebrew first before using these Nix configurations. That is another step I have to remember in the case I start with a clean Mac.
  2. Homebrew setup is not idempotent: The way nix-darwin works is that it triggers Homebrew after it evaluates expressions. However, nix-darwin and Homebrew are separate programs. If Homebrew fail to run (which can happen sometimes), nix-darwin will not aware of the problem at all. If rebuilding the flake at the first time is not working due to errors from Homebrew, rebuilding the flake second time would usually be successful. This made the entire configuration not as functional as Nix advertised.

brew-nix

link

While I searched for the solution on solving the idempotent problem, I stumbled upon a new flake called brew-nix. I am not the person who add random flakes just to solve one small problem, but I think the functionality of this flake is so valuable, much like how Homebrew is basically the only MacOS package manager.

The way brew-nix works is that it still uses Homebrew’s repository as a source, so it inherits the apps diversity Homebrew has and ensuring compatibility with MacOS. Then, it downloads the apps to /nix/store path and symlinks them to application directory. We can see that the installation does not use Homebrew at all; just downloads and symlinks. brew-nix already does the Homebrew’s job. Therefore, we do not need Homebrew at the first place, and still get the same application with full integration of Nix!

First, I include their flakes.

inputs = {
    # nixpkgs, nix-darwin, ...
    brew-api = {
      url = "github:BatteredBunny/brew-api";
      flake = false;
    };
    brew-nix = {
      url = "github:BatteredBunny/brew-nix";
      inputs.brew-api.follows = "brew-api";
    };
}

Then, I can add the module in nix-darwin to expose the option.

  darwinConfigurations = {
    macair = nix-darwin.lib.darwinSystem {
      system = "aarch64-darwin";
      modules = [
+       brew-nix.darwinModules.default
        # other modules

After we enable brew-nix, all those casks will be accessible with prefix brewCasks.

brew-nix.enable = true;
environment.systemPackages = with pkgs; [
    brewCasks.alacritty
    brewCasks.audacity
    brewCasks.bitwarden
    # and so on ...
];

Finally, after doing darwin-rebuild, all the applications will be shown as symlinks under /Applications/Nix Apps directory (also symlinked).

My Takes

link

After I migrated to brew-nix completely, everything seems fine. I can use applications like normal, which is surprising. Each flake update does not have the Homebrew’s error problem anymore, so I am pretty satisfied right now.

The only problem I faced right now is the application state that is refreshed/wiped after every update. For example, my Firefox browser does not remember my default profile after I update the app, so I have to relink my profile to be the default one every time after the update. There might be some settings that will persist or relink the state data, but that is future me problem for now.

There is also a problem of the apps not showing up in MacOS Spotlight Search. I assumed it might be because of how the apps are symlinked, they are not indexed. Also, most importantly, Spotlight Search is bad, so I will blame this on MacOS and not the project.

Conclusion

link

Does this mean that Homebrew is pretty much useless after this configuration?

Yes and No. I do not need to have Homebrew installed on my machine. Still, it does not mean that the whole Homebrew project is obsolete. brew-nix is still relying on Homebrew repository.

I saw this project as a temporary solution to installing Nix applications in MacOS. Many GUI MacOS applications can already be installed via nixpkgs directly (without Homebrew repository). After I check my list of around 20 apps, around 15 can be installed directly nixpkgs, 5 of them need to be installed via brew-nix. I am not sure what stop them from porting those applications to nixpkgs.

Mac without Homebrew, or even brew-nix, is possible. Only time will tell.