Skip to main content
This flake uses a custom garden.packages attribute instead of the traditional environment.systemPackages or home.packages. This approach prevents duplicate package listings and simplifies package management across both NixOS and home-manager configurations.

The garden.packages pattern

Instead of separate lists for system and user packages, you define packages once in an attribute set:
garden.packages = {
  inherit (pkgs) git neovim ripgrep;
  
  wrapped-nvim = pkgs.symlinkJoin {
    name = "wrapped-nvim";
    paths = [ pkgs.nvim pkgs.astro-language-server ];
  };
};
Using an attribute set for packages provides several benefits:
  • No duplicates - Each package name can only appear once
  • Better composition - Easy to merge package sets from different modules
  • Named packages - Clear what each package is for
  • Custom packages - Define wrapped or modified packages inline

Adding packages to your user

1

Create or edit your user's packages.nix

Create or edit /home/<user>/packages.nix:
home/yourname/packages.nix
{ pkgs, ... }:
{
  garden.packages = {
    # Simple packages
    inherit (pkgs)
      git
      neovim
      ripgrep
      fd
      bat
      eza
      ;
  };
}
2

Import in your user configuration

Ensure packages.nix is imported in /home/<user>/default.nix:
home/yourname/default.nix
{
  imports = [
    ./packages.nix
    ./cli
    ./gui
  ];
}

Conditional packages

You can conditionally include packages based on profiles or platform:
{
  lib,
  pkgs,
  config,
  ...
}:
let
  inherit (lib) optionalAttrs mergeAttrsList;
  inherit (pkgs.stdenv.hostPlatform) isLinux isDarwin;

  cfg = config.garden.profiles;
in
{
  garden.packages = mergeAttrsList [
    # Always installed
    {
      inherit (pkgs) git jq;
    }

    # Only on workstation profile
    (optionalAttrs cfg.workstation.enable {
      inherit (pkgs)
        just
        nix-output-monitor
        ripgrep
        ;
    })

    # Only on Linux
    (optionalAttrs isLinux {
      inherit (pkgs)
        brightnessctl
        wl-clipboard-rs
        ;
    })

    # Only on macOS
    (optionalAttrs isDarwin {
      inherit (pkgs)
        darwin.trash
        ;
    })
  ];
}

Custom and wrapped packages

You can define custom packages or wrap existing ones:
{
  pkgs,
  ...
}:
{
  garden.packages = {
    # Neovim with language servers
    nvim-wrapped = pkgs.symlinkJoin {
      name = "nvim-wrapped";
      paths = [
        pkgs.neovim
        pkgs.lua-language-server
        pkgs.nil # Nix LSP
      ];
    };
  };
}

Real-world examples

Workstation packages

From home/isabel/packages.nix:
home/isabel/packages.nix
{
  lib,
  pkgs,
  config,
  inputs',
  ...
}:
let
  inherit (lib) optionalAttrs mergeAttrsList;
  inherit (pkgs.stdenv.hostPlatform) isLinux;

  cfg = config.garden.profiles;
in
{
  garden.packages = mergeAttrsList [
    (optionalAttrs cfg.workstation.enable {
      inherit (pkgs)
        glow              # Fancy markdown renderer
        jq                # JSON parser
        just              # Command runner
        nix-output-monitor # Better nix build output
        rsync
        unzip
        wakatime-cli
        yq                # YAML parser
        ;

      inherit (inputs'.tgirlpkgs.packages) zzz; # Code snippets
    })

    (optionalAttrs (cfg.graphical.enable && isLinux) {
      inherit (pkgs)
        brightnessctl     # Brightness control
        grim              # Screenshot tool
        libnotify         # Notifications
        pwvucontrol       # PipeWire control
        signal-desktop    # Messaging
        slurp             # Screen area selection
        swappy            # Screenshot editor
        wl-clipboard-rs   # Wayland clipboard
        wl-gammactl       # Display color temperature
        ;

      inherit (inputs'.tgirlpkgs.packages) cake-wallet;
    })
  ];
}

Development environment

home/yourname/packages.nix
{
  pkgs,
  config,
  lib,
  ...
}:
{
  garden.packages = lib.mkIf config.garden.profiles.development.enable {
    # Core development tools
    inherit (pkgs)
      git
      gh              # GitHub CLI
      lazygit         # TUI for git
      direnv          # Per-directory environments
      ;

    # Language toolchains
    inherit (pkgs)
      rustup          # Rust
      go              # Go
      nodejs          # Node.js
      python3         # Python
      ;

    # Build tools
    inherit (pkgs)
      gnumake
      cmake
      pkg-config
      ;
  };
}

System-level packages

For system-wide packages (available to all users), use the same pattern in your system configuration:
systems/yoursystem/default.nix
{
  pkgs,
  ...
}:
{
  garden.packages = {
    inherit (pkgs)
      vim
      wget
      curl
      htop
      ;
  };
}

Package organization tips

1

Group related packages

Use comments to organize packages by category:
garden.packages = {
  # Shell utilities
  inherit (pkgs) bat eza fd ripgrep zoxide;

  # Network tools  
  inherit (pkgs) curl wget rsync;

  # Development
  inherit (pkgs) git gh lazygit;
};
2

Use keep-sorted for consistency

The source repository uses keep-sorted comments:
inherit (pkgs)
  # keep-sorted start
  bat
  eza
  fd
  ripgrep
  # keep-sorted end
  ;
3

Split by profile

Create separate package modules for different use cases:
home/yourname/
├── packages/
│   ├── default.nix       # Core packages
│   ├── development.nix   # Dev tools
│   ├── graphics.nix      # Creative apps
│   └── gaming.nix        # Games
└── default.nix

Finding packages

Search for packages in nixpkgs:

Next steps

Adding users

Create users to install packages for

Adding systems

Configure systems with packages

Troubleshooting

Fix package installation issues

Templates

Use Nix flake templates

Build docs developers (and LLMs) love