Deploy Ruby On Rails:
Ubuntu 20.04 Focal Fossa in 2024

The definitive guide to deploying Ruby on Rails to production to your Virtual Private Server (VPS).

In this guide, we'll deploy Ruby on Rails using Kamal. Kamal is a deployment tool that uses Docker containers to deploy your application with free SSL certificates and zero downtime deployments.

First, we need a server to deploy our code to. A Virtual Private Server (VPS) is a common choice but you can also deploy to a bare metal server too.

Here are some hosts you could use. We're fans of DigitalOcean and Hetzner.

We'll create an Ubuntu server on DigitalOcean in this example, but the process is the same for most hosting providers.

Create A Server

Let's start with Create -> Droplet on DigitalOcean's dashboard. Choose a region near you or your users.

Choose the latest Ubuntu LTS version as the OS. LTS versions get long-term support which is ideal for production.

Choose a size for your server. You'll want at least 1GB of RAM but this will depend on your application's needs.

Configuring Kamal

Kamal is a free, open source, zero-downtime deployment tool. New Rails applications come with Kamal pre-installed and configured for the dependencies you've chosen (like PostgreSQL).

If you have an older application, you can add Kamal to the Gemfile and initialize it with the following command.

bundle add kamal && kamal init

Kamal has 2 main files to configure:

The deploy yaml file contains the server IP, roles, domain, and other details for where to deploy your application. The secrets file contains secrets (like API tokens and passwords) like your Docker registry token and Rails master key needed for deployment.

Docker Registry

We'll need a Docker registry to store the image for our production application. We'll use Docker Hub for this.

Login to Docker Hub and create a Personal Access Token with Read & Write permissions.

Once created, save this key somewhere secure. Then we will add it to our terminal to use with Kamal.

export KAMAL_REGISTRY_PASSWORD=my-secret-token

Let's update config/deploy.yml with our Docker Hub username and server IP address.

Change my_app to the name of your application, my-user to your Docker Hub username and 192.168.0.1 to your server's IP. Some of these are used in a couple places, so make sure to update all of them.

# Name of your application. Used to uniquely configure containers.
service: my_app

# Name of the container image.
image: your-user/my_app

# Deploy to these servers.
servers:
web:
  - 192.168.0.1

# Enable SSL auto certification via Let's Encrypt (and allow for multiple apps on one server).
# Set ssl: false if using something like Cloudflare to terminate SSL (but keep host!).
proxy:
ssl: true
host: app.example.com

# Credentials for your image host.
registry:
# Specify the registry server, if you're not using Docker Hub
# server: registry.digitalocean.com / ghcr.io / ...
username: your-user

# Always use an access token rather than real password when possible.
password:
  - KAMAL_REGISTRY_PASSWORD

# Inject ENV variables into containers (secrets come from .kamal/secrets).
env:
secret:
  - RAILS_MASTER_KEY
clear:
  # Run the Solid Queue Supervisor inside the web server's Puma process to do jobs.
  # When you start using multiple servers, you should split out job processing to a dedicated machine.
  SOLID_QUEUE_IN_PUMA: true

# Aliases are triggered with "bin/kamal <alias>". You can overwrite arguments on invocation:
# "bin/kamal logs -r job" will tail logs from the first server in the job section.
aliases:
console: app exec --interactive --reuse "bin/rails console"
shell: app exec --interactive --reuse "bash"
logs: app logs -f
dbc: app exec --interactive --reuse "bin/rails dbconsole"

# Use a persistent storage volume for sqlite database files and local Active Storage files.
# Recommended to change this to a mounted volume path that is backed up off server.
volumes:
- "my_app_storage:/rails/storage"

# Bridge fingerprinted assets, like JS and CSS, between versions to avoid
# hitting 404 on in-flight requests. Combines all files from new and old
# version inside the asset_path.
asset_path: /rails/public/assets

# Configure the image builder.
builder:
arch: amd64

Adding Database Servers

The above is the bare bones configuration for Kamal to deploy Rails with SQLite as the database. Your SQLite database will be stored in storage which is mounted as a volume on the host OS. This preserves your database across deploys.

For other databases like PostgreSQL, MySQL or Redis, you will want to add accessories for each service. These services will be run as separate Docker containers and can run on the same or separate machines.

Here are some example accessories for PostgreSQL, MySQL, and Redis.

# Use accessory services (secrets come from .kamal/secrets).
accessories:
postgres:
  image: postgres:17
  host: 192.168.0.1
  port: 5432:5432
  env:
    clear:
      POSTGRES_USER: postgres
      POSTGRES_DB: my_app_production
    secret:
      - POSTGRES_PASSWORD
  directories:
    - data:/var/lib/postgresql/data

mysql:
  image: mysql:8
  host: 192.168.0.1
  # Change to 3306 to expose port to the world instead of just local network.
  port: "127.0.0.1:3306:3306"
  env:
    clear:
      MYSQL_ROOT_HOST: '%'
    secret:
      - MYSQL_ROOT_PASSWORD
  files:
    - config/mysql/production.cnf:/etc/mysql/my.cnf
    - db/production.sql:/docker-entrypoint-initdb.d/setup.sql
  directories:
    - data:/var/lib/mysql

redis:
  image: redis:7
  host: 192.168.0.1
  port: 6379
  directories:
    - data:/data
You may want to create a new Rails application with a matching database to copy the example Kamal config.

Deploy your Rails application

Now that we've told Kamal how to access Docker Hub and our server, it's time to deploy.

For our first run, we need to set up the server to get it ready for deployments and deploy our app for the first time.

kamal setup

Open your server's IP address (or domain) in your browser to view your newly deployed app!

You can deploy new changes with the deploy command.

kamal deploy

Want to stay up-to-date with Ruby on Rails?

Join 86,651+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.