Documentation Table of Contents

3. Deploying Applications

Deploying to Build transforms your application source code into a running, accessible service. By connecting your GitHub repository through the dashboard, Build handles the complexity of building, releasing, and running your application so you can focus on writing code.

This section covers everything you need to know about getting your application onto Build, from initial preparation through to advanced deployment workflows using release phases and custom buildpacks.

3.1 Preparing your app for deployment

Before deploying to Build, your application needs to meet a few requirements to ensure a smooth deployment process. Most of these are standard practices for modern web applications, and you may already have them in place.

Host Your Code on GitHub

Build deploys applications directly from GitHub repositories. Your application code should be hosted in a GitHub repository that you have access to—either in your personal account or in an organisation you belong to.

If your project isn't already a Git repository, initialise one and push it to GitHub:

$ cd my-app

$ git init

$ git add .

$ git commit -m "Initial commit"

$ git remote add origin https://github.com/you/my-app.git

$ git push -u origin main

Your application code must be committed and pushed to GitHub before it can be deployed. Build deploys whatever is in your repository at the time of deployment—uncommitted or unpushed changes are not included.

Declare Dependencies

Build needs to know what dependencies your application requires. This is done through the standard dependency file for your language or framework:

These files should be located in the root directory of your repository. Build uses them both to detect your application's language and to install the necessary dependencies during the build process.

Add a Procfile

While Build can often detect how to start your application automatically, adding a Procfile gives you explicit control over the commands used to run your app. A Procfile is a simple text file named Procfile (no extension) placed in the root of your repository.

For example, a simple ruby web application using the puma web server might have:

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

See section 3.4 for detailed information on Procfiles and process types.

Use Environment Variables for Configuration

Following the twelve-factor app methodology, your application should read configuration from environment variables rather than hardcoding values. This includes database URLs, API keys, feature flags, and any other environment-specific settings.

Build provides a straightforward way to manage these through config vars, which are securely injected into your application's environment at runtime. You can manage config vars through the Apps’s Settings tab:

3.2 Deploying

Build deploys applications directly from GitHub repositories through the dashboard. You connect your repository once, and then trigger deployments manually or automatically whenever you push changes to GitHub.

Step 1: Create an App

Locate the New + dropdown button next to your avatar in the top-right corner of the page. Click New App from this menu.

On the page that opens, give your app a name and select your preferred region (e.g., us-east-1). Click Create App to proceed.

You'll be taken to your new app's overview page, with various tabs along the top for managing different aspects of your application.

Step 2: Select a Stack

Click on the Settings tab. Locate the Stack section and select the latest Heroku stack if you want to build your app using the recommended buildpack approach.

The stack determines the base operating system and runtime environment for your application. For most applications, the default stack provides everything you need.

Step 3: Configure Buildpacks

Further down on the Settings page, locate the Buildpacks section. Add any language or framework-specific buildpacks that your application requires.

For example, if you're deploying a Ruby application, add the official Heroku Ruby buildpack by specifying the full path to its GitHub repository:

https://github.com/heroku/heroku-buildpack-ruby

For most common languages, Build can auto-detect the appropriate buildpack. However, explicitly specifying buildpacks gives you more control and ensures consistent builds. See section 3.4 for more details on buildpacks.

Step 4: Connect Your GitHub Repository

Click on the Deploy tab, then locate the Connection section. This is where you connect your app to a GitHub repository.

Select your GitHub organisation from the dropdown, then search for the repository name. From the results, click Connect next to the repository you want to deploy.

Once connected, the Connection section will display a confirmation showing the linked repository.

Step 5: Deploy

Scroll to the bottom of the Deploy page to find the Manual Deploy section. Select the branch you want to deploy and click Deploy Branch.

You'll be redirected to the Build page, which shows your application being built in real-time. You can watch as Build detects your application type, installs dependencies, and compiles your code.

Once the build completes successfully, your application is automatically deployed. Navigate to the Overview tab to see your app's status, including when it was last built and deployed. Click the Go link in the top-right corner to open your newly deployed application.

3.3 Buildpacks

Buildpacks handle the heavy lifting of turning your source code into something that can actually run on Build's infrastructure. They figure out what language you're using, pull in all your dependencies, compile everything that needs compiling, and set up the runtime environment—without you having to configure any of it manually.

How Buildpacks Work

Buildpacks operate through a three-stage process. First, during detection, Build scans your repository looking for signature files that indicate which language or framework you're using—for instance, the Ruby buildpack looks for a Gemfile, while the Node.js buildpack looks for package.json. Next comes compilation, where the matched buildpack takes over: it downloads your dependencies, compiles assets if needed, and packages everything into a deployable unit. Finally, the release stage outputs any default configuration your application needs at runtime.

Setting Buildpacks via the Dashboard

Navigate to your app's Settings tab and scroll to the Buildpacks section. Click Add Buildpack and enter either the name of an official buildpack or the full URL to a custom buildpack's GitHub repository.

For example, to add the official Ruby buildpack:

https://github.com/heroku/heroku-buildpack-ruby

Buildpacks are executed in the order they appear in the list. You can drag to reorder them or remove buildpacks you no longer need.

Using Multiple Buildpacks

Some applications require multiple buildpacks. A common example is a Ruby on Rails application that also needs Node.js for asset compilation. Build executes buildpacks in order, with each one able to use binaries installed by previous buildpacks.

For example, a Rails application with JavaScript assets might have:

1. https://github.com/heroku/heroku-buildpack-nodejs

2. https://github.com/heroku/heroku-buildpack-ruby

Third-Party and Custom Buildpacks

If your application uses a language or framework not covered by official buildpacks, you can use third-party buildpacks by specifying their Git URL in the Buildpacks section of the Settings tab.

You can also create your own buildpack to support custom build processes or languages. At its core, a buildpack is a collection of three shell scripts: bin/detect checks whether the buildpack is appropriate for a given codebase, bin/compile handles dependency installation and asset compilation, and bin/release outputs configuration for the runtime environment.

3.4 Procfiles and process types

The Procfile is where you tell Build exactly how to run your application. It's a simple configuration file that maps process names to the commands that start them, giving you fine-grained control over what happens when your dynos boot up.

Procfile Basics

Create a file called Procfile (no file extension, capitalised P) and place it in your repository's root directory. Inside this file, each line defines one process type using this format:

process_name: command to execute

For example, a simple web application might have:

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

A more complex application with background processing might include:

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

worker: bundle exec sidekiq

The Web Process Type

Among all process types, web holds a unique role: Build's routing layer forwards incoming HTTP requests exclusively to processes of this type. Any application that needs to respond to web traffic must define a web process.

Your web process must listen on the port specified by the $PORT environment variable. When using Puma, this is typically configured in config/puma.rb:

port ENV.fetch("PORT") { 3000 }

And referenced in your Procfile:

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

Other Process Types

Beyond the web process, you can define any number of additional process types to handle work that doesn't involve responding to HTTP requests. These run as separate dynos and are ideal for background job processing, scheduled tasks, or long-running operations.

Popular use cases include worker processes that pull jobs from a queue (using tools like Sidekiq or Resque), clock processes that trigger scheduled tasks at specific intervals, and release processes that run during deployment.

Enabling Automatic Deploys

For continuous deployment, you can configure Build to automatically deploy whenever you push to a specific branch on GitHub. On the Deploy tab, locate the Automatic Deploys section, select your branch, and click Enable Automatic Deploys.

With automatic deploys enabled, every push to the configured branch triggers a new build and deployment—no need to visit the dashboard.

Redeploying

To deploy a new version of your application, simply push your changes to GitHub. If automatic deploys are enabled, Build will detect the new commits and start a build automatically.

If you're using manual deploys, navigate to the Deploy tab and click Deploy Branch in the Manual Deploy section to trigger a new build from the latest commit on your selected branch.

Deploying a Specific Commit

By default, Build deploys the latest commit on your selected branch. If you need to deploy a specific commit (for example, to roll back to a previous version), you can do so from the Activity tab by locating the previous successful build and clicking “Rollback to here”.

3.5 Container/Docker deployments

For applications that require custom system dependencies, specific runtime versions, or a precisely controlled environment, Build supports deploying Docker containers directly. This gives you complete control over your application's runtime environment while still benefiting from Build's managed infrastructure, routing, and add-ons ecosystem.

When to Use Container Deployments

Container deployments are ideal when you need to install system-level dependencies not available in the standard Build runtime (such as FFmpeg, ImageMagick, or custom libraries), use a specific Linux distribution or base image, maintain an existing Docker-based workflow, or run applications in languages not officially supported by Build's buildpacks.

For most applications, Build's buildpack-based deployments are simpler and provide automatic security updates to the base image. Only use container deployments when you have a specific requirement that buildpacks cannot meet.

Setting Up Dockerfile Deployments

To use a Dockerfile instead of buildpacks, you need to change your app's stack setting and add a Dockerfile to your repository.

Step 1: Add a Dockerfile to Your Repository

Create a Dockerfile in the root of your repository. Here's a simple example for a Ruby application:

FROM ruby:3.2-alpine

RUN apk add --no-cache build-base postgresql-dev yaml-dev ruby-dev

WORKDIR /app

COPY Gemfile Gemfile.lock ./

RUN bundle install

COPY . .

EXPOSE 3000

CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]

The CMD instruction defines how your application starts. This becomes your web process.

Step 2: Set the Stack to Dockerfile

Navigate to your app's Settings tab in the dashboard. In the Stack section, select dockerfile from the dropdown.

This tells Build to use your Dockerfile for builds instead of auto-detecting and running buildpacks.

Step 3: Deploy

With the stack set and your Dockerfile committed to the repository, deploy as normal from the Deploy tab. Build will:

  • Pull your source code from GitHub
  • Locate the Dockerfile in your repository root
  • Build the image
  • Push the image to Build's internal registry
  • Deploy the container to your dynos

You can watch the build progress in real-time on the Build page, just like with buildpack deployments.

Process Types with Dockerfile Deployments

When using a Dockerfile, Build creates a single web process type based on your image's CMD or ENTRYPOINT instruction. This differs from buildpack deployments, which can extract multiple process types from a Procfile.

If your application needs background workers or other process types, you have a couple of options. You can use the same image with different commands by configuring additional process types in the Resources tab, overriding the default command for each. Alternatively, for more complex setups, you might keep your workers as a separate app with its own Dockerfile.

Listening on the Correct Port

Just like with buildpack deployments, your application must listen on the port specified by the PORT environment variable. Build injects this variable at runtime and routes traffic to it.

Make sure your application reads from this environment variable rather than hardcoding a port:

CMD ["bundle", "exec", "puma", "-C", "config/puma.rb", "-p", "$PORT"]

Or configure it in your application's server configuration file.

Best Practices

Keep your images small by using Alpine-based images where possible, and use multi-stage builds to separate build-time dependencies from runtime dependencies. This reduces deployment times and improves cold-start performance.

Layer your Dockerfile efficiently by copying dependency files (like Gemfile or package.json) before copying your full source code. This allows Docker to cache the dependency installation layer and only rebuild it when dependencies change.

Always specify explicit version tags for your base images rather than using latest. This ensures reproducible builds and prevents unexpected changes when base images are updated.

3.6 Release phases and hooks

Sometimes you need to run a task every time you deploy—migrating your database, clearing a cache, or uploading assets to a CDN. The release phase gives you a hook to do exactly that: a command that runs after your code is built but before your new dynos start accepting traffic.

How Release Phase Works

Here's what happens during a deployment with a release phase configured: Build first compiles your code and creates a deployable slug. Before spinning up your new dynos, it launches a temporary one-off dyno and runs your release command. If that command completes successfully (exits with code 0), the new version goes live. If something goes wrong and the command fails, Build halts the deployment entirely—your existing version keeps running as if nothing happened.

Defining a Release Phase

Add a release process type to your Procfile:

release: bundle exec rails db:migrate

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

For an application with background workers:

release: bundle exec rails db:migrate

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

worker: bundle exec sidekiq

The release command has access to all your application's config vars, including database connection strings and other secrets.

Common Release Phase Tasks

Release phases are commonly used for:

Database schema migrations – ensuring your database structure matches your new code before it starts serving requests

Uploading compiled assets – sending CSS, JavaScript, and images to a CDN or object storage

Priming or invalidating caches – warming up caches with new data or clearing stale entries

Running data transformation scripts – backfilling data or updating records to match new code expectations

Sending deployment notifications – alerting external services that a new version is being deployed

Retrying a Failed Release

If a release failed due to a temporary issue (such as a database being momentarily unavailable) rather than a code problem, you can retry the deployment. Navigate to the Deploy tab and click Deploy Branch to trigger a fresh build and release from the same commit.

Design Considerations

When using release phases, keep in mind that the release command runs in a one-off dyno with the new code but before the new dynos start. This means your new code must be compatible with the current database state before the release command runs.

For database migrations, ensure they are backwards-compatible so that if the deployment is rolled back, the previous code version can still function. A recommended approach is to make additive changes first (adding new columns or tables), deploy code that can work with both old and new schemas, then remove old columns or tables in a later release once all code is using the new schema.