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!
Top comments (0)