PWSHub News

Deploy a Rails app to a VPS with Kamal

Ruby on Rails has always been at the forefront of developer productivity, and building robust, highly interactive web applications has never been easier. Rails has been accurately described as the one-person framework. However, building your app is only half the battle. The other half is publishing it for others to use. Unfortunately, being able to deploy your application has always been an afterthought for most developers. The "one-person framework" doesn't carry through when it comes to deployment and operations unless you have specialized knowledge of building out infrastructure or pay others large sums of money to do it for you—until now. With the release of Kamal, the "one-person framework" idea completes the stack from end to end, allowing a single developer to do more with fewer resources.

Kamal is a new deployment tool created by the folks at 37Signals. It allows you to codify the deployment process into a single file and abstract many complicated bits that most applications don't need to worry about. It does this by using a combination of software like Docker containers and Traefik. Out of the box, along with Rails, you get a complete package that will get you production-ready in no time.

How it works

Kamal runs your web application in a Docker container on a server. It then uses a web server called Traefik to handle the network traffic between the Docker container and the internet.

The neat thing is that whenever you deploy a new version of your application, Kamal will do the following:

  1. Build a new version of a Docker image.
  2. Start a new container of that image.
  3. Ensure that the new container is healthy.
  4. Tell Traefik to start directing traffic to the new container.
  5. Stop the old container.

The benefit is that you can theoretically have zero downtime and blue/green deploys.

Kamal can also help scale your application horizontally by deploying your containers to multiple servers—but you'll need to load balance the servers yourself.


Install the Kamal gem on your local computer:

Then, in your Rails application, initialize the setup:

The kamal init command will create two files. config/deploy.yml contains everything Kamal will use to deploy your app, and .env has secret environment variables used to configure your app (and should not get committed to your repo).

Acquire a VPS

Before configuring Kamal, let's discuss where we'll host our web app. There are lots of great options to choose from. An excellent cost-effective choice is a Virtual Private Server (VPS). A VPS is cost-effective because you are essentially sharing hardware with other customers. Although completely isolated from each other access-wise, you may have to contend with others for resources, which means some CPU bandwidth, memory, and storage limitations. A VPS should be fine for most applications, especially for this tutorial.

When choosing a VPS hosting provider, find one with a data center closest to you for faster response times. When selecting the level of resources, at a minimum, I'd recommend something with at least 1GB of memory for Rails applications. Also, choose a Debian-based Linux for the OS, as it's a popular choice, and it will be easy to find help if you run into trouble. Make sure that you have SSH access to the VPS. After creating your VPS, make a note of the published IP Address.

Acquire a domain name

If you plan to enable TLS/SSL for your application, you'll need to register a domain name and configure the DNS with a CNAME record to point to the IP address of your VPS.

Docker image registry

Kamal uses Docker containers for deployment. It takes your Dockerfile and builds an image that will be used on your server. This image must be uploaded to a "registry." However, there are many free options to choose from, such as DockerHub, GitHub, or DigitalOcean. You'll need to sign up for one of these services and note the username and password you'll use to log in (or access tokens if applicable).


Now that you have your VPS and registry login credentials, we can start configuring the deployment. At a minimum, your config/deploy.yml file should look like this:

service: my-app
image: <your-registry-username>/my-app
      - <your vps ip address>
    - <your-registry-username>
# If you use another service like DigitalOcean:
# registry:
#   server:
#   username:
#     - <your-registry-username>
#   password:

Fill in the required values as needed. Two environment variables will need our attention: KAMAL_REGISTRY_PASSWORD and RAILS_MASTER_KEY. These get populated by the .env file upon deployment. Edit your .env file to look something like this:


These values can be safely stored here as they shouldn't be committed to your repo, and they get securely transferred to the VPS when deployed. The RAILS_MASTER_KEY can be found in config/credentials/production.key. If it doesn't exist, you can create it by running the following:

rails credentials:edit --environment=production


The first thing you'll want to do is run the server bootstrap command:

This command will SSH into your VPS and set up and install everything necessary for Kamal to run your application. It's a good idea to run this separately first in case of any failures; if there are any, they'll be easier to spot and diagnose. It only needs to be run once.

Next, we can start the deploy:

The deploy command will start the Docker build process locally and then push the image to your registry. Next, Kamal will boot a Traefik container on your server. Kamal will then start the healthcheck process container to ensure it boots and runs without issues. If successful, it'll start a permanent container that Traefik will see and begin directing traffic to it. If everything goes well, your Rails application should be available on port 80 of your VPS. Of course, Rails requires SSL by default for newer applications and will redirect you if you try to access it.


In production, you'll want to set up your web application with TLS/SSL to protect your users' privacy. Luckily, the Traefik server has built-in functionality to help us do that! We'll need to make a few modifications to config/deploy.yml. First, tell Kamal that our Traefik server requires TLS/SSL. Add the following to deploy.yml.

      - 443:443
      - "letsencrypt:/letsencrypt"
    entrypoints.web.address: ':80'
    entrypoints.websecure.address: ':443'
    certificatesResolvers.letsencrypt.acme.httpchallenge: true
    certificatesResolvers.letsencrypt.acme.httpchallenge.entrypoint: web <your email address here> /letsencrypt/acme.json

This config tells Traefik to use LetsEncrypt to acquire an SSL certificate and store it in a mounted volume. A mounted volume is crucial because it allows the certificate to persist if the container or server restarts. Port 443 is the default SSL port. All traffic that arrives via SSL can be tagged as coming from the websecure entry point.

Next, we need to tell Traefik where to send this SSL traffic. Modify the servers part of the deploy.yml:

      - <your vps ip address>
      traefik.http.routers.maildown-web.rule: 'Host(`<your domain name>`)'
      traefik.http.routers.maildown-web.entrypoints: websecure
      traefik.http.routers.maildown-web.tls.certresolver: letsencrypt

By adding these Docker labels to your application container, Traefik will act as an SSL termination point and direct all websecure traffic to the application. Let's deploy these changes:

First, we reboot Traefik to pick up the new configuration:

Then, we redeploy our application to pick up the new labels:

If everything goes well, you can now access your web app via https://.

Where to go from here

I hope that you've found this introduction to Kamal useful. There is a lot more we can do with Kamal. Look for more blog posts about Kamal coming soon. In the meantime, check out some of these links for other Kamal features:


Related stories
1 month ago - Every Supabase project comes with a full Postgres database, a free and open source database which is considered one of the world's most stable and advanced databases. Postgres is an ideal choice for your Ruby on Rails applications as...
2 weeks ago - Honeybadger co-founder Joshua Wood explains how to graph Ahoy page views in Rails with Chartkick, with a preview of our upcoming observability tool—Insights!
3 weeks ago - How to build a picture guessing game with Twilio Twilio is known among developers for having the go-to APIs for delivery notifications, promotional...
5 days ago - Webhooks, originally proposed as a way to consume asynchronous feeds, became the one-size-fits-all solution for integrating cloud software. Here’s why we think there’s a better solution.
Other stories
1 hour ago - Using voice memos to brainstorm ideas is quick, and easier than ever thanks to AI-powered apps. Here's how to get started.
1 hour ago - Mistral Medium only came out two months ago, and now it's followed by Mistral Large. Like Medium, this new model is currently only available via their API. It scores well …
1 hour ago - Have you started learning React, only to face bugs that made you contemplate a career in goat herding? Don't worry – we've all been there. In this guide, you'll join me on a quest through the quirky wonders of React. I'll help you...
1 hour ago - The Geolocation API is a standard API implemented in browsers to retrieve the location of the people who are interacting with a web application. This API enable users to send their location to a web application to enable relevant...
1 hour ago - Explore the intricacies of reading and writing files in various formats, handling directories, mastering error-handling techniques, and harnessing the efficiency of streams for powerful, performance-boosting file operations.
1 hour ago - WordPress is a popular content management system (CMS) known for its flexibility and user-friendly interface. But its built-in editor is not ideal for collaborative editing. Many publishers who work with writers need collaborative writing...
4 hours ago - Performance testing is an important yet underrated field of software development. And it’s a must-have skill that can help you prevent common software failures that occur in production applications. Performance testing is a routine...
5 hours ago - In nearly all of my recent explorations of Generative AI, I've come to realize how important prompts are. That hasn't necessarily translated me into...