Practicalities of deploying dockerized ASP.NET Core application to Heroku


.NET is a relative newcomer in the open-source world, and its popularity is nowhere near mainstream platforms like Node.js. So you can imagine there're few tutorials that deal with .NET and frameworks such as ASP.NET on Heroku. And those that do, probably won't use containers.

Image showing heroku menu without C#

Do you see C#/.NET here? Yes, me neither.

Getting started

This tutorial will assume you have Docker, .NET Core and Heroku tools installed. I will use Linux (Ubuntu), but AFAIK those tools are cross-platform so the steps will be the same for any supported OS.

Let's take the easiest case — simple MVC app. If you don't have one, just create it by running

dotnet new mvc --name mymvc

I'll also assume you have a dockerfile ready, maybe something like proposed in this tutorial:

FROM AS builder
WORKDIR /sources
COPY *.csproj .
RUN dotnet restore
COPY . .
RUN dotnet publish --output /app/ --configuration Release
COPY --from=builder /app .
CMD ["dotnet", "MyMvc.dll"]

Note how ENTRYPOINT was substituted with CMD — more on that later.
So, cd to your app's folder and let's begin.

  1. Login to Heroku container registry.

    heroku container:login

  2. If you don't have existing git repo, git init a new one

  3. Run heroku create to create a new app, note the git repo address provided, e.g.

    Creating salty-fortress-4191... done, stack is heroku-16 |

  4. (Optional) Check that you have heroku remote by running git remote -v

  5. Tell Heroku to use containers:

    heroku stack:set container

  6. Create heroku.yml file. Minimalistic version is something like:

    web: Dockerfile

  7. By default ASP.NET core runs on port 5000 and 5001 (https). Heroku won't allow that. If you try running it as-is, Kestrel won't start, throwing an exception:

    System.Net.Sockets.SocketException (13): Permission denied

    Heroku seems to allow you app to listen on port specified in $PORT environment variable. So you need to ensure your app listens on that, rather than default one. In case you're using the default app, just substitute CreateWebHostBuilder with the following one in Program.cs:

        public static IWebHostBuilder CreateWebHostBuilder(string[] args)
            var port = Environment.GetEnvironmentVariable("PORT");
            return WebHost.CreateDefaultBuilder(args)

  8. Commit everything:

    git add . && git commit -m 'Meaningful commit message'

  9. Now push the code to get container built and released (fingers crossed):

    git push heroku master

    Now remember when ENTRYPOINT was substituted with CMD in dockerfile? We don't pass any arguments to container, so ENTRYPOINT ["dotnet", "MyMvc.dll"] and CMD ["dotnet", "MyMvc.dll"] should behave similarly. But if you leave ENTRYPOINT, you'll get an error:
    What a great error — "Unexpected fomation update response status"! Really tells you the root of the problem.
    The real problem is that when using minimalist heroku.yml that I showed above, Heroku will expect CMD instruction in your dockerfile. When you add it, everything should work just fine.


Now you should have some idea how to deploy simple ASP.NET Core apps to Heroku. Is it intuitive? Absolutely not. Is Heroku the best platform to host your .NET apps? Probably not. But as it's easy to sign up there and the most basic plan is free — maybe you might want to host something there, just for fun.


  3. (Dockerfile)

Also popular now: