Skip Navigation
Show nav
Dev Center
  • Get Started
  • Documentation
  • Changelog
  • Search
  • Get Started
    • Node.js
    • Ruby on Rails
    • Ruby
    • Python
    • Java
    • PHP
    • Go
    • Scala
    • Clojure
    • .NET
  • Documentation
  • Changelog
  • More
    Additional Resources
    • Home
    • Elements
    • Products
    • Pricing
    • Careers
    • Help
    • Status
    • Events
    • Podcasts
    • Compliance Center
    Heroku Blog

    Heroku Blog

    Find out what's new with Heroku on our blog.

    Visit Blog
  • Log inorSign up

Getting Started on Heroku Fir with Ruby

Introduction

Complete this tutorial to deploy a sample Ruby app to Heroku Private Spaces on the Fir generation of the platform. To deploy the app to the Common Runtime or Cedar Private Spaces, follow this guide instead.

The tutorial assumes that you have:

  • A verified Heroku Account
  • An existing Fir Private Space
  • A team admin or member role that has the app creation permission on the space.
  • An SSH key added to your Heroku account
  • Ruby 3.2.4 installed locally - see the installation guides for Ruby and Rails on macOS, Windows, and Linux
  • Bundler installed locally - run gem install bundler
  • Postgres installed locally

Using dynos and databases to complete this tutorial counts towards your usage. We recommend using dyno-1c-0.5gb dynos and an Essential-0 Postgres database to complete this tutorial. Delete all resources after completing the tutorial.

Set Up

Install the Heroku Command Line Interface (CLI). Use the CLI to manage and scale your app, provision add-ons, view your logs, and run your app locally.

The Heroku CLI requires Git, the popular version control system. If you don’t already have Git installed, complete the following before proceeding:

  • Git installation
  • First-time Git setup

Download and run the installer for your platform:

apple logomacOS

Install Homebrew and run:

$ brew install heroku/brew/heroku

windows logoWindows

Download the appropriate installer for your Windows installation:

64-bit installer

32-bit installer

You can find more installation options for the Heroku CLI here.

After installation, you can use the heroku command from your command shell.

On Windows, start the Command Prompt (cmd.exe) or Powershell to access the command shell.

To log in to the Heroku CLI, use the heroku login command:

$ heroku login
heroku: Press any key to open up the browser to login or q to exit:
Opening browser to https://cli-auth.heroku.com/auth/cli/browser/***
heroku: Waiting for login...
Logging in... done
Logged in as me@example.com

This command opens your web browser to the Heroku login page. If your browser is already logged in to Heroku, click the Log In button on the page.

This authentication is required for the heroku and git commands to work correctly.

If you have any problems installing or using the Heroku CLI, see the main Heroku CLI article for advice and troubleshooting steps.

If you’re behind a firewall that uses a proxy to connect with external HTTP/HTTPS services, set the HTTP_PROXY or HTTPS_PROXY environment variables in your local development environment before running the heroku command.

Clone the Sample App

If you’re new to Heroku, it’s recommended that you complete this tutorial using the Heroku-provided sample application.

To deploy an existing application, follow this article instead.

Clone the sample application to get a local version of the code. Execute these commands in your local command shell or terminal:

$ git clone https://github.com/heroku/ruby-getting-started.git
$ cd ruby-getting-started

You now have a functioning Git repository that contains a simple application. It includes a Gemfile file, which Ruby’s dependency manager, bundler, uses to install dependencies.

Define a Procfile

Use a Procfile, a text file in the root directory of your application, to explicitly declare what command to execute to start your app.

The Procfile in the example app looks like this:

web: bundle exec puma -C config/puma.rb

This Procfile declares a single process type, web, and the command needed to run it. The name web is important here. It declares that this process type is attached to Heroku’s HTTP routing stack and receives web traffic when deployed. The command used here runs Puma, the web server, and passes in a configuration file.

A Procfile can contain additional process types. For example, you can declare a background worker process that processes items off a queue.

Configure an IPv6 Host

The code we’re deploying is already ready for IPv6, but when you’re deploying your own app you must make sure it binds to the IPv6 interface.

Fir uses IPv6 to route web requests. By default, Puma will try to bind to the IPv4 host 0.0.0.0. Your app must instead bind to the IPv6 host ::. To accomplish this, use Puma’s port DSL in config/puma.rb:


# Support IPv6 by binding to host `::` instead of `0.0.0.0`
port(ENV.fetch("PORT") { 3000 }, "::")

If you’re using rails server or bin/rails server in your Procfile, you must set the host with the --binding "[::]" flag. For example:

web: bin/rails server --binding "[::]" --port "${PORT:?Error: PORT env var is not set!}" --environment "$RAILS_ENV"

Create Your App in a Fir Space

Delete your app and database as soon as you’re done to control costs.

 

You can get a list of all Heroku spaces by running $ heroku spaces

Create an app on Heroku to prepare the platform to receive your source code by replacing <space-name> with the name of your Fir space in the command below:

$ heroku create --space <space-name>
Creating app in space <space name>...
Creating app in space <space name>... done, powerful-fortress-82261
http://powerful-fortress-82261-3670d9de0fe8.plum-virginia.herokuapp.com/ | https://git.heroku.com/powerful-fortress-82261.git

When you create an app, a Git remote called heroku also gets created and associated with your local Git repository. Git remotes are versions of your repository that live on other servers. You deploy your app by pushing its code to that special Heroku-hosted remote associated with your app.

Heroku generates a random name for your app, in this case, powerful-fortress-82261. You can specify your own app name.

Provision a Database

The sample app requires a database. Provision a Heroku Postgres database, an add-on available through the Elements Marketplace. Add-ons are cloud services that provide out-of-the-box additional services for your application, such as logging, monitoring, databases, and more.

An essential-0 Postgres size costs $5 a month, prorated to the minute. At the end of this tutorial, we prompt you to delete your database to minimize costs. For production apps in Private Spaces, you might want to use a private database plan such as private-0 or higher.

$ heroku addons:create heroku-postgresql:essential-0 --confirm powerful-fortress-82261 -- --region us
Creating heroku-postgresql:essential-0 on powerful-fortress-82261...
Creating heroku-postgresql:essential-0 on powerful-fortress-82261... ~$0.007/hour (max $5/month)
Database should be available soon
postgresql-silhouetted-53983 is being created in the background. The app will restart when complete...
Use heroku addons:info postgresql-silhouetted-53983 to check creation progress
Use heroku addons:docs heroku-postgresql to view documentation

You can wait for the database to provision by running this command:

$ heroku pg:wait

After that command exits, your Heroku app can access the Postgres database. The DATABASE_URL environment variable stores your credentials, which your app is configured to connect to. You can see all the add-ons provisioned with the addons command:

$ heroku addons

 Add-on                                           Plan        Price        Max price State
 ──────────────────────────────────────────────── ─────────── ──────────── ───────── ───────
 heroku-postgresql (postgresql-silhouetted-53983) essential-0 ~$0.007/hour $5/month  created
  └─ as DATABASE

The table above shows add-ons and the attachments to the current app (powerful-fortress-82261) or other apps.

Deploy the App

Using a dyno to complete this tutorial counts towards your usage. Delete your app and database as soon as you’re done to control costs.

Deploy your code. This command pushes the main branch of the sample repo to your heroku remote, which then deploys to Heroku:

$ git push heroku main
remote: Updated 105 paths from 16f42dd
remote: Compressing source files... done.
remote: Building source:
remote: Heroku Labs: Build Time Config Vars are *not enabled*.
remote: By default, your config vars are only available at runtime. See https://devcenter.heroku.com/build-time-config-vars for details on Build Time Config Vars.
remote: Extracting source
remote: Image with name "powerful-fortress-82261/builds" not found
remote: 3 of 6 buildpacks participating
remote: heroku/nodejs-engine 3.6.1
remote: heroku/ruby          7.0.1
remote: heroku/procfile      4.2.1
remote:
remote: # Heroku Node.js Engine
remote:
remote: - Checking Node.js version
remote:   - Node.js version not specified, using `22.x`
remote:   - Resolved Node.js version: `22.15.0`
remote: - Installing Node.js distribution
remote:   - Downloading Node.js `22.15.0 (linux-arm64)` from https://nodejs.org/download/release/v22.15.0/node-v22.15.0-linux-arm64.tar.gz ... (0.4s)
remote:   - Verifying checksum
remote:   - Extracting Node.js `22.15.0 (linux-arm64)`
remote:   - Installing Node.js `22.15.0 (linux-arm64)` ... (< 0.1s)
remote: - Done (finished in < 0.1s)
remote:
remote: ## Heroku Ruby Buildpack
remote:
remote: - Metrics agent
remote:   - Skipping install (`barnes` gem not found)
remote: - Ruby version `3.2.4` from `Gemfile.lock`
remote:   - Installing .... (1.0s)
remote: - Bundler version `2.5.9` from `Gemfile.lock`
remote:   - Running `gem install bundler --version 2.5.9` ... (0.8s)
remote: - Bundle install gems
remote:   - Running `BUNDLE_FROZEN="1" BUNDLE_GEMFILE="/workspace/Gemfile" BUNDLE_WITHOUT="development:test" bundle install`
remote:
remote:       Fetching gem metadata from https://rubygems.org/.........
remote:       Fetching rake 13.2.1
remote:       Installing rake 13.2.1
remote:       Fetching bigdecimal 3.1.9
remote:       Fetching concurrent-ruby 1.3.5
remote:       Fetching base64 0.2.0
remote:       Fetching connection_pool 2.5.0
remote:       Installing bigdecimal 3.1.9 with native extensions
remote:       Installing base64 0.2.0
remote:       Fetching drb 2.2.1
remote:       Installing concurrent-ruby 1.3.5
remote:       Installing connection_pool 2.5.0
remote:       Fetching minitest 5.25.4
remote:       Installing drb 2.2.1
remote:       Installing minitest 5.25.4
remote:       Fetching mutex_m 0.3.0
remote:       Installing mutex_m 0.3.0
remote:       Fetching builder 3.3.0
remote:       Fetching erubi 1.13.1
remote:       Installing builder 3.3.0
remote:       Fetching mini_portile2 2.8.8
remote:       Installing erubi 1.13.1
remote:       Installing mini_portile2 2.8.8
remote:       Fetching racc 1.8.1
remote:       Fetching crass 1.0.6
remote:       Installing crass 1.0.6
remote:       Installing racc 1.8.1 with native extensions
remote:       Fetching rack 3.0.12
remote:       Fetching nio4r 2.7.4
remote:       Installing nio4r 2.7.4 with native extensions
remote:       Installing rack 3.0.12
remote:       Fetching websocket-extensions 0.1.5
remote:       Installing websocket-extensions 0.1.5
remote:       Fetching zeitwerk 2.6.14
remote:       Installing zeitwerk 2.6.14
remote:       Fetching timeout 0.4.3
remote:       Installing timeout 0.4.3
remote:       Fetching marcel 1.0.4
remote:       Installing marcel 1.0.4
remote:       Fetching mini_mime 1.1.5
remote:       Installing mini_mime 1.1.5
remote:       Fetching date 3.4.1
remote:       Installing date 3.4.1 with native extensions
remote:       Fetching msgpack 1.8.0
remote:       Installing msgpack 1.8.0 with native extensions
remote:       Fetching coffee-script-source 1.12.2
remote:       Installing coffee-script-source 1.12.2
remote:       Fetching execjs 2.10.0
remote:       Installing execjs 2.10.0
remote:       Fetching stringio 3.1.0
remote:       Installing stringio 3.1.0 with native extensions
remote:       Fetching io-console 0.7.2
remote:       Installing io-console 0.7.2 with native extensions
remote:       Fetching webrick 1.8.2
remote:       Installing webrick 1.8.2
remote:       Fetching thor 1.3.1
remote:       Installing thor 1.3.1
remote:       Fetching ffi 1.16.3
remote:       Installing ffi 1.16.3 with native extensions
remote:       Fetching rb-fsevent 0.11.2
remote:       Installing rb-fsevent 0.11.2
remote:       Fetching pg 1.5.9
remote:       Installing pg 1.5.9 with native extensions
remote:       Fetching tilt 2.1.0
remote:       Installing tilt 2.1.0
remote:       Fetching turbolinks-source 5.2.0
remote:       Installing turbolinks-source 5.2.0
remote:       Fetching i18n 1.14.7
remote:       Installing i18n 1.14.7
remote:       Fetching tzinfo 2.0.6
remote:       Installing tzinfo 2.0.6
remote:       Fetching rack-session 2.0.0
remote:       Installing rack-session 2.0.0
remote:       Fetching rack-test 2.1.0
remote:       Installing rack-test 2.1.0
remote:       Fetching sprockets 4.2.0
remote:       Installing sprockets 4.2.0
remote:       Fetching websocket-driver 0.7.6
remote:       Installing websocket-driver 0.7.6 with native extensions
remote:       Fetching net-protocol 0.2.2
remote:       Installing net-protocol 0.2.2
remote:       Fetching puma 6.6.0
remote:       Fetching nokogiri 1.18.3
remote:       Installing puma 6.6.0 with native extensions
remote:       Installing nokogiri 1.18.3 with native extensions
remote:       Fetching coffee-script 2.4.1
remote:       Installing coffee-script 2.4.1
remote:       Fetching uglifier 4.2.1
remote:       Installing uglifier 4.2.1
remote:       Fetching psych 5.1.2
remote:       Installing psych 5.1.2 with native extensions
remote:       Fetching bootsnap 1.18.4
remote:       Installing bootsnap 1.18.4 with native extensions
remote:       Fetching rackup 2.1.0
remote:       Installing rackup 2.1.0
remote:       Fetching reline 0.5.7
remote:       Installing reline 0.5.7
remote:       Fetching turbolinks 5.2.1
remote:       Installing turbolinks 5.2.1
remote:       Fetching activesupport 7.1.3.2
remote:       Installing activesupport 7.1.3.2
remote:       Fetching net-imap 0.4.19
remote:       Installing net-imap 0.4.19
remote:       Fetching net-pop 0.1.2
remote:       Installing net-pop 0.1.2
remote:       Fetching net-smtp 0.5.0
remote:       Installing net-smtp 0.5.0
remote:       Fetching rdoc 6.6.3.1
remote:       Installing rdoc 6.6.3.1
remote:       Fetching rb-inotify 0.10.1
remote:       Installing rb-inotify 0.10.1
remote:       Fetching sassc 2.4.0
remote:       Installing sassc 2.4.0 with native extensions
remote:       Fetching globalid 1.2.1
remote:       Installing globalid 1.2.1
remote:       Fetching activemodel 7.1.3.2
remote:       Installing activemodel 7.1.3.2
remote:       Fetching mail 2.8.1
remote:       Installing mail 2.8.1
remote:       Fetching irb 1.13.1
remote:       Installing irb 1.13.1
remote:       Fetching sdoc 2.6.1
remote:       Installing sdoc 2.6.1
remote:       Fetching listen 3.9.0
remote:       Installing listen 3.9.0
remote:       Fetching activejob 7.1.3.2
remote:       Installing activejob 7.1.3.2
remote:       Fetching activerecord 7.1.3.2
remote:       Installing activerecord 7.1.3.2
remote:       Fetching rails-dom-testing 2.2.0
remote:       Fetching loofah 2.24.0
remote:       Installing rails-dom-testing 2.2.0
remote:       Installing loofah 2.24.0
remote:       Fetching rails-html-sanitizer 1.6.2
remote:       Installing rails-html-sanitizer 1.6.2
remote:       Fetching actionview 7.1.3.2
remote:       Installing actionview 7.1.3.2
remote:       Fetching actionpack 7.1.3.2
remote:       Fetching jbuilder 2.13.0
remote:       Installing jbuilder 2.13.0
remote:       Installing actionpack 7.1.3.2
remote:       Fetching actioncable 7.1.3.2
remote:       Fetching activestorage 7.1.3.2
remote:       Fetching actionmailer 7.1.3.2
remote:       Fetching railties 7.1.3.2
remote:       Installing activestorage 7.1.3.2
remote:       Installing actioncable 7.1.3.2
remote:       Installing actionmailer 7.1.3.2
remote:       Fetching sprockets-rails 3.4.2
remote:       Installing sprockets-rails 3.4.2
remote:       Installing railties 7.1.3.2
remote:       Fetching actionmailbox 7.1.3.2
remote:       Fetching actiontext 7.1.3.2
remote:       Installing actionmailbox 7.1.3.2
remote:       Installing actiontext 7.1.3.2
remote:       Fetching rails 7.1.3.2
remote:       Fetching sassc-rails 2.1.2
remote:       Fetching jquery-rails 4.6.0
remote:       Fetching coffee-rails 5.0.0
remote:       Installing rails 7.1.3.2
remote:       Installing sassc-rails 2.1.2
remote:       Installing coffee-rails 5.0.0
remote:       Fetching sass-rails 6.0.0
remote:       Installing sass-rails 6.0.0
remote:       Installing jquery-rails 4.6.0
remote:       Bundle complete! 13 Gemfile dependencies, 83 gems now installed.
remote:       Gems in the groups 'development' and 'test' were not installed.
remote:       Use `bundle info [gemname]` to see where a bundled gem is installed.
remote:
remote:   - Done (1m 45s)
remote:   - Running `bundle clean --force` ... (0.2s)
remote: - Default process detection
remote:   - Running `bundle list` ... (0.1s)
remote:   - Detected rails app (`rails` gem found)
remote: - Rake assets install
remote:   - Detected rake (`rake` gem found, `Rakefile` found at `/workspace/Rakefile`)
remote:   - Running `rake -P --trace` .... (1.8s)
remote:   - Compiling assets with cache (detected `rake assets:precompile` and `rake assets:clean` via `rake -P`)
remote:   - Creating cache for /workspace/public/assets
remote:   - Creating cache for /workspace/tmp/cache/assets
remote:   - Running `rake assets:precompile assets:clean --trace`
remote:
remote:       ** Invoke assets:precompile (first_time)
remote:       ** Invoke assets:environment (first_time)
remote:       ** Execute assets:environment
remote:       ** Invoke environment (first_time)
remote:       ** Execute environment
remote:       ** Execute assets:precompile
remote:       I, [2025-04-28T23:13:52.746206 #9795]  INFO -- : Writing /workspace/public/assets/manifest-dad05bf766af0fe3d79dd746db3c1361c0583026cdf35d6a2921bccaea835331.js
remote:       I, [2025-04-28T23:13:52.746466 #9795]  INFO -- : Writing /workspace/public/assets/manifest-dad05bf766af0fe3d79dd746db3c1361c0583026cdf35d6a2921bccaea835331.js.gz
remote:       I, [2025-04-28T23:13:52.746750 #9795]  INFO -- : Writing /workspace/public/assets/lang-logo-b6c7c4b6a37e9c2425ca4d54561010c0719870ae325c849de398499f1ab098a9.png
remote:       I, [2025-04-28T23:13:52.747354 #9795]  INFO -- : Writing /workspace/public/assets/application-9ced36c9568ebfd1053e04ba411af767274dfcccd9807c0989f8bd17ca5e8f5b.js
remote:       I, [2025-04-28T23:13:52.747463 #9795]  INFO -- : Writing /workspace/public/assets/application-9ced36c9568ebfd1053e04ba411af767274dfcccd9807c0989f8bd17ca5e8f5b.js.gz
remote:       I, [2025-04-28T23:13:52.747579 #9795]  INFO -- : Writing /workspace/public/assets/welcome-27cfb9694c5e92d25d972c2b4a2d2e222ad088aef866823f772241c1db423402.js
remote:       I, [2025-04-28T23:13:52.747669 #9795]  INFO -- : Writing /workspace/public/assets/welcome-27cfb9694c5e92d25d972c2b4a2d2e222ad088aef866823f772241c1db423402.js.gz
remote:       I, [2025-04-28T23:13:52.747774 #9795]  INFO -- : Writing /workspace/public/assets/widgets-27cfb9694c5e92d25d972c2b4a2d2e222ad088aef866823f772241c1db423402.js
remote:       I, [2025-04-28T23:13:52.747861 #9795]  INFO -- : Writing /workspace/public/assets/widgets-27cfb9694c5e92d25d972c2b4a2d2e222ad088aef866823f772241c1db423402.js.gz
remote:       I, [2025-04-28T23:13:52.747969 #9795]  INFO -- : Writing /workspace/public/assets/application-776d900b9840362472b5b6b4afb9b798c78d53098a77b289b8bfc22c6d241913.css
remote:       I, [2025-04-28T23:13:52.748051 #9795]  INFO -- : Writing /workspace/public/assets/application-776d900b9840362472b5b6b4afb9b798c78d53098a77b289b8bfc22c6d241913.css.gz
remote:       I, [2025-04-28T23:13:52.748718 #9795]  INFO -- : Writing /workspace/public/assets/scaffolds-04024382391bb910584145d8113cf35ef376b55d125bb4516cebeb14ce788597.css
remote:       I, [2025-04-28T23:13:52.748828 #9795]  INFO -- : Writing /workspace/public/assets/scaffolds-04024382391bb910584145d8113cf35ef376b55d125bb4516cebeb14ce788597.css.gz
remote:       I, [2025-04-28T23:13:52.748940 #9795]  INFO -- : Writing /workspace/public/assets/theme-776d900b9840362472b5b6b4afb9b798c78d53098a77b289b8bfc22c6d241913.css
remote:       I, [2025-04-28T23:13:52.749028 #9795]  INFO -- : Writing /workspace/public/assets/theme-776d900b9840362472b5b6b4afb9b798c78d53098a77b289b8bfc22c6d241913.css.gz
remote:       I, [2025-04-28T23:13:52.749121 #9795]  INFO -- : Writing /workspace/public/assets/welcome-04024382391bb910584145d8113cf35ef376b55d125bb4516cebeb14ce788597.css
remote:       I, [2025-04-28T23:13:52.749186 #9795]  INFO -- : Writing /workspace/public/assets/welcome-04024382391bb910584145d8113cf35ef376b55d125bb4516cebeb14ce788597.css.gz
remote:       I, [2025-04-28T23:13:52.749278 #9795]  INFO -- : Writing /workspace/public/assets/widgets-04024382391bb910584145d8113cf35ef376b55d125bb4516cebeb14ce788597.css
remote:       I, [2025-04-28T23:13:52.749376 #9795]  INFO -- : Writing /workspace/public/assets/widgets-04024382391bb910584145d8113cf35ef376b55d125bb4516cebeb14ce788597.css.gz
remote:       I, [2025-04-28T23:13:52.749473 #9795]  INFO -- : Writing /workspace/public/assets/actiontext-78de0ebeae470799f9ec25fd0e20ae2d931df88c2ff9315918d1054a2fca2596.js
remote:       I, [2025-04-28T23:13:52.749559 #9795]  INFO -- : Writing /workspace/public/assets/actiontext-78de0ebeae470799f9ec25fd0e20ae2d931df88c2ff9315918d1054a2fca2596.js.gz
remote:       I, [2025-04-28T23:13:52.749673 #9795]  INFO -- : Writing /workspace/public/assets/actiontext.esm-328ef022563f73c1b9b45ace742bd21330da0f6bd6c1c96d352d52fc8b8857e5.js
remote:       I, [2025-04-28T23:13:52.749750 #9795]  INFO -- : Writing /workspace/public/assets/actiontext.esm-328ef022563f73c1b9b45ace742bd21330da0f6bd6c1c96d352d52fc8b8857e5.js.gz
remote:       I, [2025-04-28T23:13:52.749856 #9795]  INFO -- : Writing /workspace/public/assets/trix-e17a480fcb4e30c8571f0fed42dc81de5faeef93755ca30fe9623eb3f5c709e5.js
remote:       I, [2025-04-28T23:13:52.749932 #9795]  INFO -- : Writing /workspace/public/assets/trix-e17a480fcb4e30c8571f0fed42dc81de5faeef93755ca30fe9623eb3f5c709e5.js.gz
remote:       I, [2025-04-28T23:13:52.750021 #9795]  INFO -- : Writing /workspace/public/assets/trix-5552afe828fe79c41e53b9cc3616e9d7b8c2de1979ea62cbd663b88426ec41de.css
remote:       I, [2025-04-28T23:13:52.750090 #9795]  INFO -- : Writing /workspace/public/assets/trix-5552afe828fe79c41e53b9cc3616e9d7b8c2de1979ea62cbd663b88426ec41de.css.gz
remote:       I, [2025-04-28T23:13:52.750179 #9795]  INFO -- : Writing /workspace/public/assets/activestorage-503a4fe23aabfbcb752dad255f01835904e6961d5f20d1de13987a691c27d9cd.js
remote:       I, [2025-04-28T23:13:52.750249 #9795]  INFO -- : Writing /workspace/public/assets/activestorage-503a4fe23aabfbcb752dad255f01835904e6961d5f20d1de13987a691c27d9cd.js.gz
remote:       I, [2025-04-28T23:13:52.750356 #9795]  INFO -- : Writing /workspace/public/assets/activestorage.esm-b3f7f0a5ef90530b509c5e681c4b3ef5d5046851e5b70d57fdb45e32b039c883.js
remote:       I, [2025-04-28T23:13:52.750427 #9795]  INFO -- : Writing /workspace/public/assets/activestorage.esm-b3f7f0a5ef90530b509c5e681c4b3ef5d5046851e5b70d57fdb45e32b039c883.js.gz
remote:       I, [2025-04-28T23:13:52.750528 #9795]  INFO -- : Writing /workspace/public/assets/actioncable-1c7f008c6deb7b55c6878be38700ff6bf56b75444a086fa1f46e3b781365a3ea.js
remote:       I, [2025-04-28T23:13:52.750598 #9795]  INFO -- : Writing /workspace/public/assets/actioncable-1c7f008c6deb7b55c6878be38700ff6bf56b75444a086fa1f46e3b781365a3ea.js.gz
remote:       I, [2025-04-28T23:13:52.750715 #9795]  INFO -- : Writing /workspace/public/assets/actioncable.esm-06609b0ecaffe2ab952021b9c8df8b6c68f65fc23bee728fc678a2605e1ce132.js
remote:       I, [2025-04-28T23:13:52.750794 #9795]  INFO -- : Writing /workspace/public/assets/actioncable.esm-06609b0ecaffe2ab952021b9c8df8b6c68f65fc23bee728fc678a2605e1ce132.js.gz
remote:       ** Invoke assets:clean (first_time)
remote:       ** Invoke assets:environment
remote:       ** Execute assets:clean
remote:
remote:   - Done (1.5s)
remote:   - Storing cache for /workspace/public/assets
remote:   - Storing cache for /workspace/tmp/cache/assets
remote: - Done (finished in 1m 51s)
remote:
remote: ## Procfile Buildpack
remote:
remote: - Processes from `Procfile`
remote:   - web: `bundle exec puma -C config/puma.rb`
remote: - Done (finished in < 0.1s)
remote: Adding layer 'heroku/nodejs-engine:available_parallelism'
remote: Adding layer 'heroku/nodejs-engine:dist'
remote: Adding layer 'heroku/nodejs-engine:web_env'
remote: Adding layer 'heroku/ruby:binruby'
remote: Adding layer 'heroku/ruby:bundler'
remote: Adding layer 'heroku/ruby:cache_public_assets'
remote: Adding layer 'heroku/ruby:cache_tmp_cache_assets'
remote: Adding layer 'heroku/ruby:env_defaults'
remote: Adding layer 'heroku/ruby:gems'
remote: Adding layer 'heroku/ruby:user_binstubs'
remote: Adding layer 'buildpacksio/lifecycle:launch.sbom'
remote: Added 1/1 app layer(s)
remote: Adding layer 'buildpacksio/lifecycle:launcher'
remote: Adding layer 'buildpacksio/lifecycle:config'
remote: Adding layer 'buildpacksio/lifecycle:process-types'
remote: Adding label 'io.buildpacks.lifecycle.metadata'
remote: Adding label 'io.buildpacks.build.metadata'
remote: Adding label 'io.buildpacks.project.metadata'
remote: Setting default process type 'web'
remote: Saving powerful-fortress-82261/builds...
remote: *** Images (sha256:f204ec0c10e222e330f7effab6b73b7366369b6e9a456379d1b8999555fb9222):
remote:       powerful-fortress-82261/builds:217cecc4-528c-4b26-aa35-50a76db44b74
remote: Adding cache layer 'heroku/nodejs-engine:dist'
remote: Adding cache layer 'heroku/ruby:binruby'
remote: Adding cache layer 'heroku/ruby:bundler'
remote: Adding cache layer 'heroku/ruby:cache_public_assets'
remote: Adding cache layer 'heroku/ruby:cache_tmp_cache_assets'
remote: Adding cache layer 'heroku/ruby:gems'
remote: Uploading cache
remote: Launching...
remote: http://powerful-fortress-82261-3670d9de0fe8.plum-virginia.herokuapp.com/ deployed to Heroku
remote: Verifying deploy... done.
To https://git.heroku.com/powerful-fortress-82261.git
 * [new branch]      main -> main

The app is now deployed. The default dyno size for Fir Private Spaces is dyno-1c-0.5gb.

Visit the app at the URL shown in the logs. As a shortcut, you can also open the website as follows:

$ heroku open

View Logs

Fir apps do not retain log history like Cedar apps. To view an event in your Fir logs, you must run the logging command while that event occurs.

Heroku treats logs as streams of time-ordered events, aggregated from the output streams of all your app and Heroku components. Heroku provides a single stream for all events. View information about your running app by using one of the logging commands:

$ heroku logs
Fetching logs...

2025-04-28T23:16:24.521242+00:00 app[web-65bc95dcdb-48zcz]: I, [2025-04-28T23:16:24.521088 #14]  INFO -- : [94118f1f-e2af-8b11-4fe5-eb701d01f117] Started GET "/" for 13.110.54.12 at 2025-04-28 23:16:24 +0000
2025-04-28T23:16:24.521885+00:00 app[web-65bc95dcdb-48zcz]: I, [2025-04-28T23:16:24.521808 #14]  INFO -- : [94118f1f-e2af-8b11-4fe5-eb701d01f117] Processing by WelcomeController#index as */*
2025-04-28T23:16:24.523457+00:00 app[web-65bc95dcdb-48zcz]: I, [2025-04-28T23:16:24.523371 #14]  INFO -- : [94118f1f-e2af-8b11-4fe5-eb701d01f117]   Rendered layout layouts/application.html.erb (Duration: 0.8ms | Allocations: 327)
2025-04-28T23:16:24.523650+00:00 app[web-65bc95dcdb-48zcz]: I, [2025-04-28T23:16:24.523591 #14]  INFO -- : [94118f1f-e2af-8b11-4fe5-eb701d01f117] Completed 200 OK in 2ms (Views: 1.2ms | ActiveRecord: 0.0ms | Allocations: 590)
2025-04-28T23:16:28.428315+00:00 heroku-router[web]: at=info method=GET path="/" host=powerful-fortress-82261-3670d9de0fe8.plum-virginia.herokuapp.com request_id=9c4ce527-1188-f40f-b4cd-0a0ceb37d480 fwd="123.456.789.0" dyno=web-65bc95dcdb-48zcz connect=1ms service=5ms status=200 bytes=9175 protocol=http tls_version=tls1.3

To generate more log messages, refresh the app in your browser.

To stop streaming the logs, press Ctrl+C.

Declare App Dependencies

Heroku recognizes an app as a Ruby app by the existence of a Gemfile file in the root directory.

The demo app you deployed already has a Gemfile:

source 'https://rubygems.org'
ruby '>= 3.1', '< 3.4'

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 7.1.3'
# Use postgresql as the database for Active Record
gem 'pg', '~> 1.5.9'
# Use SCSS for stylesheets
gem 'sass-rails'
# Use Uglifier as compressor for JavaScript assets
gem 'uglifier'
...

The Gemfile file specifies the dependencies to install with your application. It also determines the version of Ruby used to run your application on Heroku.

When an app deploys, Heroku reads this file and installs the appropriate Ruby version and dependencies using the bundle install command.

To run the app locally, you must also install dependencies locally. This Gemfile dependency pg only resolves if you have Postgres installed locally. Install Postgres before you proceed.

If the command which psql returns some value on your command line, you have Postgres installed locally:

$ which psql
/usr/local/bin/psql

Run bundle install in your local directory to install the dependencies, preparing your system for running the app locally:

$ bundle install
Bundle complete! 13 Gemfile dependencies, 84 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.

After installing dependencies, you can run your app locally.

Run the App Locally

The sample app uses a database, so you must create the database and table locally using the rake task:

$ bundle exec rake db:create db:migrate
Created database 'ruby-getting-started_development'
Created database 'ruby-getting-started_test'
== 20140707111715 CreateWidgets: migrating ====================================
-- create_table(:widgets)
   -> 0.0022s
== 20140707111715 CreateWidgets: migrated (0.0023s) ===========================

== 20220606153049 AddServiceNameToActiveStorageBlobs: migrating ===============
-- table_exists?(:active_storage_blobs)
   -> 0.0005s
== 20220606153049 AddServiceNameToActiveStorageBlobs: migrated (0.0005s) ======

== 20220606153050 CreateActiveStorageVariantRecords: migrating ================
-- table_exists?(:active_storage_blobs)
   -> 0.0004s
== 20220606153050 CreateActiveStorageVariantRecords: migrated (0.0004s) =======

== 20220606153051 RemoveNotNullOnActiveStorageBlobsChecksum: migrating ========
-- table_exists?(:active_storage_blobs)
   -> 0.0006s
== 20220606153051 RemoveNotNullOnActiveStorageBlobsChecksum: migrated (0.0006s)

Now start your application locally using the heroku local command:

$ heroku local web --port=5006
[OKAY] Loaded ENV .env File as KEY=VALUE Format
6:16:33 PM web.1 |  [73319] Puma starting in cluster mode...
6:16:33 PM web.1 |  [73319] * Puma version: 6.6.0 ("Return to Forever")
6:16:33 PM web.1 |  [73319] * Ruby version: ruby 3.2.4 (2024-04-23 revision af471c0e01) [arm64-darwin24]
6:16:33 PM web.1 |  [73319] *  Min threads: 5
6:16:33 PM web.1 |  [73319] *  Max threads: 5
6:16:33 PM web.1 |  [73319] *  Environment: development
6:16:33 PM web.1 |  [73319] *   Master PID: 73319
6:16:33 PM web.1 |  [73319] *      Workers: 2
6:16:33 PM web.1 |  [73319] *     Restarts: (✔) hot (✖) phased
6:16:33 PM web.1 |  [73319] * Preloading application
6:16:33 PM web.1 |  [73319] * Listening on http://[::]:5006
6:16:33 PM web.1 |  [73319] Use Ctrl-C to stop
6:16:33 PM web.1 |  [73319] - Worker 0 (PID: 73320) booted in 0.0s, phase: 0
6:16:33 PM web.1 |  [73319] - Worker 1 (PID: 73321) booted in 0.0s, phase: 0

Just like Heroku, heroku local uses the Procfile to know what command to execute.

To see your app running locally,open http://localhost:5006 with your web browser.

To stop the app from running locally, in the CLI, press Control + C to exit.

Push Local Changes

In this step, you propagate a local change to the application to Heroku.

Modify Gemfile to include an additional dependency for the cowsay gem.

In file Gemfile, on line 4 add:

gem "cowsay"

The file now looks like this:

source 'https://rubygems.org'
ruby '>= 3.1', '< 3.4'

gem "cowsay"
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
...

Modify app/views/welcome/index.erb to use the cowsay gem.

At the end of app/views/welcome/index.erb add:

<pre><%= Cowsay.say("Hello", "tux") %></pre>

Now test locally:

$ bundle install
$ heroku local --port=5006

Visit your application at http://localhost:5006. If your changes worked, you see a cute ASCII picture displayed.

Now deploy this local change to Heroku.

Almost every deploy to Heroku follows this same pattern. First, add the modified files to the local Git repository:

$ git add .

Now commit the changes to the repository:

$ git commit -m "Added cowsay gem"
[main 1bfd4bb] Added cowsay gem
 5 files changed, 7 insertions(+), 1 deletion(-)
 create mode 100644 install.txt

Now deploy as before:

$ git push heroku main

Finally, check that everything is working:

$ heroku open

Debugging

The Heroku Ruby Cloud Native Buildpack (CNB) turns your code into an Open Container Initiative (OCI) container image when you deploy to Fir. This image gets executed on our dynos.

You can use this image locally to reproduce and debug deployment problems. Build an OCI image from your application to debug locally by using the Heroku Ruby CNB. If you’re interested, check out the Ruby CNB tutorial.

Start a Console

The heroku run command to launch an interactive one-off dyno is unavailable for Fir. As an alternative, use heroku run:inside to access a running dyno until we add heroku run for Fir.

You must add an SSH key to your Heroku account before running this command.

To execute the command you need the name of a currently running process. You can see a list with heroku ps:

$ heroku ps
=== web (dyno-1c-0.5gb): bundle exec puma -C config/puma.rb (1)

web-65bc95dcdb-48zcz: up 2025/04/28 18:16:07 -0500 (~ 29s ago)

Use that dyno name to run an interactive bash session:

$ heroku run:inside web-65bc95dcdb-48zcz "bash"
Running launcher bash on powerful-fortress-82261...
Running launcher bash on powerful-fortress-82261... up, web-65bc95dcdb-48zcz
heroku@web-65bc95dcdb-48zcz:/workspace$ ruby -v
ruby 3.2.4 (2024-04-23 revision af471c0e01) [aarch64-linux]
heroku@web-65bc95dcdb-48zcz:/workspace$ rails -v
Rails 7.1.3.2
heroku@web-65bc95dcdb-48zcz:/workspace$ ls -lah
total 60K
drwxrwsrwx. 1 heroku heroku   17 Jan  1  1980 .
drwxr-xr-x. 1 root   root     34 Apr 28 23:16 ..
-rw-r--r--. 1 heroku heroku    9 Jan  1  1980 .env
drwxr-xr-x. 2 heroku heroku   46 Jan  1  1980 .github
-rw-r--r--. 1 heroku heroku  504 Jan  1  1980 .gitignore
-rw-r--r--. 1 heroku heroku 1.3K Jan  1  1980 Gemfile
-rw-r--r--. 1 heroku heroku 5.9K Jan  1  1980 Gemfile.lock
-rw-r--r--. 1 heroku heroku   40 Jan  1  1980 Procfile
-rw-r--r--. 1 heroku heroku 3.0K Jan  1  1980 README.md
-rw-r--r--. 1 heroku heroku  249 Jan  1  1980 Rakefile
drwxr-xr-x. 8 heroku heroku   96 Jan  1  1980 app
-rw-r--r--. 1 heroku heroku  281 Jan  1  1980 app.json
drwxr-xr-x. 2 heroku heroku   98 Jan  1  1980 bin
drwxr-xr-x. 5 heroku heroku  16K Jan  1  1980 config
-rw-r--r--. 1 heroku heroku  154 Jan  1  1980 config.ru
drwxr-xr-x. 3 heroku heroku   54 Jan  1  1980 db
drwxr-xr-x. 4 heroku heroku   33 Jan  1  1980 lib
drwxr-xr-x. 2 heroku heroku   19 Jan  1  1980 log
-rw-r--r--. 1 heroku heroku   43 Jan  1  1980 package.json
drwxr-xr-x. 3 heroku heroku  105 Jan  1  1980 public
drwxr-xr-x. 8 heroku heroku  126 Jan  1  1980 test
drwxr-sr-x. 1 heroku heroku   38 Apr 28 23:16 tmp
drwxr-xr-x. 3 heroku heroku   20 Jan  1  1980 vendor
heroku@web-65bc95dcdb-48zcz:/workspace$ exit
exit

If you receive an error, Error connecting to process, configure your firewall.

Type exit to exit the shell. You can run any command this way such as rails console or rake db:migrate.

Define Config Vars

Heroku lets you externalize configuration by storing data such as encryption keys or external resource addresses in config vars.

At runtime, we expose config vars as environment variables to the application.

Config vars for Fir-generation apps are only available at runtime by default. You can enable Build Time Config Vars to allow for both build and runtime availability.

For example, modify app/views/welcome/index.erb so that the method repeats an action depending on the value of the TIMES environment variable. Change the file so that its first few lines read as follows:

<% for i in 0..(ENV['TIMES'] ? ENV['TIMES'].to_i : 2) do %>
  <p>Hello World #<%= i %>!</p>
<% end %>

The heroku local command automatically sets up the environment based on the contents of the .env file in your local directory. In the top level directory of your sample project, there’s already a .env file that contains:

TIMES=10

If you run the app with heroku local --port=5006, you see “Hello World” ten times when you refresh your browser.

To set the config var on Heroku, execute the following:

$ heroku config:set TIMES=10
Setting TIMES and restarting powerful-fortress-82261...
Setting TIMES and restarting powerful-fortress-82261... done, v5
TIMES: 10

View the app’s config vars using heroku config:

$ heroku config
TIMES: 10
...

To see this change in action, deploy your changed application to Heroku.

Use a Database

Listing the config vars for your app displays the URL that your app uses to connect to the database, DATABASE_URL:

$ heroku config
DATABASE_URL:                postgres://xx:yyy@host:5432/d8slm9t7b5mjnd
HEROKU_POSTGRESQL_BROWN_URL: postgres://xx:yyy@host:5432/d8slm9t7b5mjnd
...

Heroku also provides a pg command that shows a lot more information:

$ heroku pg
=== DATABASE_URL

Plan:                  essential-0
Status:                Available
Connections:           unknown/20
PG Version:            16.4
Created:               2025-04-28 23:10
Data Size:             unknown usage / 1 GB (In compliance)
Tables:                0/4000 (In compliance)
Fork/Follow:           Unsupported
Rollback:              Unsupported
Continuous Protection: Off
Add-on:                postgresql-silhouetted-53983

The example app you deployed already has database functionality. It has a controller and database model for widgets, used by your app’s /widgets page. You can visit the page by appending /widgets to your app’s URL.

If you visit the URL, you see an error page appear. Check out the error message using heroku logs or in Papertrail to see something like this:

PG::UndefinedTable: ERROR:  relation "widgets" does not exist

This error indicates that while we could connect to the database, the widgets table wasn’t found. You can fix that error by running rake db:migrate via run:inside. To execute this command on Heroku, run it inside an existing dyno like so:

$ heroku run:inside web-65bc95dcdb-48zcz "rake db:migrate"
Running launcher rake db:migrate on powerful-fortress-82261...
Running launcher rake db:migrate on powerful-fortress-82261... up, web-65bc95dcdb-48zcz
I, [2025-04-28T23:16:55.288700 #58]  INFO -- : Migrating to CreateWidgets (20140707111715)
== 20140707111715 CreateWidgets: migrating ====================================
-- create_table(:widgets)
   -> 0.0156s
== 20140707111715 CreateWidgets: migrated (0.0158s) ===========================

I, [2025-04-28T23:16:55.312325 #58]  INFO -- : Migrating to AddServiceNameToActiveStorageBlobs (20220606153049)
== 20220606153049 AddServiceNameToActiveStorageBlobs: migrating ===============
-- table_exists?(:active_storage_blobs)

Now if you visit the /widgets page of your app again, you can list and create widget records.

If you have Postgres installed locally, you can also interact directly with the database. For example, here’s how to connect to the database using psql and execute a query:

$ heroku pg:psql
d8slm9t7b5mjnd=> \x
d8slm9t7b5mjnd=> select * from widgets;
-[ RECORD 1 ]---------------------------
id          | 1
name        | My Widget
description | It's amazing
stock       | 100
...

Read more about Heroku PostgreSQL.

Delete Your App

Remove the app from your account. We only charge you for the resources you used.

This action permanently deletes your application and any add-ons attached to it.

$ heroku apps:destroy

You can confirm that your app is gone with this command:

$ heroku apps --all

Next Steps

You now know how to configure and deploy a Ruby app, view logs, and start a console.

To learn more, see:

  • How Heroku Works
  • Preparing a Codebase for Heroku Deployment
  • Heroku Ruby Documentation
  • Getting Started with Rails 8.x on Heroku

Information & Support

  • Getting Started
  • Documentation
  • Changelog
  • Compliance Center
  • Training & Education
  • Blog
  • Support Channels
  • Status

Language Reference

  • Node.js
  • Ruby
  • Java
  • PHP
  • Python
  • Go
  • Scala
  • Clojure
  • .NET

Other Resources

  • Careers
  • Elements
  • Products
  • Pricing
  • RSS
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku Blog
    • Heroku News Blog
    • Heroku Engineering Blog
  • Twitter
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku
    • Heroku Status
  • Github
  • LinkedIn
  • © 2025 Salesforce, Inc. All rights reserved. Various trademarks held by their respective owners. Salesforce Tower, 415 Mission Street, 3rd Floor, San Francisco, CA 94105, United States
  • heroku.com
  • Legal
  • Terms of Service
  • Privacy Information
  • Responsible Disclosure
  • Trust
  • Contact
  • Cookie Preferences
  • Your Privacy Choices