Skip to content

Integrating Development Environments Between NixOS and macOS

About 848 wordsAbout 3 min

Nix

2025-07-15

About six months ago, I switched to NixOS on my PC after a recommendation from my roommate. This came at a perfect time—I had completely messed up my Ubuntu desktop environment and couldn't recover it.

NixOS is a Linux distribution built around the Nix package manager, which enables declarative configurations and reproducible builds.

Nix

Nix is a pure functional programming language designed for managing packages and configurations. It powers the Nix package manager and NixOS, allowing users to define system setups and development environments declaratively.

I quickly discovered how handy and powerful NixOS is for managing packages and configurations, so I made it the sole OS on my PC. As I grew more familiar with Nix, I realized I could install it on my MacBook running macOS too. This setup provides a nearly seamless development experience across my NixOS desktop and macOS laptop by:

  • Using Nix to set up identical global development environments on both systems, such as specific Python interpreter or Java compiler versions.
  • Defining a shared shell environment (like zsh) on both, enabling me to reuse shell scripts, variables, and aliases effortlessly.

The full configuration for this setup is available here.

What I Aimed to Achieve

I was looking for a solution that could:

  • Ensure identical package versions across different platforms.
  • Declare my entire development stack as code for easy replication.
  • Support advanced parallel computing setups, including OpenCL, OpenMP, and MPI.
  • Allow for platform-specific configurations where necessary.

My Setup Architecture

To achieve this integration, I've structured my Nix configurations in a modular way. Here's an overview of the directory layout:

nix/
├── home-manager/
│   ├── shared/
│   │   └── programming.nix    # Cross-platform packages
│   ├── linux/
│   │   └── home.nix          # Linux-specific config
│   └── darwin/
│       └── home.nix          # macOS-specific config
├── nixos/
│   └── configuration.nix      # NixOS system config
├── nix-darwin/
│   └── configuration.nix      # macOS system config
└── flake.nix                  # Main configuration entry point

This structure keeps shared elements centralized while allowing for OS-specific tweaks.

Shared Programming Environment

At the heart of my setup is the programming.nix file, which defines development tools that work consistently across both platforms. This ensures I have the same versions of everything, from compilers to libraries.

# Programming environment packages that work on both platforms
{ config, pkgs, ... }:

{
  home.packages = with pkgs; [
    # Development tools
    git
    wget
    curl
    
    # Programming languages and runtimes
    nodejs
    typescript
    (python3.withPackages (ps: with ps; [
      numpy
      pandas
      scipy
      matplotlib
      scikit-learn
      jupyterlab
    ]))
    
    # Java Development
    jdk21
    maven
    gradle
    
    # C/C++ Development
    gcc
    cmake
    gdb
    lldb
    pkg-config

    # Parallel programming stack
    mpi
    llvmPackages.openmp
    opencl-headers
    opencl-clhpp
    ocl-icd
    clinfo
    
    # Documentation and text processing
    pandoc
    typst
    
    # Development environments
    docker
  ];
  
  home.sessionVariables = {
    JAVA_HOME = "${pkgs.jdk21}";
    CC = "${pkgs.gcc}/bin/gcc";
    CXX = "${pkgs.gcc}/bin/g++";
  };
}

This configuration pulls in essential tools for various programming tasks, including a robust Python setup for data science and machine learning, Java tools for backend development, and components for parallel computing.

Integrating Homebrew with Nix on macOS

While Nix handles most of my command-line tools, I rely on Homebrew for GUI applications and certain macOS-specific packages not available in Nix. To keep everything declarative, I integrate Homebrew directly into my Nix configuration using the homebrew module in nix-darwin.

# Homebrew configuration for macOS
{ config, pkgs, ... }:

{
  # Homebrew declarative configuration
  homebrew = {
    enable = true;
    
    # App installation preferences
    onActivation = {
      autoUpdate = true;        # Update homebrew itself
      upgrade = true;           # Upgrade all packages to latest versions
      cleanup = "zap";          # Uninstall packages not listed in config
      
      # Additional update options (optional)
      extraFlags = [
        "--verbose"             # Show detailed output during updates
      ];
    };
    
    # Global homebrew settings
    global = {
      brewfile = true;          # Use Brewfile for management
      lockfiles = false;        # Don't create lock files
    };
    
    # GUI Applications from your brew list
    casks = [
      # Cloud storage and sync
      "baidunetdisk"
      "google-drive"
      "nutstore"

      # Learning and education
      "eudic"
      "pdf-expert"
      
      # Browsers & web clients
      "firefox"
      "google-chrome"
      "bilibili"
      
      # Development tools
      "github"
      "jetbrains-toolbox"
      "visual-studio-code"
      "cursor"
      "kate"

      # macUI
      #"sketchybar" see home manager
      "font-hack-nerd-font"
      
      # Design and productivity
      "canva" 
      "figma"
      "obsidian"
      "typora"

      # Communication
      "qq"
      "wechat"
      "whatsapp"
      "microsoft-teams"
      "telegram-desktop"
      
      # AI and productivity
      "chatgpt"
      "cherry-studio"
      
      # Network and system tools
      "clash-verge-rev"
      #"stats"
      
      # Office and productivity
      "zoom"
      "slack"
      "microsoft-auto-update"
      
      # Gaming and entertainment
      "steam"
      
      # Academic and research
      "zotero"
      
      # Statistical computing and analysis
      "r"
      "rstudio"
      
      # Document preparation
      "mactex"  # Full MacTeX distribution includes TeXLive
    ];
    
    # Formulae (command-line tools) - keeping empty for now since most are handled by nix
    brews = [
      # AI and ML tools are now handled by nixpkgs
    ];
    
    # Mac App Store apps (if any)
    masApps = {
      # Example: "Xcode" = 497799835;
    };
  };
}

This approach keeps my macOS setup clean and reproducible. Homebrew handles the graphical apps, while Nix manages the rest, all defined in code.

Benefits and Challenges

This integrated setup has transformed my workflow. Switching between my desktop and laptop feels effortless, with consistent environments reducing setup time and debugging headaches.

That said, there are challenges: some packages require platform-specific tweaks, and initial setup can be steep for Nix newcomers. But once configured, the benefits far outweigh the effort.

If you're exploring similar cross-platform dev setups, I recommend starting with Nix flakes for better modularity. Feel free to adapt this to your needs!