Skip to content

Instantly share code, notes, and snippets.

Last active Oct 28, 2022
What would you like to do?
Expression for a buildEnv-based declarative user environment.

Expression for a buildEnv-based declarative user environment

This is one way of managing your user profile declaratively.

Alternatives include:

  • an attrset-based nix-env-based environment, installed using nix-env -ir rather than nix-env --set. LnL has an overlay which shows a way of doing this.
  • home-manager, which provides NixOS-like config for your $HOME

Note that this is incompatible with regular imperative use of nix-env, e.g. nix-env -iA nixpkgs.hello. It has the advantage of allowing the installation of multiple outputs of the same package much better than nix-env's builtin profile builder does.

I personally currently use the nix-env -ir approach like LnL, but without an overlay.

Feature comparison:

Prevents imperative use of nix-env (nix-env -iA nixpkgs.hello)

  • buildEnv ✔️
    • buildEnv takes responsibility for the entire user profile, meaning that nix's builtin env builder cannot modify it
  • attrset
    • the attrset approach leaves building the profile up to nix-env, which allows adding packages ad-hoc (though they will be removed on the next profile build) using nix-env -i
  • home-manager
    • home-manager installs a single buildEnv into the user profile, remaining compatible with imperative/impure nix-env

Management of files outside ~/.nix-profile

  • buildEnv
  • attrset
  • home-manager ✔️

Declarative config like NixOS

  • buildEnv
  • attrset
  • home-manager ✔️

Gives good control over outputs installed

  • buildEnv ✔️
  • attrset
  • home-manager
Save this file to ~/default.nix or your preferred path (make sure to
change the location in the update-profile script if you choose a
different place).
Install using `nix-env -f ~ --set`, from then on use `update-profile`.
{ pkgs ? import <nixpkgs> {}
, name ? "user-env"
}: with pkgs;
buildEnv {
inherit name;
extraOutputsToInstall = ["out" "bin" "lib"];
paths = [
nix # If not on NixOS, this is important!
(writeScriptBin "update-profile" ''
nix-env --set -f ~ --argstr name "$(whoami)-user-env-$(date -I)"
# Manifest to make sure imperative nix-env doesn't work (otherwise it will overwrite the profile, removing all packages other than the newly-installed one).
(writeTextFile {
name = "break-nix-env-manifest";
destination = "/manifest.nix";
text = ''
throw ''\''
Your user environment is a buildEnv which is incompatible with
nix-env's built-in env builder. Edit your home expression and run
update-profile instead!
# To allow easily seeing which nixpkgs version the profile was built from, place the version string in ~/.nix-profile/nixpkgs-version
(writeTextFile {
name = "nixpkgs-version";
destination = "/nixpkgs-version";
text = lib.version;
Copy link

AndersonTorres commented Nov 23, 2020

Hello, people! Sorry by the inconvenience, but how can I use this configuration file with the new Nix Flakes framework?

Copy link

lheckemann commented Nov 23, 2020

I don't know, I think that's still pending new profile management tools. For now, use classic nix-env I guess.

Copy link

jnetod commented Nov 29, 2020

@AndersonTorres I tried, without success, to reproduce this with nix profile; but I found a way to use buildenv with flakes (in a certain way), you'll need 3 files, besides buildenv.nix:

# flake.nix

  description = "buildenv + flake";

  inputs = {
    nixpkgs.url = github:NixOS/nixpkgs/nixos-20.09;
    flake-compat = {
      url = "github:edolstra/flake-compat";
      flake = false;

  outputs = { self, nixpkgs, flake-compat }: rec {
    system = "x86_64-linux";
    packages.${system}.user-env = import (./buildenv.nix) { pkgs = nixpkgs.legacyPackages.${system}; };
    defaultPackage.x86_64-linux = self.packages.${system}.user-env;
# default.nix

(import (
    lock = builtins.fromJSON (builtins.readFile ./flake.lock);
  in fetchTarball {
    url = "${lock.nodes.flake-compat.locked.rev}.tar.gz";
    sha256 = lock.nodes.flake-compat.locked.narHash; }
) {
  src =  ./.;
# flake.lock

$ nix flake update

Then you can proceed with the installation, just like buildenv.nix instructions.

To update your buildenv you'll need to update your flake.lock first. You could try to put some update logic on update-profile script.


Copy link

PanAeon commented Aug 3, 2021

@AndersonTorres yet another way to use buildEnv with flakes and nix profile:

  description = "A home flake";
  # original idea

  inputs = {
    nixpkgs-unstable.url = "nixpkgs/nixpkgs-unstable";
    neuron.url = "github:srid/neuron";
    neuron.inputs.nixpkgs.follows = "nixpkgs-unstable";

  outputs = { self, nixpkgs, nixpkgs-unstable, neuron }: 
      overlay-unstable = final: prev: {
        unstable = import nixpkgs-unstable {
          system = "x86_64-linux";
          config.allowUnfree = true;
    in {
    defaultPackage.x86_64-linux =  with import nixpkgs { system = "x86_64-linux"; overlays = [ overlay-unstable ]; config = { allowUnfree = true; }; }; buildEnv {
      name = "home-env";
      paths = [

To install nix profile install .
And to upgrade nix profile upgrade '.*'

Copy link

AndersonTorres commented Aug 5, 2021

@PanAeon many thanks! I have made a quick test and it is working! I will update my files accordingly.

P.S.: for those who may have problem with changing paths like the one below:

$ nix profile install
error: packages '/nix/store/d0wb2gam2pyksz2dsjx6sa83761jrrgi-hello-2.12/bin/hello' and '/nix/store/61n15131n3b9mn7ajvddbyfnv2b3pgjd-hello-2.12/bin/hello' have the same priority 5
; use 'nix-env --set-flag priority NUMBER INSTALLED_PKGNAME' to change the priority of one of the conflicting packages (0 being the highest priority)

The solution taken from NixOS/nix#6143 is just remove the profile before installing it.

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