Effortless Rails 7+ Deployment with Kamal Gem on DigitalOcean

shahzaib

Shah Zaib

Posted on July 30, 2024

Effortless Rails 7+ Deployment with Kamal Gem on DigitalOcean

Deploying a Rails application can often be daunting, especially when aiming for a production-ready setup. This guide will walk you through deploying a Rails 7.1+ application using the Kamal gem on Digital Ocean. By the end of this post, you'll have your app live and running with minimal hassle.

Prerequisites

  • Basic knowledge of Ruby on Rails
  • A Rails 7.1+ application
  • A Digital Ocean account
  • Docker installed on your local machine

Step 1: Install Kamal Gem

First, let's install the Kamal gem. Add the gem to your Gemfile and run the bundle command:

bundle add kamal && bundle install
Enter fullscreen mode Exit fullscreen mode

Step 2: Initialize Kamal Configuration

Initialize Kamal configuration by running the following command:

kamal init
Enter fullscreen mode Exit fullscreen mode

This command will generate two crucial files: .env and config/deploy.yml.

Step 3: Configure .env File

Add the following environment variables to your .env file:

RAILS_MASTER_KEY=XXXXXXXXXX
KAMAL_REGISTRY_PASSWORD=XXXXXXXXX
Enter fullscreen mode Exit fullscreen mode
  • RAILS_MASTER_KEY: Your Rails master key.
  • KAMAL_REGISTRY_PASSWORD: Your Docker Registry password. To find it, go to hub.docker.com, log in, and create a new token under Account Settings > Security > New Access Token.

Step 4: Update deploy.yml

Purchase a VPS from Digital Ocean and add its IP to the relevant field in deploy.yml. Update the configuration according to your project credentials.

Example config/deploy.yml

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

# Docker image
image: username/myapp

# Main server configuration
servers:
  web:
    hosts:
      - 192.168.0.1
    labels:
      traefik.http.routers.messi-web.rule: Host(`intellecta.app`)
      traefik.http.routers.messi-web.tls: true
      traefik.http.routers.messi-web.entrypoints: websecure
      traefik.http.routers.messi-web.tls.certresolver: letsencrypt

  job:
    hosts:
      - 192.168.0.1
    cmd: bundle exec sidekiq -q high_priority -q default -q mailers
Enter fullscreen mode Exit fullscreen mode

Step 5: Registry Configuration

Configure your Docker registry credentials in deploy.yml:

# Credentials for your image host.
registry:
  # Specify the registry server if you're not using Docker Hub
  # server: registry.digitalocean.com / ghcr.io / ...
  username: shahzaib
  password:
    - KAMAL_REGISTRY_PASSWORD
Enter fullscreen mode Exit fullscreen mode

Step 6: Set Environment Variables

Define your environment variables in deploy.yml:

env:
  clear:
    HOSTNAME: 192.168.0.1
    DB_HOST: 192.168.0.1
    RAILS_SERVE_STATIC_FILES: true
    RAILS_LOG_TO_STDOUT: true
  secret:
    - KAMAL_REGISTRY_PASSWORD
    - RAILS_MASTER_KEY
    - POSTGRES_PASSWORD
    - REDIS_URL
Enter fullscreen mode Exit fullscreen mode

Step 7: Configure Accessories

Set up your accessory services like PostgreSQL and Redis:

accessories:
  db:
    image: postgres:16
    host: 192.168.0.1
    env:
      clear:
        POSTGRES_DB: "myapp_production"
      secret:
        - POSTGRES_PASSWORD
    directories:
      - data:/var/lib/postgresql/data
  redis:
    image: redis:7.0
    host: 192.168.0.1
    directories:
      - data:/data
Enter fullscreen mode Exit fullscreen mode

Rails Database Configuration

Update your Rails database configuration in config/database.yml:

production:
  <<: *default
  username: myapp
  password: <%= ENV["POSTGRES_PASSWORD"] %>
  database: myapp_production
  host: <%= ENV["DB_HOST"] %>
Enter fullscreen mode Exit fullscreen mode

Step 8: Configure Traefik

Traefik handles TLS termination, HTTPS redirect, and zero downtime deployments. Add the following configuration to deploy.yml:

traefik:
  options:
    publish:
      - "443:443"
    volume:
      - "/letsencrypt/acme.json:/letsencrypt/acme.json"
  args:
    entryPoints.web.address: ":80"
    entryPoints.websecure.address: ":443"
    entryPoints.web.http.redirections.entryPoint.to: websecure
    entryPoints.web.http.redirections.entryPoint.scheme: https
    entryPoints.web.http.redirections.entrypoint.permanent: true
    certificatesResolvers.letsencrypt.acme.email: "support@intellecta.app"
    certificatesResolvers.letsencrypt.acme.storage: "/letsencrypt/acme.json"
    certificatesResolvers.letsencrypt.acme.httpchallenge: true
    certificatesResolvers.letsencrypt.acme.httpchallenge.entrypoint: web
Enter fullscreen mode Exit fullscreen mode

Step 9: Deployment Commands

Run the following commands to set up and deploy your app:

kamal setup
kamal env push
Enter fullscreen mode Exit fullscreen mode

After 210 seconds, your app will be live. To deploy changes, use:

kamal deploy
Enter fullscreen mode Exit fullscreen mode

Step 10: Troubleshooting Logs

If there are issues, check the logs:

kamal traefik logs
kamal app logs 
Enter fullscreen mode Exit fullscreen mode

For live logs:

kamal app logs -f
Enter fullscreen mode Exit fullscreen mode

Step 11: Access Rails Console

Access the Rails console using:

kamal app exec -i "bin/rails c"
Enter fullscreen mode Exit fullscreen mode

Step 12: Access Bash on Server

Access bash on the server using:

kamal server exec --interactive "/bin/bash"
Enter fullscreen mode Exit fullscreen mode

Final config/deploy.yml

Here's the overall final look of your config/deploy.yml:

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

# Name of the container image.
image: username/myapp

# Deploy to these servers.
servers:
  web:
    hosts:
      - 192.168.0.1
    labels:
      traefik.http.routers.messi-web.rule: Host(`intellecta.app`)
      traefik.http.routers.messi-web.tls: true
      traefik.http.routers.messi-web.entrypoints: websecure
      traefik.http.routers.messi-web.tls.certresolver: letsencrypt

  job:
    hosts:
      - 192.168.0.1
    cmd: bundle exec sidekiq -q high_priority -q default -q mailers

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

# Inject ENV variables into containers (secrets come from .env).
# Remember to run `kamal env push` after making changes!
env:
  clear:
    HOSTNAME: 192.168.0.1
    DB_HOST: 192.168.0.1
    RAILS_SERVE_STATIC_FILES: true
    RAILS_LOG_TO_STDOUT: true
  secret:
    - KAMAL_REGISTRY_PASSWORD
    - RAILS_MASTER_KEY
    - POSTGRES_PASSWORD
    - REDIS_URL

# Use accessory services (secrets come from .env).
accessories:
  db:
    image: postgres:16
    host: 192.168.0.1
    env:
      clear:
        POSTGRES_DB: "myapp_production"
      secret:
        - POSTGRES_PASSWORD
    directories:
      - data:/var/lib/postgresql/data
  redis:
    image: redis:7.0
    host: 192.168.0.1
    directories:
      - data:/data

traefik:
  options:
    publish:
      - "443:443"
    volume:
      - "/letsencrypt/acme.json:/letsencrypt/acme.json"
  args:
    entryPoints.web.address: ":80"
    entryPoints.websecure.address: ":443"
    entryPoints.web.http.redirections.entryPoint.to: websecure
    entryPoints.web.http.redirections.entryPoint.scheme: https
    entryPoints.web.http.redirections.entrypoint.permanent: true
    certificatesResolvers.letsencrypt.acme.email: "support@intellecta.app"
    certificatesResolvers.letsencrypt.acme.storage: "/letsencrypt/acme.json"
    certificatesResolvers.letsencrypt.acme.httpchallenge: true
    certificatesResolvers.letsencrypt.acme.httpchallenge.entrypoint: web
Enter fullscreen mode Exit fullscreen mode

By following these steps, you can easily deploy your Rails 7+ application using the

Kamal gem on Digital Ocean. This guide should help you set up a robust, production-ready environment with minimal effort. Happy deploying!

💖 💪 🙅 🚩
shahzaib
Shah Zaib

Posted on July 30, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related