Ruby
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Methods and Blocks
-
Section 4: Object-Oriented Ruby
-
Section 5: Metaprogramming
-
Section 6: Working with Data and Files
-
Section 7: Ruby Frameworks Overview
-
Section 8: Ecosystem and Testing
-
Section 9: Practical Projects
-
Section 10: Interview Practice
-
Section 11: Data Structures and Algorithms in Ruby
-
Section 12: More Practice Exercises
-
Section 13: Enumerable and Functional Style
-
Section 14: More OOP Practice
-
Section 15: More Testing
-
Section 16: Enumerable and Comparable Modules In Depth
-
Section 17: Ruby Standard Library: Core Utilities
-
Section 18: Ruby Standard Library: Data and Security
-
Section 19: Ruby Standard Library: CLI and Text
-
Section 20: Ruby Networking
-
Section 21: Ruby on Rails Deep Dive
-
Section 22: Ruby Metaprogramming Deep Dive
-
Section 23: Ruby Design Patterns
-
Section 24: Ruby Concurrency
-
Section 25: Ruby Testing Deep Dive
-
Section 26: Ruby Gems and Packaging
-
Section 27: Ruby Performance
-
Section 28: More Practice Exercises
-
Section 29: More Interview Practice
-
Section 30: Rails API Development
-
Section 31: Rails Authentication and Authorization
-
Section 32: Rails Testing Deep Dive
-
Section 33: Rails Performance
-
Section 34: Rails Deployment
-
Section 35: Sinatra and Lightweight Ruby Web Apps
-
Section 36: More Ruby Language Deep Dive
-
Section 37: Ruby 3.x Modern Features
-
Section 38: More Data Structures in Ruby
-
Section 39: More Practical Projects
-
Section 40: Ruby Ecosystem Tools
-
Section 41: More Practice and Review
-
Section 42: Final Practice and Mastery
-
Section 43: Ruby Interview Deep Dive
-
Section 44: Ruby Background Processing Deep Dive
-
Section 45: Ruby GraphQL
-
Section 46: Ruby Object Model Deep Dive
-
Section 47: Ruby Hanami Framework Overview
-
Section 48: Ruby gRPC and Protocol Buffers
-
Section 49: Ruby Data Processing
-
Section 50: Ruby Search Integration
-
Section 51: Ruby File Upload and Media
-
Section 52: Ruby Email and Notifications
-
Section 53: Ruby Admin Panels
-
Section 54: Ruby Feature Flags and Experimentation
-
Section 55: Ruby Monitoring and Observability
-
Section 56: Ruby Docker and Deployment Deep Dive
-
Section 57: Ruby Security Deep Dive
-
Section 58: More Advanced Metaprogramming
-
Section 59: More Final Projects
139: Deploying Rails with Kamal
Imagine you're moving into a new house. In the old days of deployment, you'd move in with a giant list of instructions: "Buy this specific brand of lightbulbs, install the flooring exactly like this, and make sure the plumbing is exactly version 2.1." If the house had slightly different wiring than you expected, the whole thing would fall apart the moment you flipped a switch. That's how we used to deploy apps—hoping the server's environment perfectly matched our local machine.
Kamal changes that. Instead of giving the server a list of instructions, Kamal lets you build a "shipping container." You pack your Ruby version, your gems, your compiled assets, and your OS settings into one sealed box. When you deploy, you aren't installing software on the server; you're just dropping that container onto the plot of land and telling it to run. If it works on your machine, it works on the server, because the environment is inside the box.
Setting the Stage with Docker and SSH
Before we touch Kamal, you need two things: a place to store your "containers" (a Docker Registry) and a way to talk to your server (SSH). I usually recommend Docker Hub for the registry, though GitHub Packages works great too. You'll need a VPS—something like a DigitalOcean Droplet or a Hetzner instance—running Ubuntu.
The beauty here is that you don't need to install Ruby or Rails on the server itself. The server only needs Docker installed. That's it. I've spent way too many hours of my life debugging "missing shared library" errors on production servers; Kamal effectively kills that entire category of headache.
Mapping the BookWorm App to deploy.yml
Let's say we're deploying an app called BookWorm. To get Kamal working, we focus on the deploy.yml file. This is the brain of your deployment. Instead of a complex script, it's a declarative list of where the app goes and how it behaves.
service: bookworm
image: your-docker-hub-username/bookworm
servers:
- 123.45.67.89 # Your VPS IP
registry:
username: your-docker-hub-username
password:
- KAMAL_REGISTRY_PASSWORD
env:
clear:
PORT: 3000
secret:
- RAILS_MASTER_KEY
- DATABASE_URL
Notice the distinction between clear and secret environments. I always tell my juniors: if it's a password or a key, it must be in secret. Kamal handles these by pulling them from your local .env file and securely injecting them into the container at runtime, so your secrets never actually live in your version control.
The Magic of Zero-Downtime Deploys
One of the things that makes Kamal feel like magic is how it handles updates. It doesn't just kill the old version of BookWorm and start the new one—that would leave your users staring at a 502 error for a few seconds.
Instead, Kamal starts the new container on a different port. Once that new container signals that it's healthy and ready to take traffic, Kamal tells Traefik (the load balancer it installs for you) to flip the switch. The traffic shifts to the new version instantly, and only then does the old container get shut down. It's a seamless handoff.
When you're ready, you just run kamal deploy. Kamal builds the image locally (or on a remote builder), pushes it to your registry, pulls it down to your servers, and performs that switch. If something goes sideways, kamal rollback is your best friend—it just flips the traffic back to the previous container that's still sitting there on the server.
📋 Practical Task
Configuring BookWorm for a Dual-Server High Availability Setup
You've successfully deployed BookWorm to a single server, but your user base is growing. You've just purchased a second VPS to ensure that if one server goes down, the app stays online.
Modify your deploy.yml to achieve the following:
- Add a second server IP (use
98.76.54.32as the second IP). - Configure the
rolesattribute for the servers so that the first server (123.45.67.89) handles both thewebandjobroles, but the second server (98.76.54.32) only handles thewebrole. - Add a new secret environment variable called
STRIPE_API_KEYto theenv: secretsection.
Write out the updated deploy.yml content that reflects these changes.
There are no comments for now.