Docs

Documentation versions (currently viewingVaadin 25 (prerelease))

Deploying a Vaadin Application with Control Center

Learn how to package and deploy a Vaadin application using the Control Center App resource.

Control Center manages the deployment of Vaadin applications through a custom Kubernetes resource called App. When an App manifest is applied, Control Center automatically provisions all necessary components: deployments, services, ingress routing, configuration, certificate requests, and more.

To learn more about how Control Center manages applications internally, refer to the Architecture section.

This guide covers the steps to prepare a Vaadin application, package it as a container image, and deploy it to a cluster using Control Center.

Add the Control Center Dependency

To enable Control Center integration (for example, metadata reporting and health endpoints), add the control-center-starter dependency to your project.

Source code
build.gradle.kts
dependencies {
    implementation("com.vaadin:control-center-starter")
}
build.gradle.kts
pom.xml
pom.xml
Tip
When using the Vaadin Bill Of Materials (BOM), the version does not need to be specified explicitly.

Build the Application Image

Spring Boot supports building container images using Cloud Native Buildpacks, which is the recommended approach for Vaadin applications.

Buildpacks offer several benefits:

  • Simplified image creation — no need to write or maintain a Dockerfile

  • Optimized image layering — dependencies, classes, and static resources are separated to improve caching and rebuild performance

  • Secure defaults — minimal base images, non-root execution, and JVM tuning are applied automatically

The resulting image is production-ready and follows best practices out-of-the-box.

Source code
Terminal
./gradlew bootBuildImage -Pvaadin.productionMode=true --imageName=company/my-app:1.0.0
Terminal
Terminal
Terminal

This produces a Docker image tagged as company/my-app:1.0.0.

For more information, see related Spring documentation for Gradle and Maven.

Push to a Container Registry

For remote Kubernetes clusters, the image must be pushed to a registry:

Source code
Terminal
docker push company/my-app:1.0.0
Note
If not pushed, the image is only available on the local system where it was built.

Local Cluster Support

For local development clusters, load the image into the runtime:

Source code
Terminal
minikube image load company/my-app:1.0.0
Terminal
Terminal
Terminal
Terminal
Terminal

Create and Apply the App Resource

Create a Kubernetes manifest using the App custom resource. This manifest instructs Control Center to deploy the application and manage its configuration.

Source code
my-app.yaml
apiVersion: vaadin.com/v1alpha1
kind: App
metadata:
  name: my-app
  namespace: vaadin
spec:
  host: demo.example.com
  image: company/my-app:1.0.0
  version: "1.0.0"
  replicas: 3

Field Overview

  • metadata.name: Logical name of the application inside the cluster.

  • metadata.namespace: Namespace where the application should be deployed (optional).

  • spec.host: Public hostname where the application will be reachable.

  • spec.image: Fully qualified container image reference.

  • spec.version: Application version (used for tracking and visibility).

  • spec.replicas: Number of instances (default: 1).

Add Environment Variables

Environment variables can be added to the application using the spec.env field. These variables are injected into the container and become available to the Vaadin application at runtime—either through the standard Java System.getenv() API or automatically mapped by Spring Boot into configuration properties.

This mechanism is useful for setting configuration values such as logging levels, Spring profiles, external API keys, or feature toggles.

Source code
my-app.yaml
spec:
  env:
    - name: LOGGING_LEVEL_MY_PACKAGE
      value: debug
    - name: API_KEY
      valueFrom:
        secretKeyRef:
          name: my-secret
          key: api-key
Important
Changes to environment variables trigger an application redeployment.

Configure Resource Limits

To control the amount of CPU and memory allocated to the application pods, use the spec.resources field. This helps ensure fair scheduling and cluster stability.

Source code
my-app.yaml
spec:
  resources:
    requests:
      cpu: "500m"
      memory: "256Mi"
    limits:
      cpu: "1000m"
      memory: "512Mi"
  • requests define the minimum resources the pod is guaranteed to receive.

  • limits define the maximum resources the pod can consume.

Setting both helps the Kubernetes scheduler place the pod efficiently and enforce upper bounds during runtime.

Provision a Database

Control Center can automatically provision a database for a deployed application on the managed PostgreSQL cluster that is installed with Control Center. The database is automatically created, and credentials are injected into the application environment.

To provision a database, set the spec.postgres.database field in the App manifest with the name of a database.

Example App manifest with database provisioning enabled:

Source code
my-app.yaml
apiVersion: vaadin.com/v1alpha1
kind: App
metadata:
  name: my-app
  namespace: vaadin
spec:
  host: demo.example.com
  image: company/my-app:1.0.0
  version: "1.0.0"
  postgres:
    database: my-database

When the application starts, Control Center injects the database configuration into the environment, and Spring Boot automatically configures a DataSource bean.

This DataSource can be used directly in the application, for example via JdbcTemplate.

Source code
Inject and use the database connection
public class CustomerView extends VerticalLayout {

    public CustomerView(DataSource dataSource) {
        var customerList = new UnorderedList();
        var jdbc = new JdbcTemplate(dataSource);
        jdbc.queryForList("SELECT name FROM customers", String.class).forEach(name -> {
            customerList.add(new ListItem(name));
        });
        add(new H1("Customer List"));
        add(customerList);
    }
}

Database configuration is handled entirely through environment variables, allowing standard Spring Boot features such as Flyway migrations or JPA/Hibernate integration to work without additional configuration.

Enable User Authentication

Control Center includes a preconfigured instance of Keycloak to provide secure user authentication for deployed applications. This integration supports advanced identity features including multi-factor authentication (MFA) and passwordless login.

Configure Spring Security

Add the following Spring Security configuration to the application using the ControlCenterSecurityConfigurer provided by the control-center-starter dependency:

Source code
Java
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http
            .with(ControlCenterSecurityConfigurer.controlCenter(), Customizer.withDefaults())
            .build();
    }
}

This configuration enables authentication through the Keycloak instance managed by Control Center.

To define who can access to the application views, refer to Annotating View Classes.

Note
The configurer can be added to an existing security configuration.

Reference a Keycloak Realm in the App Manifest

To activate authentication, the application must declare the Keycloak realm to use in the App manifest. This is done by adding the keycloak.realm property:

Source code
my-app.yaml
spec:
  keycloak:
    realm: my-realm

A realm in Keycloak represents an isolated identity space. Each realm contains its own users, credentials, roles, and authentication settings.

To list available realms in the cluster:

Source code
Terminal
kubectl get realms -n vaadin

Example output:

Source code
NAME             AGE   MESSAGE               STATE
control-center   30s   Realm is up-to-date   APPLIED
my-realm         30s   Realm is up-to-date   APPLIED

A default realm named control-center is created automatically during Control Center installation. New realms can also be created and managed; for details, refer to the Identity Management documentation.

Once configured, users must authenticate through the specified realm before accessing the application.

Apply the Manifest

Once the App manifest is written and saved, it can be applied to the Kubernetes cluster using the kubectl apply command.

Source code
Terminal
kubectl apply -f my-app.yaml

This instructs Kubernetes to create the custom resource in the specified namespace. Control Center continuously watches for changes to App resources and automatically begin provisioning the application based on the declared specification.

When the manifest is applied, Control Center performs a series of automated operations:

  1. A Kubernetes Deployment is created using the specified image, configured with sane defaults for Vaadin applications, including health probes, memory and CPU limits, and internal management ports.

  2. A Service and Ingress are provisioned to expose the application on the specified host, using HTTPS.

  3. A TLS certificate is requested from the configured issuer (such as Let’s Encrypt) for the host.

  4. If DNS management is enabled, a corresponding DNS record is created automatically and kept in sync.

  5. Runtime configuration is injected via config maps and secrets, including identity provider integration and optional database connections.

This flow ensures that the application is fully integrated into the cluster with minimal configuration, using secure and production-ready defaults.

To verify the deployment status, run:

Source code
Terminal
kubectl get apps -A

Example output:

Source code
NAMESPACE   NAME      AGE   PHASE     READY   URI                                VERSION
vaadin      my-app    30s   RUNNING   3/3     https://demo.example.com           1.0.0

Explanation of the columns:

  • PHASE: Current lifecycle state (PENDING, RUNNING, FAILED)

  • READY: Indicates how many replicas are running and ready

  • URI: Public HTTPS URL where the application is exposed

  • VERSION: Application version as declared in spec.version

If the PHASE remains in PENDING, inspect the related events, pod status, and logs to determine whether an image pull, DNS, or certificate issue is delaying the rollout.

Once the status is RUNNING and the READY column matches the desired replicas, the application is live.

To access it, point your browser to:

Source code
https://demo.example.com

This completes the deployment process. The application is now fully integrated into the cluster, secured with HTTPS, and managed by Control Center.