Ruby for Automation: Building Small Microservices and Self-Contained CLI Tools
While Python often dominates the conversation around automation and scripting in the DevOps world, Ruby remains a powerhouse for developers who value developer happiness, expressive syntax, and robust tooling.
Ruby is not just for building massive web applications with Rails. Its elegance shines brightly when building small, self-contained Command Line Interface (CLI) tools and lightweight microservices. In this post, we’ll explore how to leverage Ruby for automation, how to package your tools effectively, and why you might choose it over the competition.
Why Ruby for Automation?
Ruby’s greatest strength is its readability. Automation scripts often start small but grow into complex beasts. Ruby’s syntax allows these scripts to remain readable and maintainable long after they have been written. Features like blocks, iterators, and a comprehensive standard library make text processing and file manipulation—core tasks of automation—feel natural.
Building Robust CLI Tools
For simple scripts, Ruby’s built-in OptionParser is excellent. However, for more complex automation requiring subcommands (git-style), the gem Thor is the industry standard.
Here is an example of a simple CLI tool using Thor that manages a hypothetical service:
# server_cli.rb
require 'thor'
class ServerCLI < Thor
desc "start PORT", "Start the server on the specified PORT"
def start(port)
puts "Starting server on port #{port}..."
# Logic to start a process could go here
end
desc "deploy ENV", "Deploy to a specific environment (staging/production)"
def deploy(env)
if env == "production"
puts "Deploying to PRODUCTION! Caution recommended."
else
puts "Deploying to #{env}..."
end
end
end
ServerCLI.start(ARGV)
To run this, you simply execute ruby server_cli.rb start 8080. This level of structure is hard to beat with ad-hoc shell scripts.
Lightweight Microservices with Sinatra
Sometimes automation requires an HTTP interface—perhaps a webhook listener for GitHub actions or a tiny internal API. Booting up a full Rails app is overkill here. Enter Sinatra.
Sinatra allows you to spin up a microservice in a single file:
# app.rb
require 'sinatra'
require 'json'
post '/webhook' do
payload = JSON.parse(request.body.read)
if payload['status'] == 'success'
puts "Build passed! Triggering deployment..."
status 200
else
puts "Build failed."
status 400
end
end
With just a few lines, you have a functional endpoint ready to integrate into your automation pipeline.
Packaging with Bundler
One of the historical pain points of scripting is dependency management ("It works on my machine!"). Ruby solves this elegantly with Bundler.
Single-File Scripts with bundler/inline
For automation tasks that require external gems but aren't large enough to warrant a full project directory, you can use bundler/inline. This allows you to define dependencies directly inside the script file:
#!/usr/bin/env ruby
require 'bundler/inline'
gemfile do
source 'https://rubygems.org'
gem 'http'
gem 'rainbow' # For colored terminal output
end
require 'http'
require 'rainbow'
response = HTTP.get("https://api.github.com")
puts Rainbow("GitHub Status: #{response.code}").green
When you share this script, the user doesn't need to manually gem install anything. Ruby handles it upon execution.
Testability
Automation code is often production code, yet it is rarely tested as such. Ruby’s testing culture is pervasive. Tools like Minitest (which comes with Ruby) allow you to write tests for your scripts effortlessly.
require 'minitest/autorun'
require_relative 'my_script_logic'
class TestAutomation < Minitest::Test
def test_directory_creation
FileStubber.stub do
assert_output(/Directory created/) { create_backup_dir }
end
end
end
Because Ruby is object-oriented, you can easily isolate logic into classes and test them, moving away from the fragile "run it and see if it breaks" approach common in Bash scripting.
Ruby vs. Python for Automation
Python is the giant in this space, largely due to its pre-installation on most Linux distros and heavy use in AI/ML. However, Ruby offers distinct advantages:
- blocks and Procs: Ruby’s approach to closures (blocks) makes working with files, streams, and collections significantly more ergonomic than Python's iterators.
- String Processing: Ruby has arguably better support for regex and string manipulation out of the box, inherited from its Perl roots.
- DSL Capabilities: Ruby allows you to create Domain Specific Languages easily (like Chef or Puppet), which can make configuration management scripts much more readable.
While Python might be the default choice, Ruby is often the better choice for developers who prioritize the developer experience and code maintainability.
Conclusion
Whether you are writing a quick one-off script using bundler/inline, a structured CLI tool with Thor, or a tiny microservice with Sinatra, Ruby offers a mature, stable, and joy-inducing ecosystem for automation. Next time you reach for Bash or Python, give Ruby a try—you might just find yourself enjoying the process.

