A small trick to make using IntelliJ with NixOS nicer
Published on Last updated on
In this article, I document how I currently do Ruby-related development using NixOS and IntelliJ. This has some challenges, as IntelliJ was not designed to work with NixOS. While I talk about Ruby here, this trick should generalize pretty well to other programming languages.
Of course, I could make the switch to another editor, but IntelliJ is my editor of choice, and I am not ready to make the switch. It is pretty smart, and even integrates with LanguageTool to provide better spellchecking.
Using bundix doesn’t work
Bundix seems to be the recommended solution when using a project with an existing Gemfile
.
However, the Ruby environment created by bundix is not usable with Intellij.
In a normal Ruby installation, various commands (aside from ruby
) itself are actually ruby scripts.
However, with bundix, these other commands become binary stubs for some reason.
This means you can no longer do ruby -S irb
, which is required by IntelliJ.
The solution is to not use bundix; instead install gems using bundler as normal.
While this is not an ideal solution, it is also useful for projects where you cannot commit a gemset.nix
file.
With the above in mind, a project for me typically looks like this:
- My system configuration contains
intellij
. - My project-specific
flake.nix
containsruby
. - My gems are listed in the
Gemfile
and installed normally.
IntelliJ’s SDK model conflicts with Nix
The big issue is that IntelliJ is conceptually not designed to work with NixOS. It has its own SDK manager and expects the dependencies to be installed at the system level. This problem is made worse by the Nix store, since paths change between versions, or even when some dependency down the line changes. A further complication is that the paths to dependencies in NixOS change if the contents of those dependencies change.
This would mean that every time you run nix flake update
, you would need to re-configure the SDK in IntelliJ,
which is a tedious task.
The workaround
First, you need to start IntelliJ from within the nix development shell,
to make sure IntelliJ has access to the SDK and other dependencies that are listed in the flake.nix
file.
Second, the flake.nix
file needs to symlink the dependencies to a known path.
This known path is then used to configure the SDK in IntelliJ.
For example, consider the following (simplified) flake I use for this website,
which is built using Nanoc.
I have the following (simplified) flake.nix
:
Only part of the flake.nix
file is shown here;
the relevant part is the link
command.
This will symlink the current ruby and bundle install to known paths: $PRJ_DATA_DIR/current/ruby
and $PRJ_DATA_DIR/current/bundle
.
Note that you also need to set the relevant environment variables (e.g. GEM_HOME
) to make this work.
My Ruby SDK configuration in IntelliJ then looks like this (with $PRJ_DATA_DIR = /home/niko/Ontwikkeling/site/.data
).
Finally, you probably want to put .data
in your global .gitignore
.
The biggest benefit of the symbolic links is that the SDK configuration in IntelliJ is largely immune to path changes in the Nix store. Other benefits of this approach are:
- The system is not polluted with specific ruby installations.
- I can use the normal bundler commands to install gems.
There are also downsides to this approach:
- It is not possible to start IntelliJ outside the nix shell.
- Working on multiple projects at the same time is impossible, as you cannot start IntelliJ twice, even from a different shell.
In the future, I might investigate a fix for that last issue.
By setting the IDE’s properties,
you can have multiple IntelliJ instances open at the same time.
A potential solution is then to have code in the flake.nix
that creates a new location for the IntelliJ stuff (e.g. plugins and settings), copies those from the system install and then finally sets IntelliJ up with that now location.