Effortless Rails 7+ Deployment with Kamal Gem on DigitalOcean
Shah Zaib
Posted on July 30, 2024
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
Step 2: Initialize Kamal Configuration
Initialize Kamal configuration by running the following command:
kamal init
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
-
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
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
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
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
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"] %>
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
Step 9: Deployment Commands
Run the following commands to set up and deploy your app:
kamal setup
kamal env push
After 210 seconds, your app will be live. To deploy changes, use:
kamal deploy
Step 10: Troubleshooting Logs
If there are issues, check the logs:
kamal traefik logs
kamal app logs
For live logs:
kamal app logs -f
Step 11: Access Rails Console
Access the Rails console using:
kamal app exec -i "bin/rails c"
Step 12: Access Bash on Server
Access bash on the server using:
kamal server exec --interactive "/bin/bash"
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
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!
Posted on July 30, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.