Ruby Development Using Guix

Carlo Zancanaro - Sunday 08 February 2026 | guix | ruby

Up until a few months ago, I was working on a Ruby on Rails monolith. In the four years that I worked on the project, we went through a few ways of building a local development environment:

  1. directly using Bundler on our machine, then
  2. using Bundler inside a Docker image, then
  3. using Nix and Bundix, then
  4. back to using Bundler inside a Docker image (not everyone liked Nix).

All of these options were fine, but my package manager of choice is Guix. I really wanted a way for me to work with my Ruby on Rails project alongside the rest of my team, but using Guix to build my development environment.

In 2015 David Thompson posted about using Guix for Ruby development which describes using Guix packages to build a Ruby development environment. Guix has a lot of Ruby packages, but it doesn't package all of RubyGems, nor does it package all the different versions of each Gem. This makes it difficult to use Guix to get precise dependencies that are needed for most real-world Ruby projects.

To make things easier for myself, I built Guix Ruby. A Guix channel that provides a guix ruby command which imports a Gemfile.lock and constructs Guix packages for all the listed dependencies.

To install it, just need to add this to your channels file (~/.config/guix/channels.scm):

(cons* (channel
(name 'guix-ruby)
(url "https://git.sr.ht/~czan/guix-ruby")
(branch "main"))
%default-channels)

Your next guix pull will add a new Guix command: guix ruby!

Basic Usage

Running guix ruby in the root of a Bundler project will read Gemfile and Gemfile.lock to produce a new file: Gemfile.lock.scm. It looks something like this:

(use-modules ...) ; imports
(lambda* (#:key (ruby ruby) (groups '(default)) (gem-transformers %default-gem-transformers))
...) ; gem definitions

You can then load this file in your manifest.scm or guix.scm file, and call the resulting function to get a list of Guix package objects which match your Bundler dependencies.

;; An example manifest.scm
(define bundler-packages (load "Gemfile.lock.scm"))
(packages->manifest
(cons* ruby (bundler-packages #:groups '(default development test))))

Too easy.

When you're first setting up a project, you can ask guix ruby to create a manifest.scm file by passing the --init flag. It will be a bit more complicated than the example above, because it is more explicit about the Ruby version that it uses.

Adding New Dependencies

When it's time to add dependencies, you may find that using bundle install does not work due to a read-only file system. Instead, you can use bundle lock to update Gemfile.lock, then run guix ruby to update Gemfile.lock.scm.

You can also ask guix ruby to do this in one step with the --lock flag.

Updating Dependencies

At the moment, guix ruby doesn't have a shortcut to update dependencies, but bundle lock --update can still be used.

Git Dependencies

Sometimes it's useful to have Bundler clone dependencies directly from Git. This is supported by guix ruby, which will write an appropriate Guix package definition. However, when using bundle lock and bundle lock --update, you may run into issues. Bundler tries to clone the referenced repository in order to get some information about it. Unfortunately, the way Gemfile.lock.scm defines packages causes this to break (because it defines GEM_HOME pointing to a read-only directory).

Setting GEM_HOME to point to some readable directory should be enough to get it working, at the expense of an extra clone of the repos:

$ bundle lock --update # no good :(
...
There was an error accessing `/gnu/store/...`
The underlying system error is Errno::EROFS: Read-only file system @ dir_s_mkdir
$ GEM_HOME=/tmp/bundle-cache bundle lock --update # works :)
Fetching https://git.sr.ht/...
Fetching gem metadata from https://rubygems.org/..........
Resolving dependencies...
Writing lockfile to /.../Gemfile.lock

Gem Transformers

You may have noticed the gem-transformers parameter defined in Gemfile.lock.scm. This provides a mechanism for you to provide "transformations" to the automatically generated packages, to do things like adding inputs that are needed to build extensions. A default set of transformers is built into Guix Ruby itself, but you can provide your own transformers if a Gem isn't already supported.

I'm keen to support as much as we can in Guix Ruby, though, so if you find you need to define a transformer then let me know on the mailing list and I can add it to the default set.

Try it out!

I've been using guix ruby for a while to manage the dependencies on my Ruby projects. I've managed to get a few complex projects working using it (including Rails!), and I'm finding it very convenient to use Guix for Ruby development. If you're looking to bring together Ruby and Guix, please try it out. If you run into any issues, let me know on the mailing list and I'll do my best to help you.