Moving From Heroku to AWS

This guide will show you how you can easily move away from Heroku and on to Amazon Web Services (AWS). AWS is a secure cloud services platform, offering compute power, database storage, content delivery and other functionality to help businesses scale and grow. Millions of developers use AWS to build sophisticated applications with increased flexibility, scalability and reliability.

If you're interested in learning all the complexities of provisioning, configuring, and managing a production infrastructure this guide probably isn't for you. The idea here is to make migrating from Heroku to AWS as simple and painless as possible. Since you're already using Heroku, I'm betting you're not really interested in all the nitty gritty details. You just want a way to get all of the great things Heroku has to offer, without Heroku. Nanobox is a powerful Heroku alternative that gives you the same great workflow you're used to with Heroku with all the flexibility and control of AWS.

Getting Started

If you haven't already, you'll need to create a free Nanobox account and download Nanobox Desktop.

Configure Your App

Every web application has an underlying infrastructure. This infrastructure is typically made up of web servers, databases, and workers. The purpose of Nanobox is to provision, configure, and deploy your application's infrastructure so you don't have to.

Similar to Heroku's Procfile, Nanobox reads a config file called a boxfile.yml which describes your application's infrastructure. This file lives at the root of your project and tells Nanobox everything your application needs so Nanobox can build and configure your infrastructure for you.

Procfile to boxfile.yml Example

Translating a Procfile to a boxfile.yml is very simple. A typical Procfile might look like this:

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

The matching boxfile.yml would look like this:

run.config:
  engine: ruby

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

worker.sidekiq:
  start: bundle exec sidekiq

data.db:
  image: nanobox/postgresql:9.6

Understanding the boxfile.yml

There are a few obvious differences between the Procfile and the boxfile.yml. The following section explains each node individually to give you a better understanding of what everything means. If you aren't interested in the details, feel free to skip ahead.

The engine

An engine is comparable to a Heroku buildpack. It's a set of scripts that build and configure your app's environment and runtime. These scripts retrieve dependencies, compile your application code, and more.

Every boxfile.yml must have an engine specified:

run.config:
  engine: ruby

Here, the ruby engine tells Nanobox to build an environment that includes Ruby, Gem, and Bundler. The ruby engine also tells Nanobox to run bundle install for you during the build process.

Nanobox has engines available for many different languages:

nanobox-guides

Components

A Nanobox component is essentially the same thing as a Heroku process type. Nanobox has three component types available:

  • web: Receives HTTP, TCP, and UDP requests from your app’s router.
  • worker: Background process inaccessible from your app’s router.
  • data: Components designed for handling data of any kind – databases, caches, job queues, etc.
Web & Worker Components

Web and worker components only require a start command, although other config options are available. The start command would be the same command you pass with your process in a Procfile:

# Procfile
web: bundle exec puma -C config/puma.rb
worker: bundle exec sidekiq
# boxfile.yml
web.site:
  start: bundle exec puma -C config/puma.rb

worker.sidekiq:
  start: bundle exec sidekiq

Note: Each component in your boxfile.yml has a component ID that follows the pattern: type.name. For example, web.site or worker.sidekiq. The types are one of the three from above, but the name is completely arbitrary. Any additional component settings are nested under each ID.

Data Components

nanobox-data-components

Until recently, Heroku didn’t offer any internally managed database options, but instead integrated with external service providers through "Add-Ons". With Nanobox, databases are internally-managed as a part of your app’s private network and come with monitoring and scaling options.

Note: Nanobox's still allows for you to use externally hosted/managed data services such as S3 or RDS if you prefer.

If you choose to manage your data components with Nanobox, simply include them in your boxfile.yml. The only required option for a data component is the image, referencing the Docker image used to provision the component:

data.postgres:
  image: nanobox/postgresql:9.6

Note: You're free to use your own Docker images, however Nanobox provides official images that include functionality specific to Nanobox (recommended).

Additional Configuration

If you're already running on Heroku, there aren't many changes you'll need to make to your app, but there are some important things to note:

Listen on 0.0.0.0:8080

With Nanobox, in order for your app to receive requests from the public network, it must listen on 0.0.0.0:8080. This is generally configured in the app itself, as part of the web server config, when the app is started, or with Nginx.

If you want to use a custom port, you'll need to setup a proxy that forwards from 8080 down to your custom port. We actually recommend this method. However you still need to listen on 0.0.0.0 rather than localhost.

For an example of an Nginx proxy with Rails, check out the Configure Rails for Production guide. Examples for other frameworks are also provided in the Nanobox guides.

Database Management

Nanobox's flexibility allows you to easily connect to externally hosted/managed data services such as S3 or RDS. Or you can choose to use officially supported data components and have them managed through your Nanobox platform.

Externally Managed Database

If you choose to use an externally managed databases or services, you don't need to update your connection credentials unless those credentials are changing. Chances are that you're populating those with environment variables. If that's the case, just be sure to add those variables to your environments.

Nanobox Data Component

When using officially supported data services, Nanobox will auto-generate environment variables for each required credential, using the component's ID.

For example, with the following data components in a boxfile.yml:

data.postgres
  image: nanobox/postgresql:9.5

data.redis
  image: nanobox/redis:3.0

...the following environment variables will be generated:

# Postgres Connection Variables
DATA_POSTGRES_HOST
DATA_POSTGRES_USER
DATA_POSTGRES_PASS

# Redis Connection Variable
DATA_REDIS_HOST

Note: For data services that require a database name, we create a default gonano database, but you can also create your own. Also, the port will always be the service's default port.

Update Database Connections

Using environment variables for service connections ensures your app's portable across environments. You'll need to update your applications database config to connect using the Nanobox environment variables.

In Rails, for example, you might update your config/database.yml to something similar to this:

default: &default
  adapter: postgresql
  encoding: unicode
  pool: 5
  timeout: 5000
  host: <%= ENV['DATA_DB_HOST'] %>
  username: <%= ENV['DATA_DB_USER'] %>
  password: <%= ENV['DATA_DB_PASS'] %>

Persistent Storage

Heroku's recommended method for storing files that need to persist between deploys is using Amazon S3. You can do the same with Nanobox, but you also have another option with Nanobox storage components.

If you're going to stick with S3, just be sure to add the required auth credentials, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and S3_BUCKET_NAME as environment variables.

Setup Your AWS Account

If you haven't already, create an AWS account. In your AWS Management Console, create an IAM account that has read/write access to EC2 instances, security groups, and ssh keys. You can also setup a custom EC2 policy and user for Nanobox if you'd like.

Once the new account is created, create a new Access Key and download the key file. You'll need this and the key ID later.

Add a New Provider to Your Nanobox Account

In your Nanobox dashboard, go to your Account Admin > Hosting Accounts and click the "Add Account" button.

Add New Provider Account

Select AWS and click "Proceed."

Select AWS

Nanobox needs your AWS Access Key ID and the Access Key token to authenticate with your AWS account. Paste those in and click "Verify & Proceed."

Enter your AWS auth credentials

Name your provider and choose a default region. The name is arbitrary and only meant to help you identify it in your list of provider accounts.

Name your provider and select a default region

Launch a New App

Go to the home page of your Nanobox dashboard and click the "Launch New App" button. Select your AWS provider from the dropdown and choose the region in which you'd like to deploy your app.

Select your AWS provider

Confirm and click "Let's Go!" Nanobox will order an EC2 server on AWS under your account. When the server is up, Nanobox will provision platform components necessary for your app to run:

  • Load-Balancer: The public endpoint for your application. Routes and load-balances requests to web nodes.
  • Monitor: Monitors the health of your server(s) and application components.
  • Logger: Streams and stores your app's aggregated log stream.
  • Message Bus: Sends app information to the Nanobox dashboard.
  • Warehouse: Storage used for deploy packages, backups, etc.

Once all the platform components are provisioned and running, you're ready to deploy your app.

Stage Your App Locally

Nanobox provides "dry-run" functionality that simulates a full production deploy on your local machine. This step is optional, but recommended. If the app deploys successfully in a dry-run environment, it will work when deployed to your live environment.

nanobox deploy dry-run

More information about dry-run environments is available in the Dry-Run documentation.

Deploy

Add Your New App as a Remote

From the root of your project directory, add your newly created app as a remote.

nanobox remote add app-name

This connects your local codebase to your live app. More information about the remote command is available in the Nanobox Documentation.

Deploy to Your Live App

With your app added as a remote, you're ready to deploy.

nanobox deploy

Nanobox will compile and package your application code, send it up to your live app, provision all your app's components inside your live EC2 instance, network everything together, and voila! Your app will be live.

Manage & Scale

Once your app is deployed, Nanobox makes it easy to manage and scale your production infrastructure. In your Nanobox dashboard you'll find health metrics for all your app's instances/containers. Your application logs are streamed in your dashboard and can be streamed using the Nanobox CLI.

Although every app starts out on a single EC2 instance with containerized components, you can break components out into individual instances and/or scalable clusters through the Nanobox dashboard. Nanobox handles the deep DevOps stuff so you don't have to. Enjoy!

Moving can be a daunting task. Hopefully this guide has given you a good place to start. If you have any questions, please reach out!

Get in Touch

Posted in Heroku, AWS, Deployment, Migrating, EC2