dotnetcore-buildpack, a Heroku buildpack for ASP.NET Core
Heroku .NET Core Buildpack
At a glance
- What is it?
- jincod/dotnetcore-buildpack is the Heroku buildpack for ASP.NET Core, and it works by finding a Startup or Program file in the repository, taking the project file beside it and running dotnet publish on it. It handles C# and F# projects, lets you pick between several project files in one repository, and documents the two things that usually go wrong: Entity Framework migrations and the Heroku Postgres connection string.
- Who is it for?
- Adopt this buildpack if you deploy ASP.NET Core applications to Heroku and want the .NET publish step handled for you. Skip it if you deploy to a platform with its own .NET build support, since this exists specifically to fill that gap on Heroku.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Activity is slowing. The repository last received commits 6 months ago.
- What is it written in?
- Mainly Shell, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
How the buildpack finds your project
This is the Heroku buildpack for ASP.NET Core, and the mechanism it uses is worth understanding because it is the part that surprises people.
The buildpack supports C# and F# projects. It searches through the repository's folders to locate a Startup.* or Program.* file, and if one is found, the .csproj or .fsproj in the containing folder is used in the dotnet publish command.
So the detection is by convention rather than by configuration. A file named Startup.cs or Program.cs is the marker, and the project file that sits in the same folder is what gets published. That handles the common case, where a repository holds one web application, with no configuration at all.
For a repository with multiple web applications, the README points at two environment variables. PROJECT_FILE and PROJECT_NAME allow you to choose the project for publishing. The distinction between them is not spelled out further in the documentation shown here, so which one to set depends on the buildpack's internals, and it is worth reading the source or the Heroku build log on first use.
The buildpack is MIT licensed, written in shell, on the master branch, with the last push on 2026-04-23. The repository has no GitHub releases, and the README points at a releases page for the older tagged versions, which is where the version pinning mechanism lives.
Choosing a version and the four buildpacks:set forms
The usage section gives four ways to set the buildpack, and the difference between them is the version of .NET you get and how much of the source is trusted.
For the latest stable .NET Core you set the registry form:
heroku buildpacks:set jincod/dotnetcoreFor the edge version you point at the repository:
heroku buildpacks:set https://github.com/jincod/dotnetcore-buildpackFor a preview release you use the same URL with a fragment:
heroku buildpacks:set https://github.com/jincod/dotnetcore-buildpack#previewAnd for a previous release you use the version fragment form:
heroku buildpacks:set https://github.com/jincod/dotnetcore-buildpack#versionThe pattern is the standard Heroku buildpack reference syntax, where the fragment after the hash selects a branch, a tag or a named ref inside the repository, and the same pattern applies to the registry form. That is why the version fragment is called version in the README's example, and it is the same mechanism that lets a project pin to a tag.
The practical difference between the registry form and the URL form is who does the hosting and how quickly you get changes. The registry form is a published buildpack the Heroku team maintains, and it is the one that stays stable. The URL form builds from the repository, which means a bug fix in the repository reaches you at the next deploy, and a mistake in the repository reaches you too.
There is a second set of links for the background. The Heroku Buildpack Registry documentation explains the registry mechanism, and the buildpack references page explains the URL and fragment syntax. Those are the two pages to read if you want to understand why a fragment works the way it does rather than just following the four commands.
One point worth noting for a team that pins versions: because the preview and edge forms track the repository rather than a tag, a build that passed yesterday can fail today with no change on your side. Pinning to a specific version fragment is the way to avoid that.
The two things that break: migrations and the connection string
The README spends most of its length on two problems, and both are worth reading before your first deploy rather than after it.
The first is Entity Framework Core migrations. You cannot run migrations with the dotnet ef commands using .NET Local Tools once the app is built, and the README gives two alternatives.
The first is automatic migrations at startup. You create a .NET local tool manifest with dotnet new tool-manifest and install the tool with dotnet tool install dotnet-ef, ensuring the manifest specifies the dotnet-ef tool. Then you ensure the ASPNETCORE_ENVIRONMENT environment variable is set to Production, because ASP.NET Core scaffolding tools may create files that explicitly set it to Development, and Heroku config will override it with heroku config:set ASPNETCORE_ENVIRONMENT=Production. Finally you add a publish target to the .csproj file:
<Target Name="PrePublishTarget" AfterTargets="Publish">
<Exec Command="dotnet ef database update" />
</Target>The subtlety is the ordering. The target runs after Publish, so the migration runs as part of the build rather than at application start, and the environment variable has to be right for the tool to target the production database rather than a local one.
The second alternative is to run the migration scripts manually. The README says to run SQL scripts generated by Entity Framework Core in your app's database, giving PG Admin to connect to the Heroku Postgres service as the example.
The second problem is the connection string. If you are using the Heroku Postgres addon, Heroku provides the database connection string in the DATABASE_URL environment variable, and that is a URL rather than something the Npgsql driver accepts directly. The README shows the conversion in C#, with a regular expression matching the postgres:// form and the pieces assembled into a connection string:
using System.Text.RegularExpressions;
...
// Replace AppContext with your own DbContext Class
builder.Services.AddDbContext<AppContext>(options =>
{
if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Production")
{
var m = Regex.Match(Environment.GetEnvironmentVariable("DATABASE_URL")!, @"postgres://(.*):(.*)@(.*):(.*)/(.*)");
options.UseNpgsql($"Server={m.Groups[3]};Port={m.Groups[4]};User Id={m.Groups[1]};Password={m.Groups[2]};Database={m.Groups[5]};sslmode=Prefer;Trust Server Certificate=true");
}
else // In Development Environment
{
// So, use a local Connection
options.UseNpgsql(builder.Configuration.GetValue<string>("CONNECTION_STRING"));
}
});The important detail is the branch on ASPNETCORE_ENVIRONMENT. In production the connection string is parsed out of DATABASE_URL, and in development it comes from the application's own configuration as CONNECTION_STRING. That means the same code runs locally against a local database and on the platform against the managed one, and it explains why the environment variable being wrong causes failures in both places at once.
Node.js and what this buildpack is really for
The README also covers Node.js and npm, which is worth understanding in context.
Heroku's build system is built around a multi-language model where each application can have a Node.js buildpack and another buildpack, and the order matters. The README's section on Node.js and npm therefore covers the case where your .NET application also has a front end that needs building, and the Node.js buildpack has to run first so that the published output is present when the .NET publish step happens.
That is the real reason a buildpack like this exists at all. Heroku did not have first-class .NET support, so the community built one, and the community one has to coexist with the platform's own Node.js buildpack in the same application. A .NET application with an npm front end is the common case on Heroku precisely because Heroku started as a Node.js platform.
The alternative to using this buildpack is to avoid the platform's build system. A container image built in CI and pushed to a registry, run on a container platform, is the more common choice now, and it removes the whole class of problem this buildpack solves along with the class of problem it creates.
The honest assessment is that this buildpack is a small, focused piece of infrastructure that solves a real problem on a specific platform, and it has been maintained with versioned releases. The two things to get right on a first deploy are the ASPNETCORE_ENVIRONMENT setting and the DATABASE_URL conversion, and both are documented in enough detail to implement directly.
For a team already on Heroku with .NET applications, adopting it is a change of one config var and possibly one csproj target. For a team choosing a platform, it is one more argument for a container platform, where the .NET build is a multi-stage Dockerfile rather than a shell script that has to interoperate with another shell script.
Editorial conclusion
Adopt this buildpack if you deploy ASP.NET Core applications to Heroku and want the .NET publish step handled for you. Skip it if you deploy to a platform with its own .NET build support, since this exists specifically to fill that gap on Heroku. Verify first by pointing the buildpack at a single project and confirming the publish output runs, then check the ASPNETCORE_ENVIRONMENT setting, because the scaffolding default of Development is what breaks Entity Framework migrations on the platform.
Frequently asked questions
How does the Heroku .NET Core buildpack find the project to publish?
It searches the repository's folders for a Startup.* or Program.* file, and uses the .csproj or .fsproj in the containing folder for the dotnet publish command. It supports both C# and F# projects, and for a repository with multiple web applications you set the PROJECT_FILE and PROJECT_NAME environment variables to choose one.
How do I select a version of the dotnetcore buildpack?
With heroku buildpacks:set jincod/dotnetcore for the latest stable release from the registry, or the repository URL for the edge version. Adding a fragment to the URL selects a preview or a specific previous release, for example #preview or #version, using the standard Heroku buildpack reference syntax.
Why do my Entity Framework migrations fail on Heroku?
The dotnet ef commands from .NET Local Tools do not work once the app is built. The README gives two alternatives: configure a publish target that runs dotnet ef database update, or run the generated SQL scripts manually. In either case the ASPNETCORE_ENVIRONMENT variable has to be set to Production, because the ASP.NET Core scaffolding defaults it to Development.
How do I turn the Heroku DATABASE_URL into a connection string?
Heroku provides the connection string as a URL, so it has to be parsed and reassembled for Npgsql. The README shows a regular expression matching the postgres:// form and code that switches on ASPNETCORE_ENVIRONMENT, using the parsed URL in production and the application's own CONNECTION_STRING setting in development.
What licence is the dotnetcore-buildpack released under?
MIT. It is a shell based project on the master branch with the last push on 2026-04-23, and the repository has no GitHub releases, with the older tagged versions listed on the project's releases page and selectable through the buildpack reference fragment.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/jincod-dotnetcore-buildpack)Was this useful?