Skip to main content
Presented by Collin for GoRails. Republished on AppSignal September 19, 2026. Rails 8.1 ships a CI runner. bin/ci runs your checks on your own machine, driven by a script at config/ci.rb that new applications generate for you. The argument for it is straightforward. A hosted runner is often slower than the laptop already in front of you, and for many projects the wait is the only thing it adds. The work was led by Jeremy Daer at 37signals.

What this covers

  • Reading and extending config/ci.rb.
  • Running the suite with bin/ci.
  • Reporting a local pass back to a GitHub pull request.

Requirements

The CI script

config/ci.rb lists the steps to run. The generated version calls scripts that were already in bin:
  • bin/setup prepares the environment.
  • bin/rubocop runs style checks.
  • Bundler Audit checks gems for known vulnerabilities.
  • Importmap Audit does the same for JavaScript.
  • The test suite runs.
Nothing here is hidden. Each step is an ordinary script in bin, readable and runnable on its own, so a failing step is easy to reproduce in isolation. Add steps for anything else your project needs, and remove ones that do not apply.

Run it

Output is one line per step, with a total at the end. A failure names the step that failed, and you fix it and run again. A full pass on a small application lands in the tens of seconds.

Sign off on a pull request

Running CI locally leaves GitHub with no record that it passed. The sign-off integration adds one: it posts a status check to the branch saying the checks ran and passed. Install the GitHub CLI, then the extension:
Then enable the sign-off block in config/ci.rb. On a passing run, it signs off on the commit and the check appears on the pull request. On a failing run, it does not. Two things trip this up the first time, and both produce the same “sign-off failed” message: Both are the same rule: sign-off attests to a commit that exists on the remote. Commit, push, then run bin/ci.

What this changes for your monitoring

Moving CI onto developer machines removes a checkpoint that also happened to be a record. Hosted CI logs every run against a commit; a local runner does not, beyond the sign-off check. That makes it worth being deliberate about the record you keep after the deploy:
  • Send a deploy marker on every deploy. Markers put a line on your graphs at the moment a revision went out, and group errors by the deploy that introduced them. With local CI, the marker is frequently the first durable record tying a revision to a time.
  • Watch the deploy rather than the build. A green local run and a green build server tell you the same thing, which is that your checks passed on the code you had. Neither tells you how the release behaved. Compare error rates and response times across the marker for the first few minutes after a deploy.
  • Keep the checks honest. bin/ci is a script your team can edit, and a step that gets commented out to unblock something has a way of staying commented out. It is worth reviewing config/ci.rb in code review the way you would review a workflow file.

Transcript

Transcribed from the video and lightly edited: the automatic captions misheard a number of product and API names, and those have been corrected.
0:02 Everybody, welcome back to another lesson here at GoRails. And this one, we’re going to dig into the local CLI that is now shipping with Rails 8.1. So if you go to the little right up they have on the Ruby on Rails.org site, you can see a little bit about the local CI here. This work was led by Jeremy Daer, okay, right here from 37 Signals. And the idea here is that you don’t need, you know, a cloud computer to run your CI setup when you can run it on a computer locally.0:39 That is, in many cases, a lot faster than running it on a cloud computer. So you can see here there’s already a default CI script set up here for you. So that lives in config/ci.rb and is run by the bin/ci command. So here is that file, right? This is the exact file as it ships with Rails.1:00 And you can see that there’s a series of steps that you can add more to this if you need. But there’s some, you know, basic steps here. But really all these do is they run other bin scripts that are in your application that perform things like, you know, setting up the development environment, running RuboCop against your code, running Bundler audit, importmap audit, so forth and so forth. So as you can see here, runs your tests. And now there’s also an optional GitHub integration here that can go ahead and sign off on pull requests here for you.1:33 Let’s say, hey, CI was run. This pull request is good to go. You can go ahead and merge it if you wish. Obviously you could edit that message here, but there’s the default one. And in order to use this, you also need to install this extension from Basecamp, the gh-signoff extension.1:49 And, you know, it’s pretty simple here. If CI succeeds, it’ll go ahead and sign off on it and say it’s always good. Otherwise, it’ll go ahead and fail it, and say, hey, don’t merge this. Something needs to be fixed first. So let’s go ahead and look at this in practice.2:03 I have an application right now where we have the CI script here. Now I have the GitHub stuff commented out for right now. Let’s just go ahead and run this and see what that looks like. All right, so I’m just gonna go ahead and run the bin/ci command here. And we’ll see that’s gonna go ahead and run all of those steps that are defined over in that config CI.rb file.2:22 You can see here we have a failure. I’ll go back up to it in a second. Something about import maps that audit failed for whatever reason, we can go look at that. So here you can see at the bottom that one failure that we had right here, so continuous integration failed in 20.23 seconds, which is pretty fast. And if we scroll back up, we can see here this is our one failure here.2:44 There’s an importmap vulnerability audit that was failed. So we can go look at this and see what the issue is. We can go fix that in our code and then rerun the CI. So I’ll go ahead and go do that now. All right, so let’s go ahead and rerun the CI now and see if it passes or fails or anything else.2:57 Okay, that’s how we passed our importmap audit. So that’s good. We should be all good to go now from this point on, ‘cause I believe that was the only failure that we had. So we have some duplication warnings here though. Okay, so now we can see that on this run, the continuous integration passed.3:14 So that’s nice and all for ourselves locally. But what if we need to communicate this on GitHub? We’re opening an import request. So I’ve gone ahead and made a modification to update that read me for this code, nothing big. We’re just gonna use it as an example for running the CI.3:27 So now, in order to use the gh-signoff stuff, again, we’ll need to have this GitHub CLI installed and install this Basecamp extension. So I do have the GitHub CLI installed already. I can show you that by just running GH here and we can see that this is, in fact, GitHub’s CLI right here. So the only thing that we need to install is this extension here, which I do not have installed. So let’s go ahead and copy that command and run that.3:53 So we’ll go ahead and install that gh-signoff extension for Basecamp. Okay, good. So we have that done. And now let’s go ahead and uncomment the conditional here. Okay, save that.4:05 And now let’s go ahead and let’s run our CI again here. Now that the CI’s run this time, we see that the sign off failed. And the reason being is because even though I made a branch for updating the read me here, it has, we have uncommitted and un-post changes here locally. So we need to go ahead and commit those things first. So I’m just going to go ahead and look at a quick, get status here.4:27 And then, yeah, that’s all good. Let’s see, we’ll go ahead and add all that and commit it. We’ll say update read me and enable GitHub CLI in CI. Okay, so now we do a good set. Should be all good to go here.4:46 And now let’s go ahead and run that bin/ci command again. And let’s see what happens this time. Okay, so it failed again. We committed our changes, but also we did not push those up yet. So we see here that that probably has fallen into this category of that we still have un-post changes.5:02 So let’s go ahead and push this up. I’ll go ahead and open that up over here. And then we’ll just add a description here. It’s a very simple test of the new local CI in Rails 8.1. So let’s go ahead and make a pull request here.5:15 So let’s go ahead and run this bin/ci again here. Let’s see what happens. Okay, so now this time we see that now that we’ve committed the changes, pushed them up, we get this successful sign off here. So we see signed off on the commit here. And then we get the message, you know, sign off all systems go, break from merge and deploy, past in, you know, 0.8 seconds, continuous integration overall past in 23.37 seconds.5:43 And now if we come back to GitHub here and we scroll down on this section here, if we look in the checks section here, we can actually see that the sign off check was actually, you know, pushed up to the branch here. It says I signed off on it and we got the check. So it’s good to go, right? So now that that local CI has run, we can now use that in combination with the GitHub CLI in order to sign off on checks and say, you know, hey, we ran these locally, they everything passed successfully and we’re good to go ahead and merge this. You know, we wouldn’t actually need to go and do any of this stuff.6:20 Cause we’ve all done that locally. We don’t need to do this stuff, you know, in the GitHub CI here. So pretty cool stuff. Once, you know, get everything coordinated here, the sign off thing’s really cool. And also, you know, all of this code here in the CI script here is all stuff that you can run, you know, yourself and add to and go check out, you know, like you can go look at any of these files, been setups in your Rails app.6:44 So it’s been RuboCop. They’re all in the bin directory in your Rails application already here. You know, the bundle or stuff, you can run yourself as well. But I mean, they’re all, it’s all in here available. If you need to go look at any of that code, you can just simply go to any of those files in bin and you can see they’re all in here.7:01 For example, there’s a setup on Rubocops right above it. Bonderful audits in there. So really some cool stuff here in the with the local CI edition in Rails 8.1. So definitely, I would say go check it out, see if it works for you. You might be able to stream on some of your CI setup and just drop down to using this and maybe add on to this as needed or take away as needed, wherever you want to do, you know?7:26 So with that, I hope you enjoyed this lesson and I will see you next time.

About this tutorial

This tutorial summarizes a GoRails screencast on the local CI runner in Rails 8.1, presented by Collin. The screencast is the original work. GoRails publishes it, and the rest of the series, at gorails.com.