We’ve recently had a short discussion on the BC Professionals Discord server (please join us at https://discord.gg/4wbfNv3) about releasing your apps for on-premises customers. People asked me why all of my on-premises app releases contain separate app files for all available cumulative updates.

Because I see plenty of ISV’s making the mistake of supplying customers and partners with only one app file per major BC version, I thought I’d write this little piece to explain why that is a bad idea.

Runtime packages

When you generate a runtime package for your app, your BC instance builds a package using the tools inside the instance. The resulting .app file contains a compiled version of your app. This is great because that way, customers can’t access your code (if the app.json manifest is set correctly).

The downside, however, is that the app is not getting compiled by the instance you are installing it in. Meaning that your BC instance has to be compatible with the runtime package.

Crash and burn

When I started out with delivering runtime packages I soon found out that some customers were unable to install my app files. They usually reported incoherent unhandled .NET exceptions while running the publish command. It turned out that these crashes were caused by incompatible runtime packages.


if you generate your runtime package in Business Central 18.7, the resulting package might not install in 18.0. It won’t present you with a clear error message saying that it’s incompatible though. No, it’ll throw a bunch of .NET exceptions in your face. This happens because the runtime package contains your compiled app that was built on a newer version of BC and somehow isn’t compatible with your older instance. This doesn’t happen for every version but every once in a while, Microsoft changes something in the way packages are compiled.

To each their own

To avoid these issues, I started building separate runtime packages for all available Business Central builds.
As you can see in the screenshot below, the file sizes differ from BC 18.2 to 18.3 and upwards. If you try to install the 18.3 .app file in a 18.2 instance, the process will crash.

Separate runtime builds

Automate the process

Dynamics 365 Business Central Sandbox Docker containers and multitenancy –  Stefano Demiliani
We all love docker

Business Central is updated almost every month. That makes it nearly impossible to generate all these runtime packages by hand (unless you love wasting your time on stupid repetitive work that can be automated). But that’s where PowerShell and docker come in handy!

This is the build script I use to build all my apps.
(If the apps have dependencies, I use a slightly different script that publishes the dependencies first)

param (
    [int]$major = $(Read-Host "Enter the BC major version number (14,15,16,17,18,19)"),
    [string]$appName = $(Read-Host "Enter the app name"),
    [string]$version = $(Read-Host "Enter the app version"),
    [string]$appFile = $(Read-Host "Enter the path to the new app file")

$licenseFile = "C:\BusinessCentral\Licenses\bc19_onprem.flf";
$credential = New-Object pscredential 'username', (ConvertTo-SecureString -String 'password' -AsPlainText -Force);

function StopAndRemoveContainer($containerName) {
    docker stop $containerName;
    docker rm $containerName;

function CreateRelease($major, $cu) {
    $artifactUrl = Get-BCArtifactUrl -type OnPrem -version "$major.$cu"
    if ([string]::IsNullOrEmpty($artifactUrl)) {

    $exportFolder = "C:\BusinessCentral\Releases\$appName\$version\BC$major\$major.$cu";
    If (!(Test-Path -Path $exportFolder)) {
        New-Item $exportFolder -itemtype directory | out-null;

        $containerName = "bc$major-cu$cu";
        StopAndRemoveContainer $containerName;
        New-BcContainer -accept_eula `
            -containerName $containerName `
            -artifactUrl $artifactUrl `
            -Credential $credential `
            -auth UserPassword `
            -assignPremiumPlan `
            -accept_outdated `
            -licenseFile $licenseFile `
            -shortcuts None `

        Publish-BcContainerApp -containerName $containerName -appFile $appFile -skipVerification
        $runtimePackagePath = Get-BcContainerAppRuntimePackage -containerName $containerName -appName $appName;
        Sign-BcContainerApp -containerName $containerName -appFile $runtimePackagePath -pfxFile "C:\BusinessCentral\code_sign.pfx" -pfxPassword (ConvertTo-SecureString "MyCertPassword" -AsPlainText -Force)

        Copy-Item -Path ($runtimePackagePath) -Destination "$exportFolder\$appName.BC$major.$cu.RunTime.v$version.app";

        StopAndRemoveContainer $containerName;

$i = 0;
for ($i = 0; $i -le 99; $i++) { 
    CreateRelease $major $i;

Running this script will simply:

  • Look for all possible CU’s (at least up to 100)
  • Create a container for each CU
  • Publish your app against the container
  • Generate a runtime package for the specific version
  • Sign the generated runtime package
  • Copy the result to an output folder
  • Remove the container

No more incompatible packages

Using this build strategy, you will provide your partners with reliable app files that are guaranteed to install in all possible builds of Business Central!

One response

  1. There is also a Convert-BcAppsToRuntimePackages function, although I do not get why that uses Invoke-ScriptInBcContainer instead of the BcContainerHelper functions.
    We are doing more or less what you are doing here.

    You could also replace the StopAndRemoveContainer function with the Remove-BcContainer command. 😊

Leave a Reply

Your email address will not be published.