I've just solved this problem thanks to Chis' answer and this blogpost.
It turns out that Heroku really wants to take care of the ports for you (probably for security reasons).
With boilerplate asp core template, with docker suport added you start with:
Program.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace TestWebApp
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
Dockerfile:
#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
WORKDIR /src
COPY ["TestWebApp/TestWebApp.csproj", "TestWebApp/"]
RUN dotnet restore "TestWebApp/TestWebApp.csproj"
COPY . .
WORKDIR "/src/TestWebApp"
RUN dotnet build "TestWebApp.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "TestWebApp.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "TestWebApp.dll"]
Heroku expects your app to run on the port Heroku gives you in PORT
environment variable. It'll expose 80 (HTTP) and 443 (HTTPS) ports for you. So what you have to do is:
Remove these lines form your Dockerfile
:
EXPOSE 80
EXPOSE 443
Then you have to make your application listen on that port. To make it work, you have to change your Program.cs
file:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
var port = Environment.GetEnvironmentVariable("PORT");
webBuilder.UseStartup<Startup>()
.UseUrls("http://*:" + port);
});
Then Heroku does it's magic and your app still gets HTTPS support.
Hint:
The default Dockerfile won't work out of the box with Heroku CLI. The generated Dockerfile is supposed to be ran from the solution level (not project level). I was unable to force heroku CLI to use a Dockerfile form a nested direcotry. The way I made id work was:
- Go to your solution directory
- Create Dockerfile
- Paste modified content:
#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
WORKDIR /src
COPY ["TestWebApp/TestWebApp.csproj", "TestWebApp/"]
RUN dotnet restore "TestWebApp/TestWebApp.csproj"
COPY ./TestWebApp ./TestWebApp
WORKDIR "/src/TestWebApp"
RUN dotnet build "TestWebApp.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "TestWebApp.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "TestWebApp.dll"]
heroku login
heroku container:login
heroku container:push web --app your-app & heroku container:release web --app your-app