Debugging Ruby with the debug Gem
The debug gem is Ruby’s official debugging solution. It replaces the older lib/debug.rb standard library and gives you a fast, non-intrusive way to inspect your running code. One of its biggest advantages is that it adds zero performance overhead when you are not actively debugging.
Requirements
You need Ruby MRI 2.7 or later. Ruby 3.1 and later ship with the debug gem as a bundled gem, so no separate installation is required for those versions. If you are on Ruby 2.6 or earlier, this gem will not work — consider using byebug or pry-byebug instead.
Installation
For Ruby versions below 3.1, install the gem manually:
gem install debug
Or add it to your Gemfile:
gem "debug", ">= 1.0.0"
Always specify version 1.0.0 or higher. The debug gem below version 1.0.0 is an older, completely different gem that is no longer maintained.
Two Ways to Debug
Source Modification Mode
Add require 'debug' at the top of your file and place binding.break where you want execution to pause:
require 'debug'
def factorial(n)
return 1 if n <= 1
n * factorial(n - 1)
end
binding.break # execution stops here
result = factorial(5)
p result
# => 120
When you run this file, the debugger stops at the binding.break line and opens an interactive console. From there you can inspect variables and step through code.
binding.break has two aliases: binding.b and debugger. All three do exactly the same thing.
CLI Mode with rdbg
If you prefer not to modify your source code, use the rdbg command-line tool:
rdbg target.rb
This starts your script under the debugger immediately, pausing at the first line. You can also attach rdbg to a running process:
rdbg --attach -- rake test
Or open your script in Chrome DevTools:
rdbg --open target.rb
Debug Console Commands
Once the debugger stops, you get an (rdbg) prompt. Here are the most useful commands:
| Command | Short | Description |
|---|---|---|
break | b | Set a breakpoint |
step | s | Step into a method |
next | n | Step over the current line |
continue | c | Resume normal execution |
info | — | Show local variables and current frame |
p <expr> | — | Evaluate and print an expression |
catch | — | Break when an exception is raised |
trace | — | Trace method calls |
config | — | Show or change debugger settings |
A typical session looks like this:
(rdbg) info locals
=>#0 factorial(n=5) at target.rb:5
%self => main
n => 5
(rdbg) p n * 10
=>50
(rdbg) next
Use info to see what variables are available in the current scope, then use p to evaluate expressions with those variables.
Conditional Breakpoints
You can make a breakpoint conditional by passing a Ruby expression to binding.break:
require 'debug'
counter = 0
10.times do |i|
binding.break if i == 7 # stop only when i equals 7
counter += 1
end
p counter
# => 10
Execution pauses only when the condition evaluates to true. This saves you from manually continuing through unwanted stops.
Key Methods
The main API you will use is binding.break, available in three forms:
binding.break # unconditional breakpoint
binding.b # alias
debugger # alias
binding.break if condition # conditional
You can also inspect the source location of any binding using Binding#source_location, which is part of Ruby’s core API (available since Ruby 2.5):
loc = binding.source_location
p loc # => ["target.rb", 5]
This returns a two-element array containing the file path and line number.
rdbg Command Options
The rdbg command has several useful flags:
rdbg -n target.rb— run in non-stop mode, meaning the debugger starts but does not pause at the first linerdbg --stop target.rb— stop at the very first line before any code executesrdbg --attach target.rb— attach to an already running Ruby processrdbg --open target.rb— open in Chrome DevTools for a visual debugging experiencerdbg --port 12345 target.rb— start a debug server on a specific port for remote debugging
Remote Debugging
You can debug Ruby processes running on other machines or in containers by starting a debug server:
rdbg --port 12345 target.rb
This opens a TCP/IP server on port 12345. You can then connect to it from VSCode using the vscode-rdbg extension, or from Chrome DevTools by pointing your browser to the same port.
UNIX domain sockets are also supported if you prefer local connections without network exposure.
Frontend Integrations
The debug gem supports multiple frontends:
| Frontend | Connection Type | Extra Requirements |
|---|---|---|
| Console (rdbg) | UDS or TCP | None |
| VSCode | UDS or TCP | vscode-rdbg extension |
| Chrome DevTools | TCP/IP | Chrome browser |
The built-in console mode works out of the box. For VSCode, install the vscode-rdbg extension and configure a launch.json that points to your running debug server.
Common Gotchas
A few things that trip people up:
Wrong gem version. Always specify >= 1.0.0 in your Gemfile. Older versions are completely different software.
JRuby is not supported. The debug gem is MRI-only. On JRuby, use ruby-debug (a separate gem) instead, which provides equivalent functionality for that runtime.
Ractor support is incomplete. If you are working with Ruby’s experimental concurrency features, be aware that the debugger may not handle all cases correctly.
Thread debugging has edge cases. Most thread scenarios work, but you may encounter quirks in complex threading situations.
require 'debug' placement matters. The debug gem must be required before any code you want to debug. If you require it after your problematic code, that code will not be instrumented.
SIGINT suspends the debugger. Pressing Ctrl-C while the debugger is running suspends execution and drops you into the debug console at the nearest safe point. This is usually helpful, but be aware it can interrupt long-running operations.
See Also
- Kernel Methods — learn about
Kernel#bindingand related methods that power the debug gem - Ruby Keywords — understand reserved keywords that appear in conditional breakpoints and debugger expressions