Blog
Auto Environment Activation With Direnv + Flox
Ross Turk | 5 Jul 2024
![Auto Environment Activation With Direnv + Flox](/images/blog/lily-pads.webp)
Flox allows you to create different environments for each of your projects. When you cd
into your project, a quick flox activate
ensures you’re working with the right stack.
The shell extension direnv
gives me a neat way to automate the calling of that flox activate
.
When I cd
into a Flox project directory, direnv
reads an .envrc
that tells it to run flox activate
and apply the results to my Flox environment. When I cd
out of that directory, direnv
reverses that process, returning my environment to its previous state. Essentially, direnv
gives me turn-key flox activate
-ion!
Mind the Gap
It’s not the actual typing of flox activate
that is a problem for me; I have an alias for it, and I actually enjoy watching it run. It’s forgetting to type it that is the problem.
Countless times after cd
-ing into an environment, I’ve forgotten to flox activate
. Usually, the result is that whatever I’m trying to do just doesn’t work. But there are situations where this could cause something to work differently in a ... bad way.
Say I have a build script for Node v22 in my Flox environment, but Node v20 installed on my system. If I forget to flox activate
, my build script could run with the wrong version. This could end up being a big deal.
With direnv
I no longer even have to worry (or even think) about this.
It’s so useful that we built it into a utility environment on FloxHub.
Option 1: Using a FloxHub Environment
If you want to enable direnv in your current shell without doing anything permanent, you can do so with the following command:
% flox activate -r flox/direnv
Flox downloads some packages in the background and — presto! — you are inside an environment created using a manifest stored on FloxHub.
✅ You are now using the environment flox/direnv (remote)'.
To stop using this environment, type 'exit'
✅ Extension installed in ~/.config/direnv/lib/
🤖 direnv enabled
flox [flox/direnv (remote)] %
It has installed the flox-direnv
extension (more on that later), enabled direnv
in a new shell session, and created a floxit()
alias that can help you set up auto-activation across our Flox environments.
Flox remote environments are a great way to test-drive this before installing it permanently into your dotfiles. When you're done with it, you exit its subshell and it just ... disappears.
Option 2: Using a Default Environment
If you always want direnv
in your stack, you can easily add it to your default Flox environment.
If you don’t have a default environment, create one:
% flox init -d $HOME
✨ Created environment 'default' (x86_64-linux)
Next:
$ flox search <package> <- Search for a package
$ flox install <package> <- Install a package into an environment
$ flox activate <- Enter the environment
% flox activate
✅ You are now using the environment 'default'.
To stop using this environment, type 'exit'
flox [default] %
You can use Flox’s edit
option to configure your default
Flox environment for direnv
:
flox edit -d $HOME
This opens up Flox’s manifest.toml
in vi
. By editing this file, you can declaratively add software to your environment, specify environment variables, or create shell hooks that automate commands, as well as setup or teardown operations. If vi
isn’t your thing, you can override it by setting $EDITOR
.
Let’s customize our Flox manifest.toml
to add the following lines into their respective blocks:
[install]
direnv.pkg-path = "direnv"
[profile]
zsh = """
eval "$(direnv hook zsh)"
"""
bash = """
eval "$(direnv hook bash)"
"""
fish = """
direnv hook fish | source
"""
tcsh = """
eval `direnv hook tcsh`
"""
To make sure your default environment is activated when you log in, add this to your ~/.profile
for bash or ~/.zprofile
for zsh:
eval "$(flox activate -d $HOME)"
In order to use direnv
to automatically activate Flox environments, we use a plugin called flox-direnv
. To install this into your ~/.config
directory, run the following commands:
% mkdir -p "${HOME}/.config/direnv/lib/”
% curl -o "${HOME}/.config/direnv/lib/flox-direnv.sh" "https://raw.githubusercontent.com/flox/flox-direnv/v1.1.0/direnv.rc"
Optionally, you can ask Flox to suppress mention of your default environment in your shell prompt. Sometimes, especially when layering multiple environments, it’s nice to reclaim that space when you know your default environment will always be loaded. To do this, add the following line to your ~/.profile
:
flox config --set shell_prompt hide-default
Restart your shell.
% source ~/.profile
Voila! Your default
Flox environment now activates direnv
whenever you login.
Preparing a New Environment to Auto-Activate
Let’s take direnv
for a test drive.
First, we’ll make a new environment and install some stuff into it:
flox [default] % mkdir -p envirotron/env1 && cd envirotron/env1
flox [default] % flox init
flox [default] % flox install fortune-kind cowsay
Then we’ll create an .envrc
that contains the following line:
flox [default] % echo 'use flox' > .envrc
This command adds use flox
to ./env1
’s .envrc
file. Since we are in that directory already, this should auto-activate the Flox environment in that directory. Instead, it triggers the following error message:
flox [default] direnv: error /home/rossturk/dev/envirotron/env1/.envrc is blocked. Run `direnv allow` to approve its content
When you create or modify an .envrc
file, direnv
blocks it until you explicitly allow it by running the direnv allow
command. With good reason! This ensures that only .envrc
configurations you explicitly allow get loaded. We need to add the current directory to the allowlist. So let’s go ahead and do that:
flox [default] % direnv allow
The environment should automatically activate once you do this, and your prompt should change to:
flox [env1 default] %
You’re in a layered Flox environment. Any resources installed in either env1
and default
are available to you, along with any binaries, libraries, files, or other artifacts that are accessible to you on your local system. While you’re in the ./env1
directory, you can run fortune-kind
and cowsay
.
flox [env1 default] % cowsay That way madness lies
_______________________
< That way madness lies >
-----------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
flox [env1] % fortune
Harmony
Embracing the Way, you become embraced;
Breathing gently, you become newborn;
Clearing your mind, you become clear;
Nurturing your children, you become impartial;
Opening your heart, you become accepted;
Accepting the world, you embrace the Way.
Bearing and nurturing,
Creating but not owning,
Giving without demanding,
This is harmony.
-- Lao Tse, "Tao Te Ching"
As soon as you cd
out of ./env
, your prompt changes to flox [default] %
And cutting capers with cowsay
no longer works:
flox [default] % cowsay That way madness lies
Command 'cowsay' not found, but can be installed with:
sudo apt install cowsay
Excellent. The direnv
extension is doing its job: cd
-ing out of the directory deactivates the environment.
When you’re using the flox/direnv
FloxHub environment to enable direnv, a floxit()
alias is available to perform the above .envrc
and direnv allow
steps.
Preparing an Existing Environment to Auto-Activate
You can do this with any or all of your Flox environments. The sole required step is to create and populate an .envrc
file in each directory. Let’s automate an existing jupyter
notebook environment we use a lot.
flox [default] % cd ~/dev/jupyter
flox [default] % echo ‘use flox’ > .envrc
direnv: error /home/rossturk/dev/jupyter/.envrc is blocked. Run `direnv allow` to approve its content
So let’s fix that:
flox [default] % direnv allow
Jupyter environment ready - start notebook with alias 'nb'.
flox [jupyter] %
Some Housekeeping Notes
(1) To get around the direnv allow
error message triggered by the procedure described above, chain the echo
and direnv allow
commands, as below. You could easily create an alias for this, too.
echo 'use flox' > .envrc && direnv allow
Rinse and repeat for any other Flox environments you want to automate with direnv
.
(2) By default, direnv
’s prints verbose logging output to your terminal. (The gif in the next section shows you examples of this.) You can disable this, as the flox/direnv
environment does, by setting the following environment variable in ~/.profile
:
export DIRENV_LOG_FORMAT=''
(3) If you’re using direnv
in a layered Flox stack, your prompt only changes to reflect the most recently activated environment. For instance, before I cd
-ed into ~/dev/jupyter
, my prompt was flox [default] %
; once my Jupyter environment activated, it should have been flox [jupyter default] %
This happens because the PS1
shell variable is blacklisted in direnv
. Fear not, all of the Flox environments in your layered stack are still active and available, and you can access anything in them.
If you’d like to fix the prompt while using direnv
, you can add ${FLOX_PROMPT_ENVIRONMENTS_activate}
to your global $PS1
in your shell rc files and disable the auto-prompt feature with flox config --set shell_prompt hide-all
.
(4) direnv
can’t set shell aliases and functions, but flox activate
normally can. Wrapping Flox with direnv
means any aliases or shell functions from the Flox manifest.toml won’t be present. We still use update your PATH
to provide access to all of your environment’s packages, so using Flox this way is still very useful! This is a trade-off for now, but it’s possible to make this better in the future 🙂
What It’s All About
Flox is all about reducing or eliminating the snags and friction points that frustrate you as a developer, and this direnv
integration is a simple, obvious example of this.
But the best way to get a sense of how Flox works, what it can do, and how it can fit into (and hopefully transform) your projects and workflows is by putting Flox through its paces yourself.
Flox is free to download. It’s easy to use - it has just a few commands to master.
It’s all about eliminating obstacles between you and building software. Check it out!