Ruby BEGIN and END Hooks - Startup and Shutdown Code
last modified October 18, 2026
This tutorial explores Ruby's BEGIN and END keywords.
BEGIN blocks run before the main program, and END
blocks run when it exits.
The BEGIN keyword defines code that executes before the rest of the
file. The END keyword registers code that runs at exit. These
uppercase keywords are not the lowercase begin ... end block used
for exception handling.
BEGIN blocks are useful for setup, while END blocks
handle cleanup. END runs on normal termination, on
exit, and when an uncaught exception terminates the program. It is
skipped by exit!, exec, and fatal signals. Multiple
BEGIN blocks run in definition order, and multiple
END blocks run in reverse order.
Basic BEGIN and END Example
This simple example demonstrates the execution order of BEGIN and END blocks. Notice they run outside the normal program flow.
BEGIN {
puts "This runs first"
}
END {
puts "This runs last"
}
puts "This runs in between"
# Output:
# This runs first
# This runs in between
# This runs last
BEGIN runs before any other statement, and END runs
after the main program finishes. The output therefore appears in three distinct
phases.
Multiple BEGIN/END Blocks
Ruby allows multiple BEGIN and END blocks.
BEGIN blocks run in definition order, while END blocks
execute in reverse order (like a stack).
BEGIN {
puts "BEGIN 1"
}
BEGIN {
puts "BEGIN 2"
}
END {
puts "END 1"
}
END {
puts "END 2"
}
puts "Main program"
# Output:
# BEGIN 1
# BEGIN 2
# Main program
# END 2
# END 1
BEGIN blocks are queued in the order they appear.
END blocks behave like a stack, so the last one defined runs first.
BEGIN with Variable Initialization
BEGIN blocks can initialize variables used in the main program.
This example sets up configuration before the main code runs.
BEGIN {
$app_name = "My Ruby Program"
$start_time = Time.now
}
puts "Welcome to #{$app_name}" # prints "Welcome to My Ruby Program"
puts "Started at #{$start_time}"
END {
puts "Program ran for #{Time.now - $start_time} seconds"
}
Global variables are available everywhere, including inside methods and
END blocks. A local variable created in a BEGIN block
is visible at the top level but not inside a method. The END block
uses the saved start time to report the runtime.
Error Handling in END Blocks
END blocks execute even when the program exits early or raises
exceptions. This makes them ideal for cleanup tasks.
BEGIN {
puts "Starting risky operation"
}
END {
puts "Cleanup always happens"
}
fail_now = true
raise "Something went wrong!" if fail_now
puts "This might not run"
Here fail_now is true, so the raise
terminates the main program. END still executes, and the final
puts is skipped. Change fail_now to
false to exercise the normal path.
Remember that exit!, exec, and fatal signals bypass
END blocks.
File Operations with BEGIN/END
A common use case is file handling - opening in BEGIN and closing
in END. This keeps the file open for the whole run and closes it
when the program terminates normally or with an uncaught exception.
BEGIN {
$log_file = File.open("output.log", "w")
$log_file.puts "Log started: #{Time.now}"
}
def log(message)
$log_file.puts message
end
log("Processing started")
END {
$log_file.puts "Log ended: #{Time.now}"
$log_file.close
puts "Log file closed" # prints "Log file closed"
}
log("Processing completed")
The example opens output.log before any logging and closes it in
END. Even if the main program raises, the file is closed. The
script writes the log to the working directory.
BEGIN with Command-line Arguments
BEGIN blocks can process ARGV before the main program.
This example handles early argument parsing.
# Run: ruby argv_processing.rb --verbose
BEGIN {
$verbose = ARGV.delete("--verbose")
puts "Verbose mode enabled" if $verbose
}
puts "Remaining arguments: #{ARGV.inspect}"
END {
puts "Verbose logging complete" if $verbose
}
# Output:
# Verbose mode enabled
# Remaining arguments: []
# Verbose logging complete
BEGIN can edit ARGV before the main program reads it.
With --verbose, the script prints both verbose messages; without
it, $verbose is nil and those messages are omitted.
Complex Program Structure
This example shows BEGIN/END in a larger program with multiple components. The blocks help organize setup and teardown.
BEGIN {
require 'json'
$config = { "api_endpoint" => "https://example.com" }
}
BEGIN {
$stats = {
start: Time.now,
requests: 0
}
}
def make_request(url)
$stats[:requests] += 1
"response from #{url}"
end
puts make_request($config["api_endpoint"]) # prints "response from https://example.com"
END {
duration = Time.now - $stats[:start]
puts "Made #{$stats[:requests]} requests in #{duration.round(6)} seconds"
File.write("stats.json", JSON.dump($stats))
}
The two BEGIN blocks load JSON support and initialize counters.
The END block reports the request count and writes
stats.json. The request is simulated, so no network connection is
needed.
Source
This tutorial covered Ruby's BEGIN and END keywords with practical examples showing startup, cleanup, and exit handling.
Author
List all Ruby tutorials.