Skip to content

Deploying a Rails App

Ruby on Rails is a powerful web application framework written in Ruby that follows the Model-View-Controller (MVC) architectural pattern. Rails emphasizes convention over configuration, rapid development, and clean code, making it ideal for building scalable, maintainable web applications quickly. With built-in support for databases, caching, authentication, and asset management, Rails provides a comprehensive toolkit for modern web development.

This guide explains how to deploy a Rails application to Klutch.sh, both with and without a Dockerfile, along with database configuration, environment setup, persistent storage, security best practices, and production deployment strategies.

Prerequisites

  • Ruby 3.0 or higher
  • Rails 6.0 or higher
  • Git and a GitHub account
  • Klutch.sh account
  • Basic knowledge of Ruby and Rails development

Getting Started: Installing Rails and Creating an App

  1. Install Ruby (if not already installed):

    Use rbenv or RVM for version management:

    Terminal window
    rbenv install 3.2.0
    rbenv local 3.2.0
  2. Install Rails:

    Terminal window
    gem install rails
  3. Create a new Rails app with PostgreSQL:

    Terminal window
    rails new my-rails-app --database=postgresql
    cd my-rails-app
  4. Create the database locally:

    Terminal window
    rails db:create
  5. Start the development server:

    Terminal window
    rails server

    Your app should be running at http://localhost:3000.

Sample Code for Getting Started

Create a scaffold for a basic Article resource:

Terminal window
rails generate scaffold Article title:string content:text published:boolean
rails db:migrate

Update config/routes.rb to set the root route:

Rails.application.routes.draw do
resources :articles
root to: 'articles#index'
# Health check for monitoring
get '/health', to: proc { [200, {}, ['OK']] }
end

Create a basic controller to handle requests:

app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
before_action :set_article, only: [:show, :edit, :update, :destroy]
def index
@articles = Article.all
end
def show
end
def new
@article = Article.new
end
def create
@article = Article.new(article_params)
if @article.save
redirect_to @article, notice: 'Article was successfully created.'
else
render :new
end
end
def edit
end
def update
if @article.update(article_params)
redirect_to @article, notice: 'Article was successfully updated.'
else
render :edit
end
end
def destroy
@article.destroy
redirect_to articles_url, notice: 'Article was successfully deleted.'
end
private
def set_article
@article = Article.find(params[:id])
end
def article_params
params.require(:article).permit(:title, :content, :published)
end
end

Deploying Without a Dockerfile (Using Nixpacks)

Klutch.sh uses Nixpacks to automatically detect and build your Rails application. Nixpacks analyzes your project and determines the necessary dependencies, build steps, and runtime configuration.

Steps to Deploy with Nixpacks

  1. Prepare your Rails app for production:

    Update Gemfile to ensure production gems are included:

    # Gemfile
    source "https://rubygems.org"
    git_source(:github) { |repo| "https://github.com/#{repo}.git" }
    ruby "3.2.0"
    gem "rails", "~> 7.0.0"
    gem "pg", "~> 1.1"
    gem "puma", "~> 5.0"
    gem "sass-rails", ">= 6"
    gem "webpacker", "~> 5.0"
    gem "turbolinks-rails"
    gem "jbuilder", "~> 2.7"
    gem "redis", "~> 4.0"
    gem "bcrypt", "~> 3.1.7"
    gem "image_processing", "~> 1.2"
    gem "aws-sdk-s3", require: false
    gem "pundit"
    gem "kaminari"
    group :production do
    gem "rack-cors"
    end
    group :development, :test do
    gem "byebug", platforms: [:mri, :mingw, :x64_mingw]
    gem "rspec-rails"
    end
    group :development do
    gem "web-console", ">= 4.1.0"
    gem "listen", "~> 3.3"
    end
  2. Commit your code to GitHub:

    Terminal window
    git add .
    git commit -m "Initial Rails app setup"
    git push origin main
  3. Log in to Klutch.sh dashboard:

    Visit https://klutch.sh/app

  4. Create a new project:

    • Click “Create Project”
    • Enter your project name (e.g., “My Rails App”)
    • Select your organization or personal account
  5. Create a new app:

    • Click “Create App”
    • Select your Rails GitHub repository
    • Select the branch (typically main or master)
    • Select HTTP as the traffic type
    • Set the internal port to 3000 (the default Rails port)
    • Choose your desired region, compute power, and number of instances
  6. Add environment variables:

    In the app creation form, add these essential environment variables:

    RAILS_ENV=production
    RAILS_MASTER_KEY=<your-production-master-key>
    DATABASE_URL=postgresql://user:password@host:5432/dbname
    REDIS_URL=redis://user:password@host:6379/0
    SECRET_KEY_BASE=<your-secret-key>
  7. Deploy:

    Click “Create” to deploy. Klutch.sh will automatically:

    • Detect your Rails app
    • Install dependencies with bundle install
    • Run migrations with rails db:migrate
    • Precompile assets
    • Start the Rails server on port 3000

Your app will be available at example-app.klutch.sh.

Customizing Build and Start Commands

If you need to customize the build or start command for Nixpacks, add these environment variables during app creation:

NIXPACKS_BUILD_CMD=rails db:migrate && rails assets:precompile
NIXPACKS_START_CMD=rails server -b 0.0.0.0 -p 3000

Deploying With a Dockerfile

For more control over your build process and runtime environment, you can provide a custom Dockerfile. Klutch.sh will automatically detect and use a Dockerfile in your repository’s root directory.

Creating a Multi-Stage Dockerfile

  1. Create a Dockerfile in your project root:

    # Build stage
    FROM ruby:3.2-slim as builder
    # Install build dependencies
    RUN apt-get update && apt-get install -y build-essential libpq-dev && rm -rf /var/lib/apt/lists/*
    WORKDIR /app
    # Copy Gemfile and lock file
    COPY Gemfile Gemfile.lock ./
    # Install gems
    RUN bundle install --without development test
    # Copy application code
    COPY . .
    # Precompile assets
    RUN bundle exec rake assets:precompile
    # Production stage
    FROM ruby:3.2-slim
    # Install runtime dependencies
    RUN apt-get update && apt-get install -y libpq5 && rm -rf /var/lib/apt/lists/*
    WORKDIR /app
    # Copy gems from builder
    COPY --from=builder /usr/local/bundle /usr/local/bundle
    # Copy application from builder
    COPY --from=builder /app /app
    # Expose port
    EXPOSE 3000
    # Health check
    HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD curl -f http://localhost:3000/health || exit 1
    # Start Rails server
    CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0", "-p", "3000"]
  2. Commit the Dockerfile to GitHub:

    Terminal window
    git add Dockerfile
    git commit -m "Add production Dockerfile"
    git push origin main
  3. Log in to Klutch.sh dashboard:

    Visit https://klutch.sh/app

  4. Create a new app:

    • Click “Create App”
    • Select your Rails GitHub repository with the Dockerfile
    • Select the branch
    • Select HTTP as the traffic type
    • Set the internal port to 3000
    • Choose region, compute, and instances
  5. Add environment variables:

    RAILS_ENV=production
    RAILS_MASTER_KEY=<your-production-master-key>
    DATABASE_URL=postgresql://user:password@host:5432/dbname
    REDIS_URL=redis://user:password@host:6379/0
    SECRET_KEY_BASE=<your-secret-key>
  6. Deploy:

    Click “Create”. Klutch.sh will automatically build your Docker image and deploy your app.


Database Configuration

Rails applications typically require a persistent database. Here’s how to configure PostgreSQL and MySQL with Klutch.sh.

PostgreSQL Configuration

  1. Update config/database.yml to use the DATABASE_URL environment variable:

    default: &default
    adapter: postgresql
    encoding: unicode
    pool: <%= ENV.fetch("DB_POOL") { 5 } %>
    url: <%= ENV['DATABASE_URL'] %>
    development:
    <<: *default
    database: my_rails_app_development
    test:
    <<: *default
    database: my_rails_app_test
    production:
    <<: *default
    database: my_rails_app_production
  2. During app creation on Klutch.sh, set:

    DATABASE_URL=postgresql://username:password@postgres-host:5432/my_rails_app_production
  3. Run migrations:

    After deployment, execute migrations:

    Terminal window
    rails db:migrate RAILS_ENV=production

MySQL Configuration

  1. Create a Rails app with MySQL:

    Terminal window
    rails new my-rails-app --database=mysql
  2. Update config/database.yml:

    default: &default
    adapter: mysql2
    encoding: utf8mb4
    pool: <%= ENV.fetch("DB_POOL") { 5 } %>
    url: <%= ENV['DATABASE_URL'] %>
    development:
    <<: *default
    database: my_rails_app_development
    test:
    <<: *default
    database: my_rails_app_test
    production:
    <<: *default
    database: my_rails_app_production
  3. Set the DATABASE_URL environment variable:

    DATABASE_URL=mysql2://username:password@mysql-host:3306/my_rails_app_production

Environment Variables

Rail applications use environment variables for configuration in production. Here are the essential variables:

RAILS_ENV=production
RAILS_MASTER_KEY=<your-master-key-from-config/master.key>
SECRET_KEY_BASE=<generated-secure-key>
DATABASE_URL=<your-database-connection-string>
REDIS_URL=redis://:password@redis-host:6379/0
LOG_LEVEL=info
RACK_ENV=production

Generating Rails Master Key

If deploying a new Rails app:

Terminal window
rails credentials:edit

This creates config/master.key. Copy the value and set it as the RAILS_MASTER_KEY environment variable on Klutch.sh.


Caching with Redis

Rails can use Redis for caching to improve performance. Configure Redis in your Rails app:

Install Redis Gem

Add to Gemfile:

gem "redis", "~> 4.0"
gem "hiredis", "~> 0.6.0"

Run bundle install.

Configure Redis in Rails

  1. Update config/environments/production.rb:

    Rails.application.configure do
    config.cache_store = :redis_cache_store, {
    url: ENV.fetch('REDIS_URL'),
    connect_timeout: 1,
    read_timeout: 1,
    write_timeout: 1,
    reconnect_attempts: 0
    }
    config.session_store = :cache_store, key: '_my_app_session'
    end
  2. Set the REDIS_URL environment variable on Klutch.sh:

    REDIS_URL=redis://:password@redis-host:6379/0
  3. Use caching in your models and controllers:

    # In controller
    def index
    @articles = Rails.cache.fetch('articles', expires_in: 1.hour) do
    Article.all.to_a
    end
    end

Persistent Storage

Rails applications often need persistent storage for uploaded files, logs, and user data. Klutch.sh supports mounting persistent volumes.

Configuring Active Storage with Local Files

  1. Install Active Storage (if not already installed):

    Terminal window
    rails active_storage:install
    rails db:migrate
  2. Configure config/environments/production.rb:

    Rails.application.configure do
    config.active_storage.service = :local_disk
    end
  3. Update config/storage.yml:

    local_disk:
    service: Disk
    root_path: <%= ENV['STORAGE_PATH'] || 'storage' %>
    aws_s3:
    service: S3
    access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %>
    secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %>
    region: <%= ENV['AWS_REGION'] %>
    bucket: <%= ENV['AWS_S3_BUCKET'] %>
  4. On Klutch.sh, attach a persistent volume:

    • During app creation, navigate to the “Storage” section
    • Click “Add Volume”
    • Set mount path to /app/storage
    • Allocate size (e.g., 10GB)
    • The volume will be mounted and available at that path
  5. Set the STORAGE_PATH environment variable:

    STORAGE_PATH=/app/storage

Using AWS S3 for File Storage

For production applications, AWS S3 is recommended:

  1. Add the AWS SDK gem to your Gemfile:

    gem "aws-sdk-s3", require: false
  2. Update config/environments/production.rb:

    Rails.application.configure do
    config.active_storage.service = :aws_s3
    end
  3. Set S3-related environment variables on Klutch.sh:

    AWS_ACCESS_KEY_ID=<your-access-key>
    AWS_SECRET_ACCESS_KEY=<your-secret-key>
    AWS_REGION=us-east-1
    AWS_S3_BUCKET=my-rails-bucket
  4. Use file uploads in your models:

    class Article < ApplicationRecord
    has_one_attached :featured_image
    validates :featured_image, presence: true
    end

Security Best Practices

1. Use HTTPS

Ensure your Rails app forces HTTPS in production:

config/environments/production.rb
Rails.application.configure do
config.force_ssl = true
config.ssl_options = { hsts: { subdomains: true } }
end

2. Configure CORS for APIs

If your Rails app serves APIs to frontend applications:

config/initializers/cors.rb
# Gemfile
gem "rack-cors"
class Rack::Cors
allow do
origins 'example-app.klutch.sh', 'yourdomain.com'
resource '*', headers: :any, methods: [:get, :post, :put, :delete]
end
end

3. Set Strong Session Configuration

config/initializers/session_store.rb
Rails.application.config.session_store :cookie_store,
key: '_my_app_session',
secure: true,
httponly: true,
same_site: :strict

4. Use Devise for Authentication

Add to Gemfile:

gem "devise"

Install and configure:

Terminal window
rails generate devise:install
rails generate devise User
rails db:migrate

5. Implement Authorization with Pundit

Add to Gemfile:

gem "pundit"

Create policies:

app/policies/article_policy.rb
class ArticlePolicy
attr_reader :user, :article
def initialize(user, article)
@user = user
@article = article
end
def update?
user.admin? || article.user_id == user.id
end
def destroy?
update?
end
end

6. Secure Credentials Management

Use Rails credentials for sensitive data:

Terminal window
rails credentials:edit

Access in code:

api_key = Rails.application.credentials.stripe_api_key

7. Enable Content Security Policy

config/initializers/content_security_policy.rb
Rails.application.config.content_security_policy do |policy|
policy.default_src :self
policy.font_src :self, :https
policy.img_src :self, :https, :data
policy.style_src :self, :https, :unsafe_inline
policy.script_src :self, :https
end

Monitoring and Logging

Health Check Endpoint

Add a health check route for monitoring:

config/routes.rb
get '/health', to: proc { [200, {}, ['OK']] }

Logging Configuration

Configure logging in config/environments/production.rb:

Rails.application.configure do
config.log_level = :info
config.log_tags = [:request_id, :timestamp]
config.log_formatter = ::Logger::Formatter.new
# Send logs to stdout for container logging
config.logger = ActiveSupport::Logger.new($stdout)
end

Application Performance Monitoring

Add a monitoring gem like newrelic_rpm:

# Gemfile
gem "newrelic_rpm"

Set the New Relic license key:

NEW_RELIC_LICENSE_KEY=<your-license-key>

Custom Domains

To use a custom domain with your Rails app on Klutch.sh:

  1. Access the app settings in your Klutch.sh dashboard

  2. Navigate to the “Domains” section

  3. Add your custom domain (e.g., example.com)

  4. Update your DNS records at your domain registrar:

    Create a CNAME record pointing to example-app.klutch.sh

    Name: www
    Type: CNAME
    Value: example-app.klutch.sh
  5. Wait for DNS propagation (usually 5-30 minutes)

  6. Verify the domain in Klutch.sh

Your app will be accessible at www.example.com.


Troubleshooting

1. App Won’t Start - Missing Dependencies

Problem: Deployment fails with “bundler: command not found”

Solution:

  • Ensure Gemfile and Gemfile.lock are committed to Git
  • Run bundle lock --add-platform x86_64-linux locally and push
  • Verify no local-only gems are in your Gemfile

2. Database Migration Failures

Problem: App crashes with “PendingMigrationError”

Solution:

  • Set RAILS_ENV=production environment variable

  • Run migrations manually in your app container:

    Terminal window
    rails db:migrate RAILS_ENV=production
  • Check your DATABASE_URL is correct and database is accessible

3. Asset Precompilation Issues

Problem: Stylesheets and JavaScript not loading

Solution:

  • Ensure you have a build command: rails assets:precompile
  • Check that webpacker gem is in your Gemfile
  • Verify the RAILS_MASTER_KEY environment variable is set correctly
  • Run locally: RAILS_ENV=production rails assets:precompile

4. Redis Connection Errors

Problem: Redis cache errors in logs

Solution:

  • Verify REDIS_URL is set correctly

  • Check Redis server is running and accessible

  • Test connection locally: redis-cli -u <your-redis-url> ping

  • Add error handling in production config:

    config.cache_store = :redis_cache_store,
    { url: ENV.fetch('REDIS_URL'), error_handler: ->(method:, returning:, exception:) { } }

5. Persistent Storage Issues

Problem: Files uploaded to persistent volume don’t persist after restart

Solution:

  • Verify the persistent volume is mounted at /app/storage
  • Check that STORAGE_PATH=/app/storage is set
  • Ensure your Active Storage configuration points to the correct path
  • Verify the volume size is sufficient
  • Check file permissions: run chmod -R 755 /app/storage

Best Practices for Rails on Klutch.sh

  1. Use environment variables for all configuration - Never hardcode sensitive values or environment-specific settings in your code.

  2. Implement database connection pooling - Set DB_POOL environment variable based on your needs (typically 5-10 for web servers).

  3. Enable Rails caching in production - Use Redis or Memcached to cache expensive queries and computations.

  4. Monitor application performance - Use APM tools like New Relic, Scout, or Datadog to track performance metrics and errors.

  5. Set up structured logging - Use JSON logging to make logs searchable and easier to debug in production.

  6. Implement graceful shutdown handling - Configure Rails to handle SIGTERM signals properly for zero-downtime deployments.

  7. Use background jobs for long-running tasks - Offload heavy processing with Sidekiq, Resque, or delayed_job to prevent request timeouts.

  8. Regularly backup your database - Set up automated daily backups for your production database.

  9. Keep dependencies updated - Regularly update gems to patch security vulnerabilities and get performance improvements.

  10. Test deployment locally with Docker - Use Docker Compose locally to replicate your Klutch.sh environment before deploying.


External Resources