Skip to main content
The Ruby and Rails quickstart walks you through your first deployment to Laravel Cloud. Once your application is up and running, this guide will help you get it ready for production. We’ll cover preparing your repository, running Puma, connecting your databases, and handling assets, uploads, and background jobs. Most of the guide focuses on Ruby on Rails, but if you are deploying another Ruby or Rack application, be sure to read Other Ruby and Rack applications as well.

Prepare your repository

Cloud detects a Ruby application when it finds a Gemfile in your application directory. You should commit both your Gemfile and Gemfile.lock, and make sure they include your production server and database adapter. For Rails applications, keep the generated config/application.rb, config/environment.rb, and bin/rails files in your repository, since Cloud uses them to recognize the framework. Your application runs on Linux with ARM64 processors. If your lockfile only lists the platforms for your development machine, add Cloud’s platform locally and commit the updated lockfile:
Cloud accepts lockfiles that include the aarch64-linux, aarch64-linux-gnu, or generic ruby platform. If the application creation form reports a missing platform, push your updated lockfile and refresh the repository scan before continuing. If your application lives in a monorepo, select the directory that contains your Gemfile. Cloud keeps the surrounding repository available, so dependencies such as sibling path gems continue to work.

Choose a Ruby version

Cloud supports Ruby 3.2, 3.3, 3.4, and 4.0, with Ruby 3.4 as the default. The simplest way to choose your version is with a .ruby-version file:
Cloud reads your version declaration from each deployment’s commit. It checks .ruby-version first, then the RUBY VERSION section of Gemfile.lock, the ruby declaration in your Gemfile, .tool-versions, and finally mise.toml. A pinned version selects a major and minor release line, while a Gemfile version constraint selects the lowest supported release line that satisfies it. If your repository doesn’t declare a version, you may select one from your environment’s General Settings instead. If you declare a version Cloud doesn’t support, the deployment stops before building. You can find more details in the Ruby runtime documentation. It’s a good idea to keep your Ruby declarations consistent with one another. In particular, if your Gemfile requires an exact patch version, it must match the Ruby patch version Cloud installs, or Bundler will reject the build.

Install dependencies

Cloud installs your gems automatically before running your build commands. It uses the Bundler version recorded under BUNDLED WITH in your Gemfile.lock, or the Bundler version that ships with the runtime if that section is missing. Gems are installed using Bundler’s deployment and frozen modes, so you should update your dependencies locally and commit the lockfile rather than running bundle update during deployment. Be sure that every gem your application needs in production lives outside of the excluded groups, including puma, your database adapter, and any gems used for background jobs or building assets. By default, Cloud skips the development and test groups. You may change which groups are installed using your environment variables or linked secrets:
If you need every ordinary group installed, set BUNDLE_WITHOUT to an empty value. Cloud applies these settings while installing your gems and keeps the same group configuration at runtime. Remember to redeploy after changing them.

Run a production server

Rails

Cloud pre-fills your Rails start command using your application’s binstub, when it exists:
If your application doesn’t have a bin/rails binstub, Cloud uses bundle exec rails server with the same bind and port options. Either way, keep puma in your production dependencies so Rails can start its server. Your server must listen on the port set by the PORT environment variable, which is 3000 by default, and it must accept IPv6 connections. This matters because Cloud’s startup probes reach your application over IPv6. If you change the start command, be sure to keep the :: bind, since an IPv4-only or loopback-only listener will prevent it from reaching your application. If you would rather start Puma directly, replace the port or bind directive in your config/puma.rb file with the following:
Then, set your start command to:
Puma should have a single listener for your application’s port, so remove any other port or bind directives from your configuration. You should also keep Puma running in the foreground.

Procfile commands

If your repository includes a Procfile, Cloud uses its web command as your initial start command instead of the framework default. For example:
If you use this example, remember to configure the IPv6 listener in config/puma.rb as described in the previous section. For Rails applications, Cloud also offers the release command in your deployment settings, and your other processes as background process suggestions. It doesn’t run your entire Procfile automatically, so be sure to review these suggestions and configure the ones you need in your environment.

Rails environment variables

New Rails environments include a securely generated SECRET_KEY_BASE, which you are free to edit. You should keep this value the same across deployments so your users’ existing signed cookies and sessions stay valid. If you are migrating an existing application, use its current secret. Cloud also provides the following production defaults: Cloud sets your environment variables on your application’s process and makes them available to your build commands, so you can read any custom settings from ENV just like you do locally. If you define a variable yourself, your value takes precedence over both linked secrets and the injected defaults.

Encrypted credentials

If your application uses encrypted Rails credentials, add the matching RAILS_MASTER_KEY to your environment variables or Secrets Manager. Be sure to use the key for the credentials file your production configuration actually loads, such as config/credentials.yml.enc or config/credentials/production.yml.enc. Keep in mind that SECRET_KEY_BASE is only used to sign your application’s data. It can’t decrypt your credentials, so it doesn’t replace RAILS_MASTER_KEY. If your production configuration explicitly enables config.require_master_key, Cloud will check for a key before building your application.

Databases

When you connect a Postgres or MySQL database from your environment’s infrastructure canvas, Cloud provides a DATABASE_URL variable containing a postgresql:// or mysql2:// URL for Rails. Be sure to include the pg gem for Postgres or the mysql2 gem for MySQL in your production bundle. Rails reads DATABASE_URL automatically and merges it with your config/database.yml file. However, an explicit url: in that file takes precedence, so if your configuration still contains a URL from a previous host, remove it so Rails uses Cloud’s connection instead. You should always use a managed database in production. Since your environment’s filesystem is ephemeral, a SQLite file stored on an instance can’t be shared across replicas and won’t survive your next deployment.

Solid Cache, Solid Queue, and Solid Cable

Rails applications may use separate production connections named cache, queue, and cable. Cloud detects these connections in your config/database.yml file, and when you attach your primary database, it creates a separate database for each one it finds. These databases live in the same database cluster as your primary connection. For example, a Postgres application using all three Solid components might have the following configuration:
You only need to keep the connections your application actually uses. If you are using MySQL, use the mysql2 adapter and its gem instead. Cloud provides a connection URL for each connection, and Rails reads them automatically: To have Cloud provision these databases, keep the conventional connection names and leave out any url: entries. It skips connections that have an explicit URL or replica: true, and you will need to configure connections with any other name yourself. Since Cloud reads the structure of your YAML file without evaluating its Ruby expressions, make sure your production connection definitions are written out in the file. You may override the injected secondary URLs using your own environment variables or linked secrets, but those external connections are then yours to manage. When you replicate an environment, Cloud copies or creates the attached secondary databases based on the database replication option you choose. If you have set custom URLs, review them before deploying a replica or preview environment so it doesn’t accidentally use your production data.
Changing config/database.yml on its own doesn’t update an existing environment’s database attachments, so be sure to declare your connections before Cloud scans your repository and you attach the database. Detaching a database removes the environment’s secondary connections, but the databases themselves are kept.
Cloud waits for your attached databases to become available before deploying. Keep in mind that creating these connections doesn’t install the Solid gems or start a queue worker for you. You will still need to configure each Solid component to use its corresponding Rails connection and include its migration or schema files. The Rails multiple database guide walks through the application configuration.

Prepare the databases

Database preparation belongs in your environment’s deploy commands:
Cloud runs this command just before your new deployment starts receiving traffic, and it cancels the rollout if the command fails. Rails prepares each of your configured databases and applies any pending migrations. New Rails environments start with this command commented out. When you attach a database, Cloud enables it for you automatically, as long as you haven’t customized your deploy command. If you have, simply add the preparation step yourself. You should avoid running migrations from your start command. The start command runs every time an instance starts, including during deployments, autoscaling, and wake-ups after scaling to zero. Since your previous deployment keeps running while the new one rolls out, you should also write your migrations so they remain compatible with both.

Build and serve assets

When Cloud detects Propshaft or Sprockets in a Rails application that isn’t configured as API-only, it pre-fills the following build command:
If Cloud also detects a JavaScript package manager, it adds a step to install your JavaScript dependencies. Ruby runtimes include Node.js, npm, and Yarn. Be sure to keep the dependencies your asset build needs, including any development dependencies used by your JavaScript build tools. If you use a custom asset pipeline, review the pre-filled command and adjust it as needed. API-only Rails applications don’t receive this default asset build. Cloud’s Nginx server serves files from your application’s public directory, including the compiled assets in public/assets. The SECRET_KEY_BASE_DUMMY=1 setting only applies to asset compilation. Your real SECRET_KEY_BASE is still used at runtime, so don’t add the dummy setting as an environment variable. Also, keep in mind that initializers that access your credentials or database may still require those resources while your assets compile.

Caches and background jobs

When you attach a Laravel Valkey or Redis cache, Cloud provides a REDIS_URL variable. You should configure your cache library or Rails cache store to use this URL, and make sure it supports TLS when the URL uses the rediss:// scheme. To use Rails’ Redis cache store, include the redis gem and add the following to your config/environments/production.rb file:
If your application uses Solid Cache, you may keep your existing database cache configuration instead. Either way, choose the backing store your application is configured to use. You may run your background jobs as custom background processes on your App cluster or a worker cluster. Set the command for the job backend you are using: Be sure to keep the required gems and binstubs in your production bundle. Cloud suggests Sidekiq and GoodJob commands based on your lockfile, along with any processes from your Procfile. If you are using Solid Queue with bin/jobs, add the process yourself. You should run your workers as separate background processes rather than enabling SOLID_QUEUE_IN_PUMA on your web server. Each background process runs on every replica of its cluster, so account for your total worker concurrency and database connections when choosing process counts and replica limits. If your workers need to process jobs or recurring tasks while your App cluster scales to zero, keep their worker cluster awake. Solid Queue can run recurring tasks using its own scheduler and config/recurring.yml file. To use it, configure your recurring tasks in your application and run the jobs process. Cloud’s scheduler controls for Laravel and Symfony don’t configure Rails recurring tasks.

Store files in object storage

For user uploads, you should use object storage. To get started, attach a private bucket as your environment’s default disk. Cloud provides the bucket’s AWS connection variables, but you will need to select and configure an Active Storage service in Rails yourself. First, add the aws-sdk-s3 gem to your production Gemfile, run bundle install locally, and commit the updated lockfile. Then, add the following service to your config/storage.yml file:
Finally, select the service in your config/environments/production.rb file:
With this configuration, your uploads live outside of your instance’s filesystem, and Rails uses signed URLs to provide access to them. Be sure to keep the service private and leave out any per-object ACL options. Cloud’s object storage manages visibility at the bucket level, so it rejects the ACL requests Active Storage sends when a service uses public: true. The Ruby runtime includes libvips for image variants. If your application transforms images, include the image_processing gem as well. The Active Storage guide covers direct uploads and variant configuration in more detail.

Configure Puma concurrency

You should configure Puma’s process and thread counts based on your instance’s CPU and memory. Unlike Python applications, Cloud doesn’t calculate WEB_CONCURRENCY for Ruby applications. You may set it yourself, or start with Puma’s single-process mode and increase concurrency once you have a feel for your application’s memory usage. For example, you might add the following settings to your existing config/puma.rb file:
Setting WEB_CONCURRENCY=0 runs a single server process without clustered workers. Keep in mind that each clustered worker has its own thread pool and its own database connection pools, so set the pool for each database large enough to accommodate every thread that uses it, including any background or asynchronous work.

Shut down gracefully

When an instance stops, whether during a deployment or while scaling down, Cloud sends a SIGTERM signal to your application’s process. Puma handles this signal gracefully by letting active requests finish before exiting. You should keep Puma’s shutdown limits, such as the force_shutdown_after and worker_shutdown_timeout settings in the previous example, shorter than your environment’s graceful shutdown timeout. This leaves time for the proxy and the rest of the instance to stop cleanly. If your requests or jobs need more time to finish, increase the environment’s timeout as well. Puma’s configuration documentation describes the available shutdown and concurrency options.

Running behind the proxy

Cloud handles TLS for you before forwarding requests to your application, so Puma only needs to speak plain HTTP. Along the way, its proxy passes along the original request scheme in the X-Forwarded-Proto header, which allows Rails to recognize HTTPS requests. You should keep your production HTTPS configuration, such as config.force_ssl = true, in place. If you restrict hosts using config.hosts, be sure to allow your Cloud domain and any custom domains. You should also configure the hostnames used in URLs generated outside of a request, such as links in your mailers. Cloud provides an APP_URL variable, but Rails doesn’t apply it to these settings automatically.

Logging

Cloud captures everything your application writes to stdout and stderr and displays it in the Logs dashboard. Recent Rails applications already log to stdout. If you are deploying an older application that only writes to a log file, configure its logger in your config/environments/production.rb file:
Keep in mind that the injected RAILS_LOG_TO_STDOUT variable only has an effect when your application’s configuration reads it. The Rails configuration guide is a helpful reference when adapting an older application.

Other Ruby and Rack applications

For Ruby applications that don’t use Rails, Cloud uses the web command from your Procfile if you have one. Otherwise, it uses the Ruby or Rack entrypoint you selected. A Ruby file such as app.rb runs with bundle exec ruby app.rb, so your program needs to start its own server, read PORT, and accept IPv6 connections. For a Rack entrypoint such as config.ru, Cloud pre-fills the following start command:
Cloud’s launcher uses Puma when it is available in your production bundle, or a compatible rackup executable otherwise, and it binds the server to :: on PORT for you. Be sure to add Puma, or Rackup with a compatible server, to your production dependencies. Generic Ruby applications receive RACK_ENV=production, BUNDLE_WITHOUT=development:test, and the connection variables for any attached resources. Their DATABASE_URL uses the mysql:// or postgresql:// scheme, so you may need to adapt it for the database library you use. The Rails-specific asset builds, secrets, and database preparation described in this guide don’t apply to generic Ruby applications.