Skip to content

Instantly share code, notes, and snippets.

@jmatsushita
Last active April 28, 2024 14:42
Show Gist options
  • Save jmatsushita/5c50ef14b4b96cb24ae5268dab613050 to your computer and use it in GitHub Desktop.
Save jmatsushita/5c50ef14b4b96cb24ae5268dab613050 to your computer and use it in GitHub Desktop.
Setup nix, nix-darwin and home-manager from scratch on an M1 Macbook Pro
###
### [2023-06-19] UPDATE: Just tried to use my instructions again on a fresh install and it failed in a number of places.
###. Not sure if I'll update this gist (though I realise it seems to still have some traffic), but here's a list of
###. things to watch out for:
### - Check out the `nix-darwin` instructions, as they have changed.
### - There's a home manager gotcha https://github.com/nix-community/home-manager/issues/4026
###
# I found some good resources but they seem to do a bit too much (maybe from a time when there were more bugs).
# So here's a minimal Gist which worked for me as an install on a new M1 Pro.
# Inspired by https://github.com/malob/nixpkgs I highly recommend looking at malob's repo for a more thorough configuration
#
# Some people are coming directly to this Gist from search results and not the original post[1]. If that sounds like you, you should also know there is a video[2] that accompanies this.
#
# [1] https://discourse.nixos.org/t/simple-workable-config-for-m1-macbook-pro-monterey-12-0-1-with-nix-flakes-nix-darwin-and-home-manager/16834
# [2] https://www.youtube.com/watch?v=KJgN0lnA5mk
#
# Let's get started
#
# Let's install nix (at the time of writing this is version 2.5.1
curl -L https://nixos.org/nix/install | sh
# I might not have needed to, but I rebooted
mkdir -p ~/.config/nix
# Emable nix-command and flakes to bootstrap
cat <<EOF > ~/.config/nix/nix.conf
experimental-features = nix-command flakes
EOF
# Get the flake.nix in this gist
cd ~/.config
curl https://gist.githubusercontent.com/jmatsushita/5c50ef14b4b96cb24ae5268dab613050/raw/24a755065de59fc77a552518e106454750e86a49/flake.nix -O
# Get the configuration.nix and home.nix
curl https://gist.githubusercontent.com/jmatsushita/5c50ef14b4b96cb24ae5268dab613050/raw/24a755065de59fc77a552518e106454750e86a49/configuration.nix -O
curl https://gist.githubusercontent.com/jmatsushita/5c50ef14b4b96cb24ae5268dab613050/raw/24a755065de59fc77a552518e106454750e86a49/home.nix -O
# Until this is addressed https://github.com/LnL7/nix-darwin/issues/149
sudo mv /etc/nix/nix.conf /etc/nix/.nix-darwin.bkp.nix.conf
# Build the configuration
nix build .#darwinConfigurations.j-one.system
./result/sw/bin/darwin-rebuild switch --flake .
# Enjoy!
# Might be useful to install x86 packages in the nix profile manually
nix profile install nixpkgs#legacyPackages.x86_64-darwin.haskellPackages.stack
{ pkgs, lib, ... }:
{
# Nix configuration ------------------------------------------------------------------------------
nix.binaryCaches = [
"https://cache.nixos.org/"
];
nix.binaryCachePublicKeys = [
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
];
nix.trustedUsers = [
"@admin"
];
users.nix.configureBuildUsers = true;
# Enable experimental nix command and flakes
# nix.package = pkgs.nixUnstable;
nix.extraOptions = ''
auto-optimise-store = true
experimental-features = nix-command flakes
'' + lib.optionalString (pkgs.system == "aarch64-darwin") ''
extra-platforms = x86_64-darwin aarch64-darwin
'';
# Create /etc/bashrc that loads the nix-darwin environment.
programs.zsh.enable = true;
# Auto upgrade nix package and the daemon service.
services.nix-daemon.enable = true;
# Apps
# `home-manager` currently has issues adding them to `~/Applications`
# Issue: https://github.com/nix-community/home-manager/issues/1341
environment.systemPackages = with pkgs; [
kitty
terminal-notifier
];
# https://github.com/nix-community/home-manager/issues/423
environment.variables = {
TERMINFO_DIRS = "${pkgs.kitty.terminfo.outPath}/share/terminfo";
};
programs.nix-index.enable = true;
# Fonts
fonts.enableFontDir = true;
fonts.fonts = with pkgs; [
recursive
(nerdfonts.override { fonts = [ "JetBrainsMono" ]; })
];
# Keyboard
system.keyboard.enableKeyMapping = true;
system.keyboard.remapCapsLockToEscape = true;
# Add ability to used TouchID for sudo authentication
security.pam.enableSudoTouchIdAuth = true;
}
{
description = "Jun's darwin system";
inputs = {
# Package sets
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-21.11-darwin";
nixpkgs-unstable.url = github:NixOS/nixpkgs/nixpkgs-unstable;
# Environment/system management
darwin.url = "github:lnl7/nix-darwin/master";
darwin.inputs.nixpkgs.follows = "nixpkgs-unstable";
home-manager.url = "github:nix-community/home-manager";
home-manager.inputs.nixpkgs.follows = "nixpkgs-unstable";
# Other sources
comma = { url = github:Shopify/comma; flake = false; };
};
outputs = { self, darwin, nixpkgs, home-manager, ... }@inputs:
let
inherit (darwin.lib) darwinSystem;
inherit (inputs.nixpkgs-unstable.lib) attrValues makeOverridable optionalAttrs singleton;
# Configuration for `nixpkgs`
nixpkgsConfig = {
config = { allowUnfree = true; };
overlays = attrValues self.overlays ++ singleton (
# Sub in x86 version of packages that don't build on Apple Silicon yet
final: prev: (optionalAttrs (prev.stdenv.system == "aarch64-darwin") {
inherit (final.pkgs-x86)
idris2
nix-index
niv
purescript;
})
);
};
in
{
# My `nix-darwin` configs
darwinConfigurations = rec {
j-one = darwinSystem {
system = "aarch64-darwin";
modules = attrValues self.darwinModules ++ [
# Main `nix-darwin` config
./configuration.nix
# `home-manager` module
home-manager.darwinModules.home-manager
{
nixpkgs = nixpkgsConfig;
# `home-manager` config
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.users.jun = import ./home.nix;
}
];
};
};
# Overlays --------------------------------------------------------------- {{{
overlays = {
# Overlays to add various packages into package set
comma = final: prev: {
comma = import inputs.comma { inherit (prev) pkgs; };
};
# Overlay useful on Macs with Apple Silicon
apple-silicon = final: prev: optionalAttrs (prev.stdenv.system == "aarch64-darwin") {
# Add access to x86 packages system is running Apple Silicon
pkgs-x86 = import inputs.nixpkgs-unstable {
system = "x86_64-darwin";
inherit (nixpkgsConfig) config;
};
};
};
# My `nix-darwin` modules that are pending upstream, or patched versions waiting on upstream
# fixes.
darwinModules = {
programs-nix-index =
# Additional configuration for `nix-index` to enable `command-not-found` functionality with Fish.
{ config, lib, pkgs, ... }:
{
config = lib.mkIf config.programs.nix-index.enable {
programs.fish.interactiveShellInit = ''
function __fish_command_not_found_handler --on-event="fish_command_not_found"
${if config.programs.fish.useBabelfish then ''
command_not_found_handle $argv
'' else ''
${pkgs.bashInteractive}/bin/bash -c \
"source ${config.programs.nix-index.package}/etc/profile.d/command-not-found.sh; command_not_found_handle $argv"
''}
end
'';
};
};
security-pam =
# Upstream PR: https://github.com/LnL7/nix-darwin/pull/228
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.security.pam;
# Implementation Notes
#
# We don't use `environment.etc` because this would require that the user manually delete
# `/etc/pam.d/sudo` which seems unwise given that applying the nix-darwin configuration requires
# sudo. We also can't use `system.patchs` since it only runs once, and so won't patch in the
# changes again after OS updates (which remove modifications to this file).
#
# As such, we resort to line addition/deletion in place using `sed`. We add a comment to the
# added line that includes the name of the option, to make it easier to identify the line that
# should be deleted when the option is disabled.
mkSudoTouchIdAuthScript = isEnabled:
let
file = "/etc/pam.d/sudo";
option = "security.pam.enableSudoTouchIdAuth";
in ''
${if isEnabled then ''
# Enable sudo Touch ID authentication, if not already enabled
if ! grep 'pam_tid.so' ${file} > /dev/null; then
sed -i "" '2i\
auth sufficient pam_tid.so # nix-darwin: ${option}
' ${file}
fi
'' else ''
# Disable sudo Touch ID authentication, if added by nix-darwin
if grep '${option}' ${file} > /dev/null; then
sed -i "" '/${option}/d' ${file}
fi
''}
'';
in
{
options = {
security.pam.enableSudoTouchIdAuth = mkEnableOption ''
Enable sudo authentication with Touch ID
When enabled, this option adds the following line to /etc/pam.d/sudo:
auth sufficient pam_tid.so
(Note that macOS resets this file when doing a system update. As such, sudo
authentication with Touch ID won't work after a system update until the nix-darwin
configuration is reapplied.)
'';
};
config = {
system.activationScripts.extraActivation.text = ''
# PAM settings
echo >&2 "setting up pam..."
${mkSudoTouchIdAuthScript cfg.enableSudoTouchIdAuth}
'';
};
};
};
};
}
{ config, pkgs, lib, ... }:
{
home.stateVersion = "22.05";
# https://github.com/malob/nixpkgs/blob/master/home/default.nix
# Direnv, load and unload environment variables depending on the current directory.
# https://direnv.net
# https://rycee.gitlab.io/home-manager/options.html#opt-programs.direnv.enable
programs.direnv.enable = true;
programs.direnv.nix-direnv.enable = true;
# Htop
# https://rycee.gitlab.io/home-manager/options.html#opt-programs.htop.enable
programs.htop.enable = true;
programs.htop.settings.show_program_path = true;
home.packages = with pkgs; [
# Some basics
coreutils
curl
wget
# Dev stuff
# (agda.withPackages (p: [ p.standard-library ]))
google-cloud-sdk
haskellPackages.cabal-install
haskellPackages.hoogle
haskellPackages.hpack
haskellPackages.implicit-hie
haskellPackages.stack
idris2
jq
nodePackages.typescript
nodejs
purescript
# Useful nix related tools
cachix # adding/managing alternative binary caches hosted by Cachix
# comma # run software from without installing it
niv # easy dependency management for nix projects
nodePackages.node2nix
] ++ lib.optionals stdenv.isDarwin [
cocoapods
m-cli # useful macOS CLI commands
];
# Misc configuration files --------------------------------------------------------------------{{{
# https://docs.haskellstack.org/en/stable/yaml_configuration/#non-project-specific-config
home.file.".stack/config.yaml".text = lib.generators.toYAML {} {
templates = {
scm-init = "git";
params = {
author-name = "Your Name"; # config.programs.git.userName;
author-email = "youremail@example.com"; # config.programs.git.userEmail;
github-username = "yourusername";
};
};
nix.enable = true;
};
}
@gongqian
Copy link

gongqian commented Feb 8, 2022

@jmatsushita I am trying to learn NIX, but I am getting the following error when building nix-index and noticing a comment regarding waiting on nix-darwin modules that are pending upstream. Could you pointing me to how to get it fixed.

error: builder for '/nix/store/gdjvr5wnn5d2qpkv724fzhahgmmkpbfa-nix-index-0.1.2.drv' failed with exit code 1;
last 2 log lines:
>
> ErroSysErrorxexecuting '/nix/store/z6rd8wq02azalrlm2m5k08iy53klg624-bash-5.1-p12/bin/bash': Bad file descriptor
For full logs, run 'nix log /nix/store/gdjvr5wnn5d2qpkv724fzhahgmmkpbfa-nix-index-0.1.2.drv'.
error: 1 dependencies of derivation '/nix/store/is2hp4lj44dwb3jmyzik5l28vkv6d9aq-etc-bashrc.drv' failed to build
error: 1 dependencies of derivation '/nix/store/dc44n6iv13biss4ayardy6a7zqbnqms4-system-applications.drv' failed to build
error: 1 dependencies of derivation '/nix/store/kfq8kaksa37c1zspzbwmrsd2dnkvk5gj-darwin-system-22.05.20220206.942b081+darwin4.bcdb602.drv' failed to build

Thank you very much!!

@willcohen
Copy link

@jmatsushita many thanks. You’re correct — very much a total misunderstanding on my part of how profiles and nix-darwin interoperable. The flake works quite well!

@s33dunda
Copy link

s33dunda commented Feb 9, 2022

hello, I'm new to nix. I'm curious is there a way to substitute something like $(whoami) instead of say j-one or is this an arbitrary value or is a bootstrap.sh needed of some kind? The goal is to get my whole team the "base" packages but not change the user names.

Thank you for this tutorial. Really clean and easy to follow.

@jmatsushita
Copy link
Author

@gongqian Did you make sure that you use the x86 version of nix-index? If this doesn't work you might want to look at https://github.com/bennofs/nix-index/issues. The patch and associated comment in the gist is only if you use fish.

@willcohen Awesome!

@s33dunda Thanks for the kind words! Yes j-one is just the name of my darwinConfigurations attribute, which in my case is also my hostname. Usernames will typically be used with home-manager like here. I understand what you're trying to do but I'm not sure how to accomplish it more elegantly. You could indeed have a bootstrapping shell which for instance changes values in a nix file you import. A bit like how this file is imported here.

@aranw
Copy link

aranw commented Feb 12, 2022

@jmatsushita Thanks so much for this gist and the video. In the video you mention a module and "trick" for users.primaryUser I tried to do some googling to figure out what this does but I was unable to find anything obvious about what it does. I was wondering do you have a source about this or perhaps write a little explanation as to what it is and why you are not using it?

@aranw
Copy link

aranw commented Feb 12, 2022

@jmatsushita I was wondering do I need to put all these files in ~/.config? I want to keep these files in git and a lot of generated files and configs are in ~/.config which would be nice to keep out of git

@lucamaraschi
Copy link

@jmatsushita phenomenal job!
I was wondering if in your setup kitty is accessible from spotlight and does it open as any other mac apps?I tried your config in a new M1 box but I had no luck to get kitty to work.

@smunix
Copy link

smunix commented Mar 19, 2022

great work!

There's a typo at https://gist.github.com/jmatsushita/5c50ef14b4b96cb24ae5268dab613050#file-flake-nix-L96 , where you wrote "progams" instead of "programs"

s/config.progams/config.programs

@johnykifle
Copy link

johnykifle commented Apr 30, 2022

@jmatsushita this is amazing and thanks.

I am wondering if this could be used for the Intel based mac's? I can understand that ,"system" variable is set to "aarch64-darwin". But replacing that with "x86_64-darwin" and removing the overlay

# Configuration for nixpkgs nixpkgsConfig = { config = { allowUnfree = true; }; overlays = attrValues self.overlays ++ singleton ( # Sub in x86 version of packages that don't build on Apple Silicon yet final: prev: (optionalAttrs (prev.stdenv.system == "aarch64-darwin") { inherit (final.pkgs-x86) idris2 nix-index niv purescript; }) );

and some apple silicon stuff could do the job?

@jmatsushita
Copy link
Author

jmatsushita commented May 22, 2022

@aranw You're welcome :)

do I need to put all these files in ~/.config?

I have them in ~/.config/nix

Not sure about the primaryUsers trick, maybe this ?

@smunix Thanks! I just fixed it.

@lucamaraschi I'm afraid I dropped kitty and went back to using Terminal 😓

@johnykifle Yes I don't see a reason why this wouldn't work! Don't have a machine to test though, so feel free to report back 👋

@busti
Copy link

busti commented Jun 16, 2022

What do I need to do if I don't want to use unstable but 22.05 instead?
There are multiple references to unstable in the flake inputs. Also nix installed an unstable channel automatically for the root user
Can I set that channel programmatically so that the channel from the flake is also used for commands such as nix-shell?
See here: https://discourse.nixos.org/t/do-flakes-also-set-the-system-channel/19798/3

Furthermore, do I need to run both commands from ~/.config every time I want to rebuild?

nix build .#darwinConfigurations.username.system
./result/sw/bin/darwin-rebuild switch --flake .

or is it possible to somehow just run darwin-rebuild switch from anywhere?

Also, I had to do some additional stuff to make it work, specifically:

echo -e "run\tprivate/var/run" | sudo tee -a /etc/synthetic.conf
/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -t

but I was prompted to do that.
And I also had to sudo chmod myUsername ~/.nix-defexpr because that was created as root somehow.

Thank you so much for taking the time to answer all these questions by the way.

@busti
Copy link

busti commented Jun 18, 2022

Another thing that I had issues with was direnv not working out of the box.
I found out that is because zsh is installed globally using nix-darwin while direnv is activated in home-manager, meaning that the direnv hook is never inserted into the global zshrc.

To fix this, just put another programs.zsh.enable = true; into home.nix

@ksexton
Copy link

ksexton commented Sep 8, 2022

Seems gists can't do pull requests, so here is a link to a fork to add info about the video you made. Might be worth adding some sort of note referencing since this is showing up directly in search results.

https://gist.github.com/ksexton/89baeb4a8b518727242e4d933d49e315#file-readme

@busti
Copy link

busti commented Oct 21, 2022

Are there any special steps I need to take when setting this up for multiple users?
I have encountered multiple issues so far:

  • homebrew and any commands installed by it are not present on that user
    (casks are present, but only the applications, not the commands, i.e. vscode is installed but the code command cannot be used)
  • zsh prompts the following every time I open a shell:
    zsh compinit: insecure directories and files, run compaudit for list.
    Ignore insecure directories and files and continue [y] or abort compinit [n]?
    
  • system.defaults options are not set up for that user

@jmatsushita
Copy link
Author

@ksexton Added you changes. Thank you! 🙏

@ink-splatters
Copy link

@jmatsushita thanks a lot for this awesome work!

there is small question rather out of curiocity:

users.nix.configureBuildUsers

given the official recommended way of nix installation (macOS)

$ sh <(curl -L https://nixos.org/nix/install)

which creates nix opendirectory group and build user.

Isn't, therefore, users.nix.configureBuildUsers actually redundant? Or maybe does it exist for advanced users who prefer fine grained installation doing all these steps manually (I mean: creating users, group, APFS volume, registering daemon, etc)?

@rober-m
Copy link

rober-m commented Dec 2, 2022

Hi @jmatsushita, thank you for both your gists and video!

I do have an issue that I'm hoping you could help me with.

When running:

./result/sw/bin/darwin-rebuild switch --flake .

I get the error:

flake 'path:/Users/roberm/.config?lastModified=1670021446&narHash=sha256-Zz3FXSWLs4eFVor1Hwy+mhorjGLRdwwwSCJoS88YI9s=' does not provide attribute 'packages.aarch64-darwin.darwinConfigurations.MacBook-Pro.system', 'legacyPackages.aarch64-darwin.darwinConfigurations.MacBook-Pro.system' or 'darwinConfigurations.MacBook-Pro.system'

I'm on a newly bought MacbookPro M1 (Ventura). The only changes I made were to remove the Kitty overlay, change the name of the system and user, and change a few options that Nix warned me about being deprecated (and gave me the new equivalent ones).

Other than that, I'm using the same code as here. Do you know why this error is occurring? Or how to solve it?

Thank you for your excellent work!!!

@dansteeves68
Copy link

@rober-m Note line 45 of flake.nix, j-one = darwinSystem {.... To switch to this the first time I would specify the name "j-one" like this.

./result/sw/bin/darwin-rebuild switch --flake ".#j-one"

I am not positive this will solve your problem but it can't hurt to try.

@rober-m
Copy link

rober-m commented Dec 3, 2022

Thank you, @dansteeves68! It did solve the problem! It makes so much sense in retrospect 😂.

@zetashift
Copy link

Thank you for this, it's really helpful!
Is there also a config that shows multiple users on a single M1 mac?

@G3zz
Copy link

G3zz commented Dec 22, 2022

This was great - thanks. I just wanted to point people to nix-community/home-manager#1341 (comment) in case they wanted to get installed apps indexed by Spotlight

@philcruz
Copy link

philcruz commented Mar 9, 2023

This does work on Intel Mac.

# Enable experimental nix command and flakes
  # nix.package = pkgs.nixUnstable;
  nix.extraOptions = ''
    auto-optimise-store = true
    experimental-features = nix-command flakes
  '' + lib.optionalString (pkgs.system == "x86_64-darwin") ''
    extra-platforms = x86_64-darwin aarch64-darwin
  '';

I have both Intel and M1 Macs. How can I set this up so this is configured dynamically and it's not hard-coded?

@gshpychka
Copy link

TouchID for PAM was merged a while ago

@noghartt
Copy link

This does work on Intel Mac.

# Enable experimental nix command and flakes
  # nix.package = pkgs.nixUnstable;
  nix.extraOptions = ''
    auto-optimise-store = true
    experimental-features = nix-command flakes
  '' + lib.optionalString (pkgs.system == "x86_64-darwin") ''
    extra-platforms = x86_64-darwin aarch64-darwin
  '';

I have both Intel and M1 Macs. How can I set this up so this is configured dynamically and it's not hard-coded?

Technically, it's being dynamically. Because they're return an optional string if the pkgs.system matches the aarch64-darwin value, if does not match, won't append anything to the string.

If you want to add other architectures, you can do a key/value mapping and appending based on it too.

@jmatsushita
Copy link
Author

Just tried to use my instructions again on a fresh install and it failed in a number of places. Not sure if I'll update this gist (though I realise it seems to still have some traffic), but here's a list of things to watch out for:

@ZeroDeth
Copy link

For those who are new to nix, starting with Fleek is highly recommended.

@jmatsushita
Copy link
Author

@ZeroDeth Fleek seems fine and I haven't tried it, but AFAICT:

  • it doesn't manage your system level settings like nix-darwin does
  • It's a wrapper on top of home-manager, if you like yaml there might be advantages to that, but things will break underneath, and you'll have to know some nix to figure out why (to be fair that's not a problem with fleek, just with anything that provided some wrapping abstraction).

How is your experience with it?

@ZeroDeth
Copy link

ZeroDeth commented Jun 27, 2023

@jmatsushita I have been utilizing Nix on a virtual machine for three years, without encountering any problems. However, I attempted to use Nix on both my Intel and M1 Mac and discovered Fleek as a potential solution. While it was initially useful for beginners, it has not been functioning properly for the past two months. Despite submitting several contributions and issues to Fleek, I have not received any response or resolution.

Have you tried experimenting with devbox.sh? It's a great tool to import a remote configuration and customize your experience. https://www.jetpack.io/devbox/docs/devbox_global/#using-fleek-with-devbox-global

@archae0pteryx
Copy link

@jmatsushita thanks for this post those years ago! Even if there's some issues with things here and there, it's a great reference and helped me get my head all the way around the nix -> flake -> nix-darwin + home-manager situation... so i wanted to say thanks!

@tastycode
Copy link

@ZeroDeth I would definitely hang in there. i've used nix on mac laptops for nearly 2 years. I can't imagine going back. Just look for nix-darwin/flake configurations on github that have a large number of stars or followers. My personal setups were based on a work script that provisioned each laptop with a nix/flake setup. i've seen many very similar to mine though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment