Deploying Rails Applications
Deploying a Rails application involves several steps: setting up a server, configuring the database, managing assets, and running the application in production mode.
Production Checklist
Before deploying, ensure:
- Database is configured for production
- Secret keys are set via environment variables
- Assets are precompiled
- Proper error handling is in place
- Logging is configured
- Background jobs are set up (if needed)
Environment Configuration
Database
yaml
1# config/database.yml
2production:
3 adapter: postgresql
4 encoding: unicode
5 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
6 database: <%= ENV['DATABASE_NAME'] %>
7 username: <%= ENV['DATABASE_USERNAME'] %>
8 password: <%= ENV['DATABASE_PASSWORD'] %>
9 host: <%= ENV['DATABASE_HOST'] %>Secrets and Credentials
bash
1# Edit encrypted credentials
2rails credentials:edit
3
4# For production-specific
5rails credentials:edit --environment productionruby
1# Access credentials in code
2Rails.application.credentials.secret_key_base
3Rails.application.credentials.dig(:aws, :access_key_id)Environment Variables
ruby
1# config/environments/production.rb
2config.require_master_key = true
3
4# Use environment variables
5config.action_mailer.smtp_settings = {
6 address: ENV['SMTP_ADDRESS'],
7 port: ENV['SMTP_PORT'],
8 user_name: ENV['SMTP_USERNAME'],
9 password: ENV['SMTP_PASSWORD']
10}Deploying to Render
Create render.yaml
yaml
1# render.yaml
2databases:
3 - name: myapp-db
4 databaseName: myapp_production
5 user: myapp
6
7services:
8 - type: web
9 name: myapp
10 env: ruby
11 buildCommand: "./bin/render-build.sh"
12 startCommand: "bundle exec puma -C config/puma.rb"
13 envVars:
14 - key: DATABASE_URL
15 fromDatabase:
16 name: myapp-db
17 property: connectionString
18 - key: RAILS_MASTER_KEY
19 sync: falseBuild Script
bash
1#!/usr/bin/env bash
2# bin/render-build.sh
3set -o errexit
4
5bundle install
6bundle exec rails assets:precompile
7bundle exec rails assets:clean
8bundle exec rails db:migrateDeploying to Heroku
bash
1# Install Heroku CLI
2# Create app
3heroku create myapp
4
5# Add PostgreSQL
6heroku addons:create heroku-postgresql:mini
7
8# Set master key
9heroku config:set RAILS_MASTER_KEY=$(cat config/master.key)
10
11# Deploy
12git push heroku main
13
14# Run migrations
15heroku run rails db:migrate
16
17# Open app
18heroku openProcfile
# Procfile
web: bundle exec puma -C config/puma.rb
worker: bundle exec sidekiq
release: bundle exec rails db:migrate
Deploying with Docker
Dockerfile
dockerfile
1# Dockerfile
2FROM ruby:3.3.0-slim
3
4# Install dependencies
5RUN apt-get update -qq && \
6 apt-get install --no-install-recommends -y \
7 build-essential libpq-dev nodejs npm && \
8 rm -rf /var/lib/apt/lists/*
9
10WORKDIR /rails
11
12# Install gems
13COPY Gemfile Gemfile.lock ./
14RUN bundle install
15
16# Copy application
17COPY . .
18
19# Precompile assets
20RUN SECRET_KEY_BASE_DUMMY=1 bundle exec rails assets:precompile
21
22EXPOSE 3000
23CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]Docker Compose
yaml
1# docker-compose.yml
2version: '3.8'
3
4services:
5 db:
6 image: postgres:15
7 environment:
8 POSTGRES_PASSWORD: password
9 volumes:
10 - postgres_data:/var/lib/postgresql/data
11
12 web:
13 build: .
14 command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails server -b 0.0.0.0"
15 volumes:
16 - .:/rails
17 ports:
18 - "3000:3000"
19 depends_on:
20 - db
21 environment:
22 DATABASE_URL: postgres://postgres:password@db/myapp_production
23 RAILS_ENV: production
24 RAILS_MASTER_KEY: ${RAILS_MASTER_KEY}
25
26volumes:
27 postgres_data:Deploying to VPS (Ubuntu)
Server Setup
bash
1# Update system
2sudo apt update && sudo apt upgrade -y
3
4# Install dependencies
5sudo apt install -y curl git-core zlib1g-dev build-essential \
6 libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 \
7 libxml2-dev libxslt1-dev libcurl4-openssl-dev \
8 libffi-dev nodejs npm nginx postgresql postgresql-contrib
9
10# Install Ruby (using rbenv)
11git clone https://github.com/rbenv/rbenv.git ~/.rbenv
12git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
13echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
14echo 'eval "$(rbenv init -)"' >> ~/.bashrc
15source ~/.bashrc
16
17rbenv install 3.3.0
18rbenv global 3.3.0
19gem install bundlerNginx Configuration
nginx
1# /etc/nginx/sites-available/myapp
2upstream puma {
3 server unix:///home/deploy/myapp/shared/tmp/sockets/puma.sock;
4}
5
6server {
7 listen 80;
8 server_name example.com;
9 root /home/deploy/myapp/current/public;
10
11 location ^~ /assets/ {
12 gzip_static on;
13 expires max;
14 add_header Cache-Control public;
15 }
16
17 try_files $uri/index.html $uri @puma;
18
19 location @puma {
20 proxy_pass http://puma;
21 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
22 proxy_set_header Host $http_host;
23 proxy_redirect off;
24 }
25}Systemd Service
ini
1# /etc/systemd/system/puma.service
2[Unit]
3Description=Puma HTTP Server
4After=network.target
5
6[Service]
7Type=simple
8User=deploy
9WorkingDirectory=/home/deploy/myapp/current
10Environment=RAILS_ENV=production
11ExecStart=/home/deploy/.rbenv/bin/rbenv exec bundle exec puma -C config/puma.rb
12Restart=always
13
14[Install]
15WantedBy=multi-user.targetSSL with Let's Encrypt
bash
1# Install Certbot
2sudo apt install certbot python3-certbot-nginx
3
4# Get certificate
5sudo certbot --nginx -d example.com -d www.example.com
6
7# Auto-renewal
8sudo certbot renew --dry-runMonitoring
Error Tracking
ruby
1# Gemfile
2gem 'sentry-ruby'
3gem 'sentry-rails'
4
5# config/initializers/sentry.rb
6Sentry.init do |config|
7 config.dsn = ENV['SENTRY_DSN']
8 config.environment = Rails.env
9 config.traces_sample_rate = 0.5
10endPerformance Monitoring
ruby
1# Gemfile
2gem 'newrelic_rpm'
3gem 'skylight'Deployment is just the beginning - monitoring and maintaining your app is equally important!
