Documentation versions (currently viewingVaadin 24)

Deploy a Vaadin Flow Application on Azure

Describes how to prepare a Vaadin application for production, including deploying on Azure.

In this final part of this tutorial, you’ll learn how to deploy a Spring Boot application on Azure. You’ll use Azure Container Apps, which is a simple way to deploy applications on Azure. For a larger scale deployment and the best possible end-user experience, consider using Azure Kubernetes Service together with Vaadin Kubernetes Kit.

This part covers:

  • Vaadin production builds;

  • Packaging Vaadin applications as a Docker image;

  • Deploying a Docker packaged web application using Azure Container Apps; and

  • Tips for production deployment.

Deploy Vaadin on Any Cloud Provider
From a cloud provider’s point of view, a Vaadin application is a standard Java web application. You can deploy your application onto almost any cloud platform, in many different ways. Read the Cloud Deployment tutorials for more options.

Prepare for Production

It’s important to build a separate production-optimized version of the application before deploying it. In development mode, Vaadin has a live-reload widget, debug logging, and uses a quick but unoptimized frontend build that includes source maps for easy debugging that’s maintained using npm and Vite. Unoptimized frontend bundles can contain several megabytes of JavaScript and dependencies that aren’t needed during production deployment.

The pom.xml file includes a production profile configuration that prepares an optimized build which is ready for production. Enter the following at the command-line in the project directory to build a production-ready JAR file:

mvn install -Pproduction

Deployment Using Azure Container Apps

To use Azure Container Apps, you’ll need to do the following:

  • Install and run Docker (e.g., using Docker Desktop).

  • Install Azure CLI or make sure you have it up-to-date with az upgrade.

  • Log into Azure using a browser and make sure you have an active Azure subscription. Use Start Free Trial, if you don’t have an existing one.

From the commmand-line, login using Azure CLI like so:

az login
If you’re not located in North America, you’ll have a better experience with Azure by choosing a nearby region. For example, a Europe based developer can do this with az config set defaults.location=westeurope or pick the location per application. Pick a location that already supports Container Apps.

Docker image is a basic building block used by many modern hosting solutions to run applications. The example project contains a simple ready-made Dockerfile that essentially describes how the JAR file built in the previous step should be run.

Azure Container Apps contains a handy up command, that does much of convention-based setups for Docker-based deployments. With a single command, Azure tooling builds a Docker image, pushes it to a custom project specific Docker registry and creates a single node deployment based on it.

az containerapp up -n my-crm-app --source .

The my-crm-app should be changed to the name for your application in Azure. The last . is relevant in the command as it asks Azure to pick the sources from the current directory. The first time you use Azure Container Apps, the CLI asks you to install some new components.

The first deployment can take several minutes, depending on the speed of your computer and network. Once the deployment is finished, you should see the URL to your newly deployed Vaadin application at the end of the command output.

Avoid Data Loss

This application uses in-memory H2 database by default, which is useful for development. The database is re-created on each deployment and is embedded for each node. For actual usage, you should switch to your preferred database — at least in the production profile — to use ddl-auto=none and start to use a database migration tool like Liquibase or Flyway, so you can evolve the database schema without losing data. Check Spring and Azure documentation for more details.

Refer to the Azure Container Apps documentation for more details how to configure your deployment. The horizontal scaling doesn’t work yet with Vaadin applications — session affinity for Container Apps ingress is currently in development. However, you can configure the node to be a larger one to scale up your application. For large scale deployments, you should see Kubernetes based clustering solutions.

Tutorial Conclusion & Next Steps

If you had any problems or were confused by any part of this tutorial, you can contact @vaadin on Twitter or join Vaadin’s Discord chat.

If all went correctly, though, you built a full-stack web application in pure Java and deployed it to Azure. You can use it to experiment further or as a foundation for your next project.

Helpful Resources

migration assistance

Download free e-book.
The complete guide is also available in an easy-to-follow PDF format.

Open in a
new tab
export class RenderBanner extends HTMLElement {
  connectedCallback() {

  renderBanner() {
    let bannerWrapper = document.getElementById('tocBanner');

    if (bannerWrapper) {

    let tocEl = document.getElementById('toc');

    // Add an empty ToC div in case page doesn't have one.
    if (!tocEl) {
      const pageTitle = document.querySelector(
        'main > article > header[class^=PageHeader-module--pageHeader]'
      tocEl = document.createElement('div');

      pageTitle?.insertAdjacentElement('afterend', tocEl);

    // Prepare banner container
    bannerWrapper = document.createElement('div'); = 'tocBanner';

    // Banner elements
    const text = document.querySelector('.toc-banner-source-text')?.innerHTML;
    const link = document.querySelector('.toc-banner-source-link')?.textContent;

    const bannerHtml = `<div class='toc-banner'>
          <a href='${link}'>
            <div class="toc-banner--img"></div>
            <div class='toc-banner--content'>${text}</div>

    bannerWrapper.innerHTML = bannerHtml;

    // Add banner image
    const imgSource = document.querySelector('.toc-banner-source .image');
    const imgTarget = bannerWrapper.querySelector('.toc-banner--img');

    if (imgSource && imgTarget) {