A simple Rails development environment using nix-shell

This nix-shell environment provides a Ruby environment capable of running a Rails app without a database

This follows on from my previous article about setting up a simple Ruby development environment using nix-shell. The next thing I wanted to try was to set up a simple Rails development environment. To this end I decided to focus on the GFR website which is a Rails app, but has the advantage that it doesn't use a database.

The Gemfile for this project specified Ruby v2.5.7 and so as before, I upgraded it to use the latest v2.5 patch version, v2.5.8, so that I could use the ruby_2_5 package provided by nix.

In a similar vein, the Gemfile.lock was BUNDLED WITH v1.17.3 of bundler; whereas the bundler version provided by nixpkgs was v2.1.4. The line in Gemfile.lock wasn't an enforced constraint and I didn't want to break our Heroku deployment, so I compromised and upgraded to the v2 version of bundler supported by the Heroku Ruby buildpack, i.e. v2.0.2.

My shell.nix ended up like this:

with (import <nixpkgs> {});
  env = bundlerEnv {
    name = "site-bundler-env";
    ruby = ruby_2_5;
    gemdir  = ./.;
in mkShell {
  buildInputs = [ env env.wrappedRuby ];

The full set of changes including the gemset.nix generated by bundix are in this commit.

At this point I was surprised to discover that I could run rails server from within my nix-shell and everything worked perfectly! 🚀

$ nix-shell

# ...

$ rails s
=> Booting Puma
=> Rails application starting in development
=> Run `rails server -h` for more startup options

# ...

Started GET "/" for ::1 at 2020-09-10 18:05:37 +0100
Processing by PagesController#show as HTML

# ...

Completed 200 OK in 170ms (Views: 23.9ms)


It's worth noting that early on in the shenanigans above, I got stuck for a while with the wrong version of Ruby and nothing I did would change it. In the end I deleted a bunch of things in my /nix/store directory to fix the problem. While this probably wasn't the right way to fix it, I really appreciated the way it's relatively easy to work out how various executables are being made available to your environment, i.e. via a series of symbolic links.

I also worked out that it's not possible (at least not when using bundlerEnv) to specify the version of bundler you want to use - it seems to be fixed at v2.1.4.

Next steps

I'm still interested in working out how to have a project use a specific patch version of Ruby and to be able to lockdown the exact version of bundler. I've been reading about nix flakes and although I haven't completely got my head around them, I think they might be what I'm looking for, because they have a "lock file" which I believe can pin your dependencies to ensure reproducibility.

However, I still feel as if that's a bit of a tangent. My main aim is to be able to have multiple Rails projects on the same computer with various flavours and versions of databases, etc. So I think my next step should be to setup a development environment for a Rails project which uses a database.

Update: I've belatedly realised that some run-time dependencies (e.g. node.js & yarn) were satisfied by OS packages installed in my OSX environment, i.e. I forgot to isolate the nix shell from this environment like I did when investigating the dependency on node.js in my previous article. I plan to tackle doing this soon.

Further reading

If you'd like to know more about nix flakes, I can recommend these articles by Eelco Dolstra: