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
-
Install Ruby (if not already installed):
Use rbenv or RVM for version management:
Terminal window rbenv install 3.2.0rbenv local 3.2.0 -
Install Rails:
Terminal window gem install rails -
Create a new Rails app with PostgreSQL:
Terminal window rails new my-rails-app --database=postgresqlcd my-rails-app -
Create the database locally:
Terminal window rails db:create -
Start the development server:
Terminal window rails serverYour app should be running at http://localhost:3000.
Sample Code for Getting Started
Create a scaffold for a basic Article resource:
rails generate scaffold Article title:string content:text published:booleanrails db:migrateUpdate 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']] }endCreate a basic controller to handle requests:
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) endendDeploying 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
-
Prepare your Rails app for production:
Update
Gemfileto ensure production gems are included:# Gemfilesource "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: falsegem "pundit"gem "kaminari"group :production dogem "rack-cors"endgroup :development, :test dogem "byebug", platforms: [:mri, :mingw, :x64_mingw]gem "rspec-rails"endgroup :development dogem "web-console", ">= 4.1.0"gem "listen", "~> 3.3"end -
Commit your code to GitHub:
Terminal window git add .git commit -m "Initial Rails app setup"git push origin main -
Log in to Klutch.sh dashboard:
Visit https://klutch.sh/app
-
Create a new project:
- Click “Create Project”
- Enter your project name (e.g., “My Rails App”)
- Select your organization or personal account
-
Create a new app:
- Click “Create App”
- Select your Rails GitHub repository
- Select the branch (typically
mainormaster) - 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
-
Add environment variables:
In the app creation form, add these essential environment variables:
RAILS_ENV=productionRAILS_MASTER_KEY=<your-production-master-key>DATABASE_URL=postgresql://user:password@host:5432/dbnameREDIS_URL=redis://user:password@host:6379/0SECRET_KEY_BASE=<your-secret-key> -
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:precompileNIXPACKS_START_CMD=rails server -b 0.0.0.0 -p 3000Deploying 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
-
Create a
Dockerfilein your project root:# Build stageFROM ruby:3.2-slim as builder# Install build dependenciesRUN apt-get update && apt-get install -y build-essential libpq-dev && rm -rf /var/lib/apt/lists/*WORKDIR /app# Copy Gemfile and lock fileCOPY Gemfile Gemfile.lock ./# Install gemsRUN bundle install --without development test# Copy application codeCOPY . .# Precompile assetsRUN bundle exec rake assets:precompile# Production stageFROM ruby:3.2-slim# Install runtime dependenciesRUN apt-get update && apt-get install -y libpq5 && rm -rf /var/lib/apt/lists/*WORKDIR /app# Copy gems from builderCOPY --from=builder /usr/local/bundle /usr/local/bundle# Copy application from builderCOPY --from=builder /app /app# Expose portEXPOSE 3000# Health checkHEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \CMD curl -f http://localhost:3000/health || exit 1# Start Rails serverCMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0", "-p", "3000"] -
Commit the Dockerfile to GitHub:
Terminal window git add Dockerfilegit commit -m "Add production Dockerfile"git push origin main -
Log in to Klutch.sh dashboard:
Visit https://klutch.sh/app
-
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
-
Add environment variables:
RAILS_ENV=productionRAILS_MASTER_KEY=<your-production-master-key>DATABASE_URL=postgresql://user:password@host:5432/dbnameREDIS_URL=redis://user:password@host:6379/0SECRET_KEY_BASE=<your-secret-key> -
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
-
Update
config/database.ymlto use theDATABASE_URLenvironment variable:default: &defaultadapter: postgresqlencoding: unicodepool: <%= ENV.fetch("DB_POOL") { 5 } %>url: <%= ENV['DATABASE_URL'] %>development:<<: *defaultdatabase: my_rails_app_developmenttest:<<: *defaultdatabase: my_rails_app_testproduction:<<: *defaultdatabase: my_rails_app_production -
During app creation on Klutch.sh, set:
DATABASE_URL=postgresql://username:password@postgres-host:5432/my_rails_app_production -
Run migrations:
After deployment, execute migrations:
Terminal window rails db:migrate RAILS_ENV=production
MySQL Configuration
-
Create a Rails app with MySQL:
Terminal window rails new my-rails-app --database=mysql -
Update
config/database.yml:default: &defaultadapter: mysql2encoding: utf8mb4pool: <%= ENV.fetch("DB_POOL") { 5 } %>url: <%= ENV['DATABASE_URL'] %>development:<<: *defaultdatabase: my_rails_app_developmenttest:<<: *defaultdatabase: my_rails_app_testproduction:<<: *defaultdatabase: my_rails_app_production -
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=productionRAILS_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/0LOG_LEVEL=infoRACK_ENV=productionGenerating Rails Master Key
If deploying a new Rails app:
rails credentials:editThis 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
-
Update
config/environments/production.rb:Rails.application.configure doconfig.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 -
Set the REDIS_URL environment variable on Klutch.sh:
REDIS_URL=redis://:password@redis-host:6379/0 -
Use caching in your models and controllers:
# In controllerdef index@articles = Rails.cache.fetch('articles', expires_in: 1.hour) doArticle.all.to_aendend
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
-
Install Active Storage (if not already installed):
Terminal window rails active_storage:installrails db:migrate -
Configure
config/environments/production.rb:Rails.application.configure doconfig.active_storage.service = :local_diskend -
Update
config/storage.yml:local_disk:service: Diskroot_path: <%= ENV['STORAGE_PATH'] || 'storage' %>aws_s3:service: S3access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %>secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %>region: <%= ENV['AWS_REGION'] %>bucket: <%= ENV['AWS_S3_BUCKET'] %> -
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
-
Set the STORAGE_PATH environment variable:
STORAGE_PATH=/app/storage
Using AWS S3 for File Storage
For production applications, AWS S3 is recommended:
-
Add the AWS SDK gem to your Gemfile:
gem "aws-sdk-s3", require: false -
Update
config/environments/production.rb:Rails.application.configure doconfig.active_storage.service = :aws_s3end -
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-1AWS_S3_BUCKET=my-rails-bucket -
Use file uploads in your models:
class Article < ApplicationRecordhas_one_attached :featured_imagevalidates :featured_image, presence: trueend
Security Best Practices
1. Use HTTPS
Ensure your Rails app forces HTTPS in production:
Rails.application.configure do config.force_ssl = true config.ssl_options = { hsts: { subdomains: true } }end2. Configure CORS for APIs
If your Rails app serves APIs to frontend applications:
# Gemfilegem "rack-cors"
class Rack::Cors allow do origins 'example-app.klutch.sh', 'yourdomain.com' resource '*', headers: :any, methods: [:get, :post, :put, :delete] endend3. Set Strong Session Configuration
Rails.application.config.session_store :cookie_store, key: '_my_app_session', secure: true, httponly: true, same_site: :strict4. Use Devise for Authentication
Add to Gemfile:
gem "devise"Install and configure:
rails generate devise:installrails generate devise Userrails db:migrate5. Implement Authorization with Pundit
Add to Gemfile:
gem "pundit"Create policies:
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? endend6. Secure Credentials Management
Use Rails credentials for sensitive data:
rails credentials:editAccess in code:
api_key = Rails.application.credentials.stripe_api_key7. Enable Content Security Policy
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, :httpsendMonitoring and Logging
Health Check Endpoint
Add a health check route for monitoring:
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)endApplication Performance Monitoring
Add a monitoring gem like newrelic_rpm:
# Gemfilegem "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:
-
Access the app settings in your Klutch.sh dashboard
-
Navigate to the “Domains” section
-
Add your custom domain (e.g.,
example.com) -
Update your DNS records at your domain registrar:
Create a CNAME record pointing to
example-app.klutch.shName: wwwType: CNAMEValue: example-app.klutch.sh -
Wait for DNS propagation (usually 5-30 minutes)
-
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
GemfileandGemfile.lockare committed to Git - Run
bundle lock --add-platform x86_64-linuxlocally and push - Verify no local-only gems are in your Gemfile
2. Database Migration Failures
Problem: App crashes with “PendingMigrationError”
Solution:
-
Set
RAILS_ENV=productionenvironment variable -
Run migrations manually in your app container:
Terminal window rails db:migrate RAILS_ENV=production -
Check your
DATABASE_URLis 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
webpackergem is in your Gemfile - Verify the
RAILS_MASTER_KEYenvironment 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_URLis 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/storageis 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
-
Use environment variables for all configuration - Never hardcode sensitive values or environment-specific settings in your code.
-
Implement database connection pooling - Set
DB_POOLenvironment variable based on your needs (typically 5-10 for web servers). -
Enable Rails caching in production - Use Redis or Memcached to cache expensive queries and computations.
-
Monitor application performance - Use APM tools like New Relic, Scout, or Datadog to track performance metrics and errors.
-
Set up structured logging - Use JSON logging to make logs searchable and easier to debug in production.
-
Implement graceful shutdown handling - Configure Rails to handle SIGTERM signals properly for zero-downtime deployments.
-
Use background jobs for long-running tasks - Offload heavy processing with Sidekiq, Resque, or delayed_job to prevent request timeouts.
-
Regularly backup your database - Set up automated daily backups for your production database.
-
Keep dependencies updated - Regularly update gems to patch security vulnerabilities and get performance improvements.
-
Test deployment locally with Docker - Use Docker Compose locally to replicate your Klutch.sh environment before deploying.