Reason #166 • June 15th, 2026

Trapping signals with Signal.trap

For long-running processes, it's often desirable to trap signals sent by the operating system to gracefully handle shutdown, reload configuration, or handle other lifecycle events. In Ruby this couldn't be easier with Signal.trap:

Ruby
jobs = Array.new(10_000, &:itself)
shutdown = false

Signal.trap("INT") do
  puts
  puts "Shutting down gracefully after the next job is completed..."
  shutdown = true
end

puts "Running... Press Ctrl+C to exit."

until shutdown
  job = jobs.shift

  # Simulate doing some work
  sleep 0.5
  puts "Completed job #{job}"
end
    

Example output:

Running... Press Ctrl+C to exit.
Completed job 0
Completed job 1
Completed job 2
Completed job 3
Completed job 4
^C
Shutting down gracefully after the next job is completed...
Completed job 5

The most common signals to trap are INT (sent when the user presses Ctrl+C) and TERM (sent by default when you run kill <pid>). You can also trap HUP to reload configuration without shutting down, or USR1 and USR2 for custom application-specific signals.

A full list of supported signals can be found by looking at the Signal.list hash.

You can also ignore signals by passing "IGNORE" as a second argument. This can be useful to prevent termination on unsupported signals, e.g. Signal.trap("HUP", "IGNORE").

History

Signal.trap shipped in Ruby 1.8.0 in 2003.

Originally, Ruby provided signal trapping via Kernel#trap, to mimic the shell command. This method is still around, but Signal.trap reads more clearly in my opinion.

The underlying idea comes from Unix signal handling, in particular the C signal / sigaction APIs and the shell trap command.

Reason #167 ?