Post

Running ASP.NET Core in Heroku

After running relatively critical application in multiple environments (staging and production) for a few months and building another one, I’m confident with saying it is possible and reasonable to run .NET Core app in Heroku.

The Why

While probably the most natural cloud environment for .net core app is Azure, there is still a good reason why would you want sometimes to host on Heroku - you get a wide PaaS offering without headache (also called add-ons).

So, let’s say your app needs PostgreSQL, Neo4j, RabbitMQ or something from this list, IMHO it will be faster to spin up environment in Heroku. You can go and try find a vendor that will maintain one of those services for you in Azure’s data center, while in Heroku they all provisioned literally with button click and scaled by slider.

Enter Heroku

Currently there are 2 options to deploy an app into Heroku’s containers (also called dynos):

  • Using Dockerfile, which is a pretty new in Heroku. There are some naming issues due to copyright, thus it’s called Container Registry and Runtime
  • Using buildpack, which is similar to Kudu in Azure. The buildpack is responsible to setup a correct runtime for the application in container, build the application and provision it into container

Each option has its pros and cons. After trying both, I decided to stick with buildpack.

Prepare app

In both cases you need to be able to pass port to which the Kestrel will bind itself. This can be done either by setting environment variable ASPNETCORE_URLS:

1
ASPNETCORE_URLS='http://+:\8080' dotnet run

or by specifying via command line arguments:

1
dotnet run --server.urls http://+:8080

For command line arguments to work, add Microsoft.Extensions.Configuration.CommandLine package to the application and in Program.cs you should have something similar to:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void Main(string[] args)
{
    var configurations = new ConfigurationBuilder()
        .AddCommandLine(args)
        .Build();

    new WebHostBuilder()
        .UseConfiguration(configurations)
        .UseKestrel()
        .UseContentRoot(Directory.GetCurrentDirectory())
        .UseStartup<Startup>()
        .Build()
        .Run();
}

This will take care of passing server.urls argument to Kestrel.

Deploying with Dockerfile

This is the most simple and suggested by Heroku (later on this) method to deploy. All you really need is a Dockerfile and the place to build the image. Yes, you will need to build the image by yourself (locally, in Docker hub or with CI tool) and then push it to Heroku’s registry. They don’t build images.

I tried multiple files with different base images and they all worked flawlessly. You can use Microsoft’s images (e.g. dotnet or aspnet) or build custom one where you install runtime or SDK by yourself.

I built a small demo just to demonstrate how easy it is. The source code located here, the image is here and app running at aspnet-core-dockerfile.herokuapp.com.

To deploy it (assuming the app in Heroku is aspnet-core-dockerfile):

1
2
3
4
5
heroku plugins:install heroku-container-registry
heroku container:login
docker pull jenyay/aspnet-core
docker tag jenyay/aspnet-core registry.heroku.com/aspnet-core-dockerfile/web
docker push registry.heroku.com/aspnet-core-dockerfile/web

BTW, there is also support for Docker Compose.

Deploying with buildpack

Heroku themself were experimenting with running ASP.NET on Mono (!). This repository has 5 years history with steady commits until one year ago. Half a year ago noliar sent PR which switches from Mono runtime to .NET Core. After nothing happened with this PR, I figured out that main contributors are no longer at Heroku, so I wrote to Heroku’s support where they basically told me that I should forget about this buildpack and go with Dockerfile.

You can use Noliar’s fork and it will work just fine. I had to fork his fork because I wanted to get ride of NodeJS installation (my application is pure API, so there is no NPM or Bower packages) and I needed to support multiple deployable projects (web and worker) within single solution.

So, to deploy with buildpack you will need to:

  • Figure out which buildpack to use or create your own.
  • Define buildpack in Heroku application. This can be done either via UI by pointing to one of git repositories or from CLI

    heroku buildpacks:set https://github.com/jenyayel/dotnet-buildpack

  • Add .deployment file in root of your source code repository. This file instructs buildpack which project/s needs to be published. Assuming you are deploying WebAPI project, then the content of the file should be:

    [config] project = src/WebAPI

  • The last piece is a Procfile which instructs Heroku how to start executables. This one also resides in root of repository and should have something similar to:

    web: cd $HOME/heroku_output/WebAPI && dotnet ./WebAPI.dll –server.urls http://+:$PORT ${CORE_ENVIRONMENT}

Final note

I still decided to stick with buildpack mainly because it just takes more time to build Docker image and push into Heroku registry, than pushing source and building using buildpack.

It worth mentioning that Container Registry and Runtime is still in Beta phase and things may change/improve over time.

Update

Dec 10 2016 : friism who was one the main contributors to original .net build pack, created an a new builpack and submitted PR. Although I didn’t test it yet, it looks much cleaner.

May 2017 : I updated buildpack to support both - csproj and project.json projects, compilation to use relevant SDK versions.

October 2017 The buildback was updated to support .NET Core 2.0