NixOS Ruby on Rails Gem Mismatches
Dealing with platform-specific gems in a Nix development environment.
I’ve been leveraging Nix flakes to manage my Ruby on Rails projects, aiming for clean, isolated development environments. The magic of nix develop is fantastic: it seamlessly sets up everything from the correct Ruby version and required gems, to even the PostgreSQL database.
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
env = pkgs.bundlerEnv {
name = "bundler";
ruby = pkgs.ruby_3_3;
gemdir = ./.;
};
in with pkgs; {
devShells.default = mkShell {
buildInputs = [ bundix env postgresql ruby_3_3 vips ];
};
});
}
This setup works beautifully, but the process of adding or updating gems introduces a wrinkle. While you can use Bundler as usual, Nix requires an additional step with bundix. Bundix generates a gemset.nix file, analogous to the Gemfile.lock, which provides the necessary Nix derivations for each gem.
However, I kept encountering frustrating hash mismatch errors when running nix develop, preventing certain gems from installing. These errors were sporadic, affecting various gems. Sometimes downgrading gem versions offered a temporary fix, sometimes not.
$ nix develop
error: hash mismatch in fixed-output derivation '/nix/store/720r3vai3kd243ksq3a74qi1qk5yrdph-sass-embedded-1.83.3.gem.drv':
specified: sha256-ITkuISjZSfdOt6VGRvMY5c5FmxlX42KGQ3mtM8+P0nk=
got: sha256-sCXjtOZWq7lN4m6R1bWoet+cHRDCAa0SJgA3tNveOcA=
error: 1 dependencies of derivation '/nix/store/wgzzjpwnnw1ijsjk2j07rhcr4ypdch3n-ruby3.4-sass-embedded-1.83.3.drv' failed to build
error: 1 dependencies of derivation '/nix/store/7kcwxkpp5478zhs1lfpipc0s37px1yg0-bundler.drv' failed to build
error: 1 dependencies of derivation '/nix/store/q654nfnvswani2pcxb17bn9xaxzydkch-nix-shell-env.drv' failed to build
The culprit? Platform-specific configurations in the Gemfile.lock file. Bundler, by default, includes the platform’s architecture in the PLATFORMS section of Gemfile.lock. This is where the problems arise.
The solution is to instruct Bundler to ignore the current machine’s platform and install only Ruby platform gems. This forces native gems to be compiled from source, ensuring greater portability and consistency. You can achieve this by setting the environment variable
PLATFORMS
- x86_64-linux
+ ruby
My refined workflow for installing or updating gems:
- Ensure a Clean Slate: Exit any existing Nix development shells. echo $SHLVL should be 1. Though I’m not sure it’s essential, it helps prevent potential conflicts from nested environments.
- Create a Temporary Bundler Environment: Run nix-shell -p ruby_3_4 -p bundix to set up a temporary environment with Ruby and Bundix. I didn’t include Bundler here since its already bundled —no pun intended— with Ruby.
- Update the Gemfile: Add the new gem or modify existing gems in your Gemfile as you normally would.
- Update the lock files: Execute BUNDLE_FORCE_RUBY_PLATFORM="true" bundle lock --update; bundix to update the Gemfile.lock with the new or modified gems and generate the updated gemset.nix with the correct hashes. The bundle command will only update the Gemfile.lock but doesn’t install the gems, we’ll leave that to Nix.
- Exit the Temporary Environment: Close the nix-shell.
- Start the Development Environment: Run nix develop to apply the changes and install the updated gems.
This approach has improved the reliability of my Ruby on Rails development environments in NixOS, eliminating those frustrating hash mismatches and making gem management smoother.
Thank you, Tucker Shea, for clarifying how to update the Gemfile.lock without installing the gems themselves!