Setting Up a Debugger and Intellisense for Ruby on Rails 8 in Visual Studio Code/Cursor/Windsurf/etc
If you are confused by this guide or notice a mistake, please get in touch so I can fix it.
So, allegedly it’s very easy to set up a debugger for Ruby on Rails in Visual Studio Code. That was not my experience. Fortunately, after a few hours of messing around, I got it working pretty robustly with the following versions:
- Ruby on Rails 8.0.2
 - Visual Studio Code 1.100.2
 - Debug 1.10.0
 - rdbg 0.1.0
 - foreman 0.88.1
 
Debugger
It works somewhat fine if I directly start rails server with rdbg, but it’s not robust and does not work with bin/dev so you can’t automatically run auxillary services, so, this setup launches bin/dev in the background and then attaches.
You can also find all the files here.
Packages
gem "rdbg" # Ruby debug integration
gem "foreman" # Necessary for the procfile to work
Visual Studio Code
Install the Visual Studio Code rdbg Ruby Debug extension by Koichi Sasada.
Configuration Files
Update or create the following files:
.vscode/launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "rdbg",
      "name": "Run bin/dev & Attach rdbg",
      "request": "attach",
      "preLaunchTask": "runBinDev",
      "localfs": true,
      "localfsMap": "${workspaceFolder}:${workspaceFolder}"
    },
    {
      "type": "rdbg",
      "name": "Attach with rdbg",
      "request": "attach",
      "localfs": true,
      "localfsMap": "${workspaceFolder}:${workspaceFolder}"
    },
  ]
}
.vscode/tasks.json
Define the pre-launch task to start up the server using bin/dev. Note that using this method, the server will keep running when you stop debugging, and you can re-attach by running the same debug configuration again.
{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "runBinDev",
      "type": "shell",
      "command": "bin/dev",
      "isBackground": true,
      "problemMatcher": [
        {
          "owner": "custom-rdbg-task",
          "pattern": [
            {
              "regexp": "^.*DEBUGGER: Debugger can attach.*$",
              "kind": "info",
              "file": 1,
              "location": 1,
              "message": 0
            }
          ],
          "background": {
            "activeOnStart": true,
            "beginsPattern": "^.*DEBUGGER: Debugger can attach.*$",
            "endsPattern": "^.*DEBUGGER: Debugger can attach.*$"
          }
        }
      ],
      "presentation": {
        "reveal": "always",
        "panel": "dedicated",
        "clear": true
      },
      "group": {
        "kind": "build",
        "isDefault": false
      }
    }
  // If you have other tasks, they would be here
  ]
}
Procfile.dev
Make sure the web:  line looks like this, with a call to rdbg, and leave the rest the same:
web: RUBY_DEBUG_OPEN=TRUE RUBY_DEBUG_NONSTOP=TRUE rdbg --command --open --stop-at-load -- bundle exec bin/rails server -p 3000
You can leave bin/dev intact.
I am not sure how it would work if you had multiple processes you want to debug. You might be able to open both of them with rdbg and see if they attach to both simultaneously.
Testing
When you now go to the ‘Run and Debug’ tab, you can now select the “Run bin/dev & Attach rdbg” and run it. After selecting it, you can also use the shortcut F5.
You can set breakpoints on the left side of the editor next to the line number, and enable automatically pausing on exceptions and errors in the bottom of the “Run and Debug” tab.
As I said before, using this method, the server will keep running when you stop debugging, and you can re-attach by running the same debug configuration again.
Troubleshooting
Error during server startup when “rescue any exception” or “rescue RuntimeError” is enabled
I am also not sure why this happens, but it is harmless. You can either press continue until you’ve skipped through all the errors, or untick those boxes for startup. If you know how to get around this, please let me know.
Cannot find the TCP port
You can try to explicitly define a port in the rdbg call in Procfile.dev and the attach call in launch.json
In Procfile.dev, add --port=12345 after the --command --open part of the rdbg command.
In launch.json, add the following line to both launch configurations:
"port": 12345
Making sure the debugger gem is working
You can temporarily add an initializer, like debug.rb, to the initializers folder:
if defined?(DEBUGGER__)
  puts "Debugger support loaded successfully"
  puts "Current directory: #{Dir.pwd}"
  puts "Rails root: #{Rails.root}" if defined?(Rails)
  # Force a breakpoint to check if debugging works at all
  binding.break
end
If you launch with rdbg and it stops at binding.break, you can be confident that the debug and rdbg gems are set up fine.
Intellisense
There are two ways to set up intellisense in Visual Studio Code: Solargraph and Ruby LSP. Solargraph is older and more robust, but Ruby LSP is newer, faster, has more features and better Rails integration.
Solargraph
install the VS Code Extension.
Update your Gemfile to include the Solargraph gem:
group :development, :test do
  gem "solargraph", require: false
end
Run this command to install the gem and generate the binstubs
bundle install
bundle binstubs solargraph
Create a .solargraph.yml file in the project root:
include:
- "**/*.rb"
exclude:
- spec/**/*
- test/**/*
- vendor/**/*
- ".bundle/**/*"
require: []
domains: []
reporters:
- rubocop
- require_not_found
formatter:
  rubocop:
    cops: safe
    except: []
    only: []
    extra_args: []
require_paths: []
plugins: []
max_files: 5000
Restarting should now start Solagraph. After restarting, check if you have any notifications in the bottom of your screen giving any solargraph issues. If it says the bin/solargraph folder is missing, make sure to generate binstubs as per above. Hover over a function definition in a Ruby file to see if anything pops up with context or start typing and see if it starts auto-completing (manually trigger with ctrl+space).
Ruby LSP
I can’t quite get this working in my setup, but it might help you on your way:
Install the VS Code add-on.
and add to your Gemfile:
gem "ruby-lsp-rails", require: false
Restart Visual Studio Code, and open a Ruby file. There should be a {} Ruby tab in the bottom right. Click this for the Ruby LSP status.
You might need to add the Ruby version manager manually to your .vscode/settings.json file. Find the options here:
{
  "rubyLsp.rubyVersionManager": {
    "identifier": "auto/none/custom/asdf/chruby/rbenv/rvm/shadowenv/mise",
  },
}
In my case, it could not find my mise install, so I had to set it manually (which I did globally):
{
  "rubyLsp.rubyExecutablePath": "<path-to-your-ruby-as-I-got-from-"mise bin-paths">"
}
Typechecking with Sorbet and Tapioca
You can also set up type-checking for Ruby with Sorbet and Tapioca. Tapioca is still in alpha and it’s not working for me, but it should go like this.
Install the VS Code extension.
Add the gems to your Gemfile:
gem "sorbet", group: :development
gem "sorbet-runtime"
gem "tapioca"
Then run the following commands:
bundle install
bundle exec tapioca init
bin/tapioca dsl
bin/tapioca gems
srb tc
If this doesn’t work, you can check the sorbet and tapioca docs.
Subscribe to the Newsletter
Enjoyed this article? Subscribe to get notified when I publish new content.