Skip to content

Instantly share code, notes, and snippets.

@jmatsushita
Last active December 26, 2024 00:34
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;
};
}
@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.

@Nicceboy
Copy link

Nicceboy commented May 9, 2024

@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

Seems like that the Fleek repository is archived as Mar 24, 2024

@Jacoby6000
Copy link

Jacoby6000 commented May 13, 2024

Thank you so much for this! it helped me get off the ground with home-manager and nix-darwin.

There were a few minor things I had to change in the flake.nix:

  • LnL7/nix-darwin#228 has been merged, so the touchId setup needs to go away to prevent duplicate definitions
  • NixOS/nixpkgs#137512 has been closed, and so I think the workarounds for the kitty m1 builds have to go away. I don't use kitty, so I just removed it all.
  • There were a bunch of warnings about renamed config options. Not a huge deal, I just renamed them all 1 by 1 and there was no issue.
  • The home-manager.users.jun config key needs to be changed to indicate your mac username
  • Add users.users."$MY_USER_NAME".homeDirectory = "/Users/$MY_USER_NAME"; above the home-manager.useGlobalPkgs line. based on: nix-community/home-manager#4026

Lastly (and this was noted in comments above) the command to enable the flake should be ./result/sw/bin/darwin-rebuild switch --flake ".#j-one" rather than ./result/sw/bin/darwin-rebuild switch --flake .

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