This is more of a brain dump than a proper tutorial.

Why a Partial Migration?

I recently had to move parts of an app from Heroku to Disco. The app runs under an enterprise contract on Heroku, that allows for a fixed amount of resources. Dyno units as they call them. The usage increased temporarily due to a data migration that’s taking a few months, and Heroku checked in to see if the size of the contract should be increased.

Since the enterprise contract with Heroku still runs for another 6 months, I decided to move only a few workers to Disco, and leave the rest on Heroku for now.

Choosing the Right Datacenter

Since the app is connected to a Postgres database and RabbitMQ, it makes sense to put the new Disco server in the same datacenter as the app running on Heroku.

You can find the region on Heroku using:

heroku info <APPNAME>

One of the fields is the region:

...
Region:         us
...

You can then find that region in the list of regions:

heroku regions --json
{
  "country": "United States",
  "created_at": "2012-11-21T20:44:16Z",
  "description": "United States",
  "id": "59accabd-516d-4f0e-83e6-6e3757701145",
  "locale": "Virginia",
  "name": "us",
  "private_capable": false,
  "provider": {
    "name": "amazon-web-services",
    "region": "us-east-1"
  },
  "updated_at": "2016-08-09T22:03:28Z"
}

In this case, it’s AWS us-east-1.

Using A Specific Dockerfile

The project already uses Docker for various purposes: Docker Compose for development, stagings run on Disco, the CI builds use Docker images. It was pretty easy to create a minimal Dockerfile that only installed the minimum for the workers I was moving to Disco. And then, since the project has many Dockerfile with different names, I just had to specify which one to use in disco.json:

{
    "version": "1.0",
    "services":
    {
        "worker_general":
        {
            "command": "worker queues=default,rush"
        },
        "worker_background":
        {
            "command": "worker queues=background,rush"
        }
    },
    "images":
    {
        "default":
        {
            "dockerfile": "Dockerfile.prod.worker"
        }
    }
}

Managing Multiple disco.json Configurations

Similarly, since the project already runs staging environments on Disco, we already had a disco.json with a different list of services.

Disco let’s you specify another file by setting the environment variable DISCO_JSON_PATH to the name of the file to use instead. E.g.

disco env:set \
    DISCO_JSON_PATH='disco.staging.json' \
    --project staging-one

Transferring Heroku Environment Variables

I exported the environment variables from Heroku using:

heroku config --app app-name

I removed a few magic Heroku variables, like HEROKU_APP_NAME and manually edited the output with VS Code multi-cursors to get a command like this:

disco env:set \
    VAR_ONE='value one' \
    VAR_TWO='value two' \
    VAR_THREE='value three' \
    --project prod-workers

Keeping The Branch Clean

When creating the project, I specified the branch I wanted to use (production in this case, but it could be main or master). But to avoid making the branch dirty by pushing (or force pushing) many small tweaks to get everything right, I pushed to antoher branch, and manually deployed using

disco deploy \
    --commit 788ec8b1a1429f18cf28cf4937a54d9a57f9ae8a \
    --project prod-workers

And when everything looked good, I squashed all the commits and merged to the correct branch (production).