Why you should flox every day

(I hope I will be forgiven for this very questionable pun ๐Ÿ˜„)


6 min read

I want to share a very valuable tool that I have been using for almost a year now: flox.

How I avoided nix for so long

I never had a strong desire to completely control the software running on my machine. I was more focused on setting up an environment quickly and then dedicating all my time to developing software. However, this approach came at a cost. Every time I switched companies, I had to set up a brand-new computer and reinstall all my software. Typically, this meant a few days of frustration, but it also allowed me to clear out all the accumulated clutter from my previous job.

I was envious though of those people who were re-installing their computer in a matter of minutes, dot files, system libraries, development environments, etc...

Some of them had a secret trick for this: Nix.

Nix should have been appealing to me as a functional programmer, given its features like immutable deployments, isolation, easy rollbacks, and more. However, Nix had a reputation for being difficult to understand, challenging to use, and constantly evolving.

Ian Henry's "How to Learn Nix" is an excellent rendition of some of those difficulties. I could feel in my bones the kind of rabbit hole that learning Nix would be. Is it worth the loss of a few days every few years?

If I'm being honest, it was more than that. In my previous job, we had a mixture of technologies: Haskell, Go, Python, Postgres, etc. Even using some cryptographic libraries in Python meant building Rust code! As a result, my development environment started breaking much more frequently. Every month or so, some system library would be missing, python wouldn't be the right version anymore, and my PATH would become a mess. Nix was becoming increasingly compelling...

It doesn't have to be hard

With flox I can create several environments:

  • dotfiles for my zsh configuration

  • default for my common day-to-day tools

  • scala for my open-source Scala projects

  • haskell for my open-source Haskell projects

  • rust for my open-source Rust projects

  • ockam for my project at work

My .zshrc configuration file now is simply:

eval "$(flox activate -e flox/default)"
eval "$(flox activate -e dotfiles)"

This starts the flox/default environment giving me access to the latest version of flox, then my dotfiles environment.

The activate command:

  • grabs the packages defined in the environment from a nix packages repository, builds them, and caches them locally (so that it is only slow the first time)

  • puts the binaries included in those packages on the PATH

Dot files

What is in my dotfiles environment? We can have a look at it with the edit command:

> flox edit -e dotfiles
  shell.hook = ''
    export ZDOTDIR=$HOME/.dotfiles
    exec zsh --no-globalrcs
  packages.nixpkgs-flox.zsh = {};
  packages.nixpkgs-flox.starship = {};

The dotfiles environment exports the ZDOTDIR variable and starts zsh.

The ZDOTDIR variable points to $HOME/.dotfiles directory, which is versioned with Github. That directory contains all the files used in my .zsh configuration.

I am also fetching the fabulous starship prompt, which, with a bit of configuration, allows me to display the currently activated flox environments

I now have:

  • versioned dot files,

  • which can be easily reinstalled if I use a new computer

The many tools of the trade

Wait! Where does the additional default environment come from? This environment is activated by the $HOME/.dotfiles/.zshrc configuration file. It contains all the tools I rely on to be effective at my job. For example the ultra-fast versions of grep and find (rg and fd, implemented in Rust).

flox list -e default provides the whole list:

Several things to mention about this list:

  • this is the 101st generation! I have frequently modified this list. flox can actually list all the modifications since it was created. I can also revert to any previous version if I want to

  • all the usual suspects can be found in that list: git, rg, tree, etc... There is a very high chance that if you need an executable, you will find it by searching the nix packages

  • some packages come from my own repositories. I published what was missing, using, ... flox

Publishing the rest

For example, the excellent cmt tool allows you to use a template to format your commit messages (respecting the conventionalcommits standard if you want to)


This is a tool that I use every day but it does not exist as a nix package. So I published it myself!

I forked the original repository and ran flox init to make it a flox project:

There is no pre-packaged project template for Haskell (which is how cmt is implemented), so I used package-simple.

This is where I had to learn more about Nix, so I could understand how to build the project. cmt is a small Haskell project so building it is not particularly involved

โฏ cat pkgs/commit-tool/default.nix                                                                                                                                                                                                                                    dotfiles flox/default default
{ self, pkgs, stdenv, lib, haskellPackages }:
with haskellPackages;
  mkDerivation rec {
    pname = "cmt";
    version = "";
    src = ../../.;
    isExecutable = true;
    libraryHaskellDepends = [
      ansi-terminal attoparsec base classy-prelude containers directory
      file-embed filepath process terminal-size text
    executableHaskellDepends = [ base classy-prelude ];

    doHaddock = false;
    doCheck = false;

    homepage = "https://github.com/smallhadroncollider/cmt#readme";
    description = "Write consistent git commit messages";
    license = lib.licenses.bsd3.spdxId;
    mainProgram = "cmt";

The definition above declares that:

  • we want to make an executable named cmt

  • sources can be found at ../../.

  • the package depends on other Haskell libraries

The next step is to use flox publish to make the package available to everyone. My teammates can subscribe to the repository containing the package and install it in their environment.

But they don't even have to do that!

Sharing is caring

One great benefit of creating a flox environment is the ability to save it to a Github repository with the flox push command.

This is useful for yourself because, the day you switch to another laptop, you can simply download the environment again with flox pull and be ready in minutes.

This is also particularly useful for teams working on the same project because you can make sure that everyone is always working with the same version of the same tools: postgres, python, jq, etc... thus reducing the risk of "But it works on my machine!".

Another significant benefit of using environments is their complete isolation. Activating an environment only adds the executables for that specific environment to the PATH. When you exit the environment, everything is removed. You have complete control over what is in scope.

Note: I have been using flox mostly as a package manager to handle "managed environments". There is also a concept of "project environments" where the flox configuration contains everything: how to build, test, release. The command used to enter a project environment is just flox develop in the project directory.

Less typing

One last tip. Having to type flox activate -e scala every time you want to work on a Scala project can become pretty tiresome. The solution to this problem is to use direnv. With direnv you add a .envrc file to your project directory:

eval "$(flox activate -e scala)"

Every time you cd into the project directory, direnv runs the .envrc file and that activates the right environment. Neat!

Getting some help

I encourage you to explore Flox and see how it can assist you in managing all your environments effortlessly. However, when you venture into building and publishing packages you might have to better understand flox's notion of catalog/channels and know your way around nix. Don't hesitate to leave a message on the flox Discourse site, the team is very responsive and friendly!