Skip to Content
Technical Articles
Author's profile photo Robin Reeb

Build and Deploy UI5 Freestyle Applications to SAP BTP Cloud Foundry Environment

last content update was beginning of february 2023.
Please let me know in the comments as soon as something stops working (not expected).


This blogpost intends to show you in a (hopefully) super easy way how to build and deploy proper mta archives in your SAP BTP Cloud Foundry Environment in the context of an SAPUI5 freestyle application. With minor adjustments it will also work fine for any other HTML5 apps as well.

It’ll provide you with a hybrid setup, that allows for 2 different deployment scenarios:

  • Standalone Approuter – maximum flexibility, usage of custom domains etc.
  • Managed Approuter – reduced TCO and resource consumption

Doesn’t matter if you would like to build and deploy a new UI5 freestyle app or change the scenario of an existing one (e.g. from managed to standalone approuter) – I will try to provide you with all the info you need to achieve that.

Disclaimer1: I didn’t build this combined setup completely from scratch.
For the managed approuter scenario, I have used the corresponding BAS template.
For the standalone scenario, I built upon this github repo from Marius Obert

In both scenarios we will use the HTML5 App Repo service to store our html5 application content conveniently.

Disclaimer2: This blogpost is not a step-by-step developer tutorial for complete beginners but rather a guided template for more experienced developers that know how to work with npm, ui5 and cloud foundry. Please head over to the SAP Dev Tutorials, if you are not yet familiar with UI5 and/or the BTP CF Environment.


  • Access to a SAP BTP subaccount
  • Cloud foundry environment provisioned
  • Launchpad, Workzone or comparable subscription with managed approuter functionality
  • Quota for the HTML5 Application Repository Service
  • Developer-Access to a CF Space
  • SAP Business Application Studio Subscription or comparable local IDE like VSCode
  • CF CLI & Node available locally on your machine or in BAS dev space

TL;DR // Github Source Code Access

If you are in a hurry and would like to browse the final code for “inspirational purposes” :), you can head over to my repo directly: 


Formulating the mta development descriptor(s)

Location of mta.yaml files

The first thing you will probably notice is, that normally the mta.yaml development descriptor is located in the root folder of the project. Of course, we could have just used one mta.yaml file to describe both scenarios, but the aim of this project is to have maximum flexibility.

With 2 separate development descriptors, you can either deploy the built archives side by side in the same space, deploy them in different spaces or even deploy only one of them.

If your project is only going for 1 scenario, 1 mta file in the root folder will be sufficient, though.

In our case the development descriptors are within the folders _build_managed & _build_standalone.
The single dependency to the cloud mta build tool (mbt) is still only within the package.json within the root folder.

Content of the standalone mta.yaml file

Entity Name Description
approuter ui5-standalone-approuter
  • cloudfoundry application which is exposed on a specific route (url)
  • connects to html5 app repo, destination, connectivity and uaa services
html5 deployer html5-deployer
  • uploads the ui5app modules zipped ui5 app to the html5 app repo host
html5 content ui5app
  • references the html5 module folder aka our ui5 application
  • builds and zips it
html5 app repo host ui5-shared-repo-host
  • used for storing and accessing the html5 application content
html5 app repo runtime ui5-standalone-html5repo-runtime
  • used for communication between approuter and html5 app repo host
destination ui5-standalone-destination
  • used for defining destinations to connect to external data services
connectivity ui5-standalone-connectivity
  • used for on-premise type destinations
uaa ui5-standalone-uaa
  • references the xs-security file, which defines the scopes and role templates

Content of the managed mta.yaml file

Entity Name Description
managed approuter config ui5-managed-dest-content
  • defines destination to repo host and uaa instances in destination service, that managed approuter can leverage by using inline-generated service keys
  • values need to match the value in the manifest.json of your html5module
  • destination level per default is set to instance, to use the destination service instance defined under resources, can however be also changed to subaccount.
    This would result in the destinations being created on subaccount level – not recommended because the authorizations may differ for users for subaccount and space level.
html5 deployer ui5-managed-app-content
  • uploads the ui5app modules zipped ui5 app to the html5 app repo host
html5 content ui5app
  • references the html5 module folder aka our ui5 application
  • builds and zips it
html5 app repo host ui5-shared-repo-host
  • used for storing and accessing the html5 application content
destination ui5-managed-dest-srv
  • used for defining destinations to connect to external data services
  • connectivity service is not defined separately, apparently this functionality is part of the managed approuter
uaa ui5-managed-uaa
  • references the xs-security file, which defines the scopes and role templates

Building and Deploying the mta archives

For a condensed version of this section, you can also just read the README of the repo.

Building the mta archives

Run the mbt build command with either of the mta.yaml files either directly or via the npm scripts in the root package.json

  • mbt build -p=cf -s=./_build_standalone -t=../mta_archives
  • mbt build -p=cf -s=./_build_managed -t=../mta_archives

In both cases the build:cf script of the html5module folder will be executed implicitly to generate the ui5 build and zip it under /resources/ and include it as a requirement with the generated mta archives.

more info on the build tool:

Deploying the mta archives

Make sure to first have cf cli with installed multiapps plugin at your hands (either in your local ide terminal or within BAS in your dev space)

  • cf login (in your preferred btp cf space)
  • cf deploy ./mta_archives/ui5-standalone_1.0.0.mtar -f
  • cf deploy ./mta_archives/ui5-managed_1.0.0.mtar -f

in case of undeploy, you can undeploy the mta projects completely using:

  • cf undeploy ui5-standalone –delete-services –delete-service-keys
  • cf undeploy ui5-managed –delete-services –delete-service-keys

These commands are also available as convenient scripts within the root package.json file.


Steps after Deployment

Verify successful deployment

make sure that the terminal finished all deployment tasks successfully, then head over to your cf space in the BTP cockpit and verify, that all service instances and applications have been created and configured correctly.


Also make sure that the html5 module content has been uploaded successfully in the html5 app repo host service instance by using the commands after installing the html5 apps repo cli plugin. (Your correctly configured BAS dev space comes with this installed out of the box)

  • cf html5-list & cf html5-info

more info:
or directly in the readme to find out about additional cli options such as “-u” parameter to display URLs:

Authorize yourself and potential users

While not technically required, I have added scope protection for the paths to access the app content from html5 app repo as well as the used northwind data source via the xs-app files within the html5module folder (managed scenario) and the approuter folder (standalone scenario).

I’d like to raise awareness, that if you aren’t doing this, all users that authenticate against your used idp will also be directly able to access your app, if they happen to know the url.

However, when applying the scope checks in the xs-app files, we have to map the scopes to a role template in the xs-security files (located next to the mta dev descriptors).

After successful deployment, the role templates will then be created on your btp subaccount via the xusaa service instances.

Now, you have to create a role collection and add the user role templates in there.

Afterwards you can assign the role collection manually (as a subaccount admin) or via your provisioning tools to you and your other endusers.

Execute the apps

If your user was provisioned with the correct role collection, you can now run the app in the cockpit either via

  • the custom route of the cf application in your space (standalone)
  • the html5 applications tab on the left (managed – requires a a managed approuter e.g. from launchpad service)

In both scenarios the UI5 application content will be retrieved from the shared HTML5 app repo host service instance and the requests to the northwind odata service will be channelled through the respective destination service instance.


Further Read

Configure Fiorilaunchpad/SAP Build Workzone Integration

While it is not really a priority in this blogpost, I understand that many of you also would like to embed the application in a Fiorilaunchpad site of your respective Fiorilaunchpad subscription.

For this, I have added the “crossnavigation” property within the “” of the UI5 app manifest.json.

I won’t go into detail, but here are the required steps (assuming you have setup Fiorilaunchpad subscription and roles already):

  • Start the Fiorilaunchpad Site Manager via the provided link within your subaccount subscriptions
  • Make sure to have at least 1 site created
  • Navigate to the Channel Manager on the left and refresh HTML5 Apps Content Channel
  • Navigate to Content Manager > Content Explorer and add the app with id “ui5app”
  • Navigate to Content Manager > My Content and make sure to create at least 1 Role and 1 Group
  • Assign the app to the role and group and make sure the role is including your user
  • Open your site and the app should be visible underneath the corresponding group



Destination service vs Connectivity Service

In the standalone scenario, additionally a connectivity service instance is required if you want to connect against on-premise systems for example via principal propagation. (I suspect this functionality to be included out of the box within the managed approuter)

In this example, we are only connecting against the public non-protected northwind service, meaning it would not be necessary. I however kept it in for the sake of completeness/templating reasons, because if you are not aware of it, weird internal server errors will appear upon connecting against on-premise systems which takes a while to figure out.

More info on this, here:


Shared HTML5 App Repo Host Service Instance

Initially, I also had separate App Repo Host service instances.

However, when you are trying to push HTML5 app content with the same id in 2 different HTML5 App Repo Host service instances within the same space, you’d get the following error during deployment.

[html5-deployer] [ERROR] Upload application content failed { CODE: ‘1001’ } validation error: Application metadata for application ui5app already exists in service instance 26749b39-5a40-4700-8748-af77576b39a2

To support the scenario of deploying both scenarios in the same space, I now refer only to one service instance by using the same id within both mta development descriptors.


Merging other service instances

I could have of course also merged the xsuaa and destination service instances into shared ones across both scenarios.

However, the idea of this whole exercise is not necessarily the side-by-side deployment of both scenarios in the same space, but rather initial templates that you can pick based on your option of choice for your own apps


UI5 Code

This project is not intended to be used as a template for clean ui5 code. Even though I tried to simplify the used ui5 freestyle app as much as possible for educational reasons, there might be many flaws with it.

Usage of v2 OData service, not providing any unit/integration tests and in general outdated code (the app is based on an internal project from 2019) just to name a few.

The main purpose is to demonstrate how the app makes use of the destination service during deployment and the Fiori tools proxy during design time in BAS to connect against external services by mapping the paths respectively in the manifest.json file.

The ui5 app content is almost completely decoupled from the mta development descriptors and other “outside”-content. Only keep in mind to refer to the correct /resources/ filename as the content for the html5 module and the correct id from the UI5 manifest.json within the pathmapping of the xs-app.json of the approuter. This id will also be the id of the app content that is pushed into the html5 app repo host service instance.


Testing against Backends/Services within BAS

It is possible to test your ui5 freestyle app against actual data from backends during design time within your IDE.

For this, navigate to the html5module folder and execute npm run start normally.The ui5.yaml descriptor file is then used to proxy the requests via pathmapping (substitutes the xs-app files)

You can also use the “destination” property of a backend entry to connect live to a global destination of the btp subaccount where your BAS subscription resides in. This way also for example a connection to a backend SAP system via principal propagation authentication and a cloud connector that is connected to your btp subaccount will be possible.

see more info on this here:


Other UI Frameworks

Does the github sample we have composed for you also works for frameworks other than SAPUI5 you might ask. Certainly it does!

The corresponding Cloudfoundry buildpack, the HTML5 Application Repository Service, the MTA build tool and even the Fiorilaunchpad subscription all work fine with non-ui5 HTML5 apps.

Just be sure to configure the manifest.json of the HTML5 module accordingly and adapt the build command that generates the .zip file which is referenced in the mta files.



It’s hopefully not necessary to tell you that a direct deployment set-up via cf cli from your local device or even BAS is more than suboptimal. Please consider creating a build and deployment pipeline to ensure continuous delivery and also to allow for important additional build steps such as codescans and unit test execution.

Potential options are SAPs own CI/CD Service or third party solutions such as „Jenkins as a Service“ or „Github Actions“. I’d personally go for Github Actions, since it integrates the best with your github repository and removes the need to add additional interfaces in the picture.


Kyma Environment

So far this project only comprises deployment to the SAP BTP, Cloud Foundry Environment. I am currently looking into Kyma as well, but for the time being, you might want to have a look at:



Now, you should be able to understand the differences between deploying UI5 freestyle applications with the managed and standalone approuter concepts.

Currently I am not aware of any major problems in this project, which would require a fix, but I haven’t collaborated with anyone else here and 4 or in this case hopefully hundreds of eyes see better than 2, right? 🙂

Therefore, please let me know your feedback, potential improvements and questions in the comments and I’ll make sure to follow-up with you on them!

If you enjoyed the content, feel free to leave a like or follow my profile for similar content coming up in the future – Cheers!

Assigned Tags

      You must be Logged on to comment or reply to a post.
      Author's profile photo Jose Antonio Aguilar Sanchez
      Jose Antonio Aguilar Sanchez

      Hi Robin, very useful and intersting information. Thanks

      Author's profile photo Robin Reeb
      Robin Reeb
      Blog Post Author


      • Added information on Fiorilaunchpad integration
      • Added notes about the usage of this example with other HTML5 frameworks (non-ui5)
      • Added short remarks and links regarding the SAP BTP, Kyma Environment
      Author's profile photo Pieter Janssens
      Pieter Janssens

      Great blog and I like the concise examples in the repo! To make it even better, I would suggest adding info on:

      • UI5 app cache buster support (difference between standalone vs managed)
      • Advantages of standalone+html5 repo over standalone approuter serving static content directly
        • Since in both cases there is an approuter consuming resources
      • Warn about the fact that deploying any content to the html5 repo host will replace all previous content !!
        • So for practical reasons, advice against sharing repo hosts between apps
        • And maybe rename 'ui5-shared-repo-host' 🙂
      • Background info on the impacts and options to chose an identifier for
        • ==
          • looks strange in the URL used by the managed approuter as it will expose the app via access a route containing '<>-<>'
        • Company name/namespace as <-- personal preference
        • Other?
      • Stress the importance that the value used in the MTA, must match the value in the app's manifest.json.
      • Show how one can easily retrieve the app URL for the apps hosted in the repo's
        • cf html5-list -d -u
      Author's profile photo Robin Reeb
      Robin Reeb
      Blog Post Author

      Hi Pieter

      Thanks a lot - valid points. Especially the cachebuster one, is always a topic that people incl. myself tend to forget about - good catch, I will try to add maybe a complete section in the "further read"! 🙂

      Regarding your other points:

      • I am not even showing the option of serving static content directly since I do not see any downsides of always using the HTML5 app repo for business apps in SAP technology context.
      • I was only using shared repo host, since I don't want to use unnecessary resources - in my case it is the same single ui5 app in html5 app repo, but consumed from 2 different approuter scenarios. Of course in reality you would always rather choose only 1 scenario per app and also a dedicated repo host service instance per app - correct.
      • I do not really have a recommendation regarding naming conventions and I am also not aware of any published guidelines. This question is all I found on the topic:
      • I added the info that the manifest.json needs to match the ids in the "managed app router config"-module-description
      • I added the GH Readme for the cf html5 apps repo cli plugin with a reference to your mentioned -u option for the cli command:

      Thanks again for your input, making this blogpost even more complete!