---
title: Spring Boot on SKE with PostgreSQL Flex and Gateway API
description: "Design the Spring Boot Replatform target with SKE, PostgreSQL Flex, Gateway API, DNS, and Observability, separating the tested baseline from future extensions."
scfAsset:
  managed: false
  category: 'blueprint'
  external: false
  tags: ["design-and-mobilize", "design", "target-architecture", "replatform", "kubernetes", "postgresql", "object-storage", "spring-boot"]
  maintainers:
    - user: "lukas.weberruss"
      role: true
      website: true
source_url: "https://framework.stackit.cloud/migration/assetcontainer/stackit/architecture-spring-boot-kubernetes-paas-data-object-storage/"
source_file: "docs/migration/assetcontainer/stackit/architecture-spring-boot-kubernetes-paas-data-object-storage.mdx"
---

## Overview

This architecture maps the VM-based Spring Boot and PostgreSQL source to a Kubernetes runtime
and managed database on STACKIT. The same application JAR is retained while provisioning,
deployment, traffic management, data recovery, and operational responsibilities change.

The reference baseline uses one SKE worker and PostgreSQL Flex, with Envoy Gateway, STACKIT DNS,
and Observability. It does not deploy the additional services or multi-zone topology shown in
the optional extension pattern below.

## Typical use case

- **Runtime standardization**: replace a systemd-managed Java process with a reproducible Deployment and health checks.
- **Database operations**: move PostgreSQL to a managed service without redesigning the application schema.
- **Controlled platform change**: qualify rollout, scaling, network access, and recovery independently before production acceptance.

## Architecture diagram

```d2
direction: right

Source: "Source VM" {
  App: "Spring Music JAR + systemd"
  DB: "Self-managed PostgreSQL"
  App -> DB: "local SQL"
}
Evidence: "Approved dump + manifest"
Users: "Application clients"
Target: "STACKIT Application Project" {
  DNS: "STACKIT DNS"
  SKE: "SKE: single-worker reference" {
    Gateway: "Envoy Gateway + HTTPRoutes"
    Service: "ClusterIP Service"
    App: "Same JAR on Java 11"
    Metrics: "Boot 2 adapter + PG exporter"
    Client: "Temporary migration client"
    ExternalDNS: "Managed ExternalDNS"
    Gateway -> Service -> App
    ExternalDNS -> Gateway: "watch route hostnames" {style.stroke-dash: 3}
  }
  Flex: "PostgreSQL Flex" {
    AppDB: "springmusic"
    Rehearsal: "springmusic_rehearsal"
  }
  Obs: "Observability + Grafana"
  SKE.App -> Flex.AppDB: "JDBC / TLS"
  SKE.Client -> Flex.Rehearsal: "rehearse / prove backup"
  SKE.Client -> Flex.AppDB: "approved cutover / rollback"
  SKE.Metrics -> Flex.AppDB: "database metrics / TLS"
  SKE.ExternalDNS -> DNS: "publish Gateway address"
  Obs -> SKE.Metrics: "scrape via Gateway 9090 / 9187"
}
Source.DB -> Evidence: "freeze / export / verify"
Evidence -> Target.SKE.Client: "protected transfer via kubectl"
Users -> Target.DNS: "resolve hostname"
Users -> Target.SKE.Gateway: "HTTP baseline; HTTPS optional"
```

## Runtime and data boundaries

An init container verifies the commit-pinned JAR checksum before Java starts. Application
containers are replaceable: authoritative album data lives in PostgreSQL Flex, not in a pod
filesystem or Kubernetes PersistentVolume. Kubernetes Secrets inject database credentials;
an external Secret Manager integration is not implemented in this baseline.

The Flex ACL defaults to actual SKE egress CIDRs. Both application and migration client require
encrypted database connections. The migration client uses an isolated rehearsal database and
only replaces the application data after explicit approval and a verified pre-cutover backup.
No source-VM database connection or temporary public Flex ACL is required for the dump-based path.

## Traffic and observability

Terraform installs Envoy Gateway and then a local routing chart. The application Service is
ClusterIP; Envoy supplies the public LoadBalancer. SKE-managed ExternalDNS publishes the
HTTPRoute hostname from the Gateway address. This is Gateway API, not a legacy Ingress
controller or a separately provisioned STACKIT Application Load Balancer service.

HTTP is the tested default. For HTTPS, supply a trusted TLS Secret and configure
`gateway_tls_secret_name` according to the repository procedure; certificate issuance and
renewal remain external responsibilities. The separate metrics listeners are public and
unauthenticated in the reference and require protection before sensitive use.

Boot 2 Actuator binds to pod-local loopback; the metrics adapter exposes selected measurements.
The PostgreSQL exporter and the SKE monitoring integration feed Observability. Terraform
creates the Grafana folder and dashboard, but dashboard availability alone does not establish
application health, scrape continuity, or working alert delivery.

## Availability and recovery decisions

The tested worker count, HTTP endpoint, and sample application are a functional baseline,
not an HA production architecture. Select a supported SKE release and suitable zone capacity.
Assess multiple workers, zone distribution, workload disruption budgets, replica safety, database
availability, and the traffic layer as separate design decisions with failure tests.

Database rollback restores the pre-cutover target, while Flex managed backups serve service
recovery. Neither automatically redirects users to the source VM. Define write ownership,
traffic-switch authority, rollback deadline, retention, and recovery objectives before migration.

## Optional extension pattern

The following broader design illustrates possible additions, not resources created by the
reference Terraform. Additional node pools, topology rules, persistent volumes, RabbitMQ,
Object Storage, and Secret Manager need their own implementation, ownership, and validation.
Use them only for a demonstrated workload requirement; do not infer HA from this diagram.

```d2
vars: {
  d2-config: {
    pad: 32
  }
}

style.font-size: 22

direction: down
grid-columns: 1

Internet: "Internet" {
  icon: ../../../../../../public/stackit-icons/networking/ip.svg
  link: https://docs.stackit.cloud/products/network/core-networking/
}

SKEProject: "Application Project" {
  direction: down
  grid-columns: 1

  Access: "Access" {
    direction: right
    grid-columns: 2

    ExternalLB: "External LB" {
      icon: ../../../../../../public/stackit-icons/networking/application-load-balancer.svg
      link: https://docs.stackit.cloud/products/network/load-balancing-and-content-delivery/application-load-balancer/
    }

    DNS: "DNS" {
      icon: ../../../../../../public/stackit-icons/networking/dns.svg
      link: https://docs.stackit.cloud/products/network/core-networking/dns/
    }
  }

  Kubernetes: "Kubernetes (SKE)" {
    link: https://docs.stackit.cloud/products/runtime/kubernetes-engine/
    icon: ../../../../../../public/stackit-icons/runtime/kubernetes.svg
    direction: down
    grid-columns: 1

    EntryLayer: "Entry Layer" {
      direction: right
      grid-columns: 2

      Ingress: "Gateway API" {
        icon: ../../../../../../public/stackit-icons/networking/application-load-balancer.svg
        link: https://docs.stackit.cloud/products/runtime/kubernetes-engine/
      }

      ExternalDNS: "ExternalDNS" {
        icon: ../../../../../../public/stackit-icons/networking/dns.svg
        link: https://docs.stackit.cloud/products/runtime/kubernetes-engine/
      }
    }

    ServiceLayer: "Service Layer" {
      direction: right

      K8sService: "K8s Service" {
        icon: ../../../../../../public/stackit-icons/networking/network.svg
        link: https://docs.stackit.cloud/products/runtime/kubernetes-engine/
      }
    }

    WorkloadLayer: "Workload Layer" {
      WorkloadRow: "" {
        direction: right

        Deployment: "Deployment" {
          link: https://docs.stackit.cloud/products/runtime/kubernetes-engine/
          grid-columns: 2

          PodA: "Pod A" {
            icon: ../../../../../../public/stackit-icons/runtime/kubernetes.svg
            link: https://docs.stackit.cloud/products/runtime/kubernetes-engine/
          }

          PodB: "Pod B" {
            icon: ../../../../../../public/stackit-icons/runtime/kubernetes.svg
            link: https://docs.stackit.cloud/products/runtime/kubernetes-engine/
          }
        }

        HPA: "HPA" {
          icon: ../../../../../../public/stackit-icons/runtime/kubernetes.svg
          link: https://docs.stackit.cloud/products/runtime/kubernetes-engine/
        }
      }
    }

    PlatformLayer: "Platform Layer" {
      direction: right

      Compute: "" {
        direction: right

        NodePoolA: "Node Pool AZ-1" {
          link: https://docs.stackit.cloud/products/runtime/kubernetes-engine/
          grid-columns: 2

          VM1: "VM" {
            icon: ../../../../../../public/stackit-icons/computing/virtual-machine.svg
            link: https://docs.stackit.cloud/products/runtime/kubernetes-engine/
          }
        }

        NodePoolB: "Node Pool AZ-2" {
          link: https://docs.stackit.cloud/products/runtime/kubernetes-engine/
          grid-columns: 2

          VM2: "VM" {
            icon: ../../../../../../public/stackit-icons/computing/virtual-machine.svg
            link: https://docs.stackit.cloud/products/runtime/kubernetes-engine/
          }
        }

        NodeAutoscaler: "Node Autoscaler" {
          icon: ../../../../../../public/stackit-icons/runtime/kubernetes.svg
          link: https://docs.stackit.cloud/products/runtime/kubernetes-engine/
        }
      }

      Storage: "Persistent Storage" {
        link: https://docs.stackit.cloud/products/runtime/kubernetes-engine/
        grid-columns: 2

        PV1: "PV" {
          icon: ../../../../../../public/stackit-icons/computing/archive.svg
          link: https://docs.stackit.cloud/products/runtime/kubernetes-engine/
        }

        PV2: "PV" {
          icon: ../../../../../../public/stackit-icons/computing/archive.svg
          link: https://docs.stackit.cloud/products/runtime/kubernetes-engine/
        }
      }
    }

  }

}

Backend: "Backend Services" {
  direction: right
  grid-columns: 5

  PG: "PostgreSQL" {
    icon: ../../../../../../public/stackit-icons/databases/postgresql-flex.svg
    link: https://docs.stackit.cloud/products/databases/postgresql-flex/
  }

  Rabbit: "RabbitMQ" {
    icon: ../../../../../../public/stackit-icons/messaging/rabbit-mq.svg
    link: https://docs.stackit.cloud/products/messaging/rabbitmq/
  }

  Obj: "Object Storage" {
    icon: ../../../../../../public/stackit-icons/computing/object-storage.svg
    link: https://docs.stackit.cloud/products/storage/object-storage/
  }

  Secrets: "Secret Manager" {
    icon: ../../../../../../public/stackit-icons/security/secrets-manager.svg
    link: https://docs.stackit.cloud/products/security/secrets-manager/
  }

  Obs: "Observability" {
    icon: ../../../../../../public/stackit-icons/logging-monitoring/observability.svg
    link: https://docs.stackit.cloud/products/logging-and-monitoring/observability/
  }
}

SKEProject.Kubernetes.ServiceLayer -> SKEProject.Kubernetes.WorkloadLayer: { style.opacity: 0 }

Internet -> SKEProject.Access.ExternalLB
Internet -> SKEProject.Access.DNS
SKEProject.Access.DNS -> SKEProject.Kubernetes.EntryLayer.ExternalDNS
SKEProject.Access.ExternalLB -> SKEProject.Kubernetes.EntryLayer.Ingress
SKEProject.Kubernetes.EntryLayer.Ingress -> SKEProject.Kubernetes.ServiceLayer.K8sService
SKEProject.Kubernetes.ServiceLayer.K8sService -> SKEProject.Kubernetes.WorkloadLayer.WorkloadRow.Deployment
SKEProject.Kubernetes.WorkloadLayer -> SKEProject.Kubernetes.PlatformLayer
SKEProject.Kubernetes -> Backend

SKEProject.Kubernetes.PlatformLayer.Compute.NodePoolA -> SKEProject.Kubernetes.PlatformLayer.Compute.NodeAutoscaler: { style.opacity: 0 }
```

## Design best practices

- **Decouple runtime and data migration gates**: validate database migration and runtime rollout independently.
- **Design secret delivery explicitly**: the reference uses Kubernetes Secrets and protected Terraform state; add a reviewed external secret integration when required.
- **Standardize observability labels and dashboards**: make cross-application operation and incident handling consistent.
- **Keep RabbitMQ optional and explicit**: add it when asynchronous integration or buffering is required.
- **Qualify availability separately**: a multi-zone design needs suitable worker capacity, placement rules, disruption budgets, and application and database failure testing; it is not enabled by the baseline.

## Related migration assets

<LinkCard
  title="Replatform Spring Boot with Terraform"
  description="Follow the executable provisioning, source-evidence, rehearsal, cutover, and rollback workflow for this architecture."
  href="/migration/assetcontainer/stackit/replatform-automation-spring-boot-vm-to-kubernetes-terraform/"
/>

## Repository usage and required settings

<LinkCard
  title="STACKIT CMF Replatform Spring Boot Kubernetes repository"
  href="https://github.com/stackitcloud/stackit-cmf-replatform-springboot-k8s"
/>

1. Copy the example file: `cp env.tfvars.example env.tfvars`
2. Set required identity/project values:

```hcl
service_account_key_path   = "/path/to/stackit-sa-key.json"
create_project             = true
target_project_owner_email = "owner@sa.stackit.cloud"
parent_container_id        = "cmf-parent-container-id"
ske_cluster_name           = "rpltfk8s01"
observability_instance_name = "cmf-rpltf-observability"
dns_zone_name              = "cmf-example.runs.onstackit.cloud"
dns_zone_display_name      = "cmf-example"
```

3. Enable the target architecture switches:

```hcl
observability_enabled         = true
create_observability_instance = true
dns_enabled                   = true
create_dns_zone               = true
deploy_workload               = true
enable_postgres_flex          = true
enable_springboot_hpa         = false
enable_load_generator         = false
springboot_replicas           = 1
deploy_postgres_migration_job = false
create_grafana_dashboard     = true
```

4. Optional CMF flag wrapper (`flags.env`):

```dotenv
setup_project=true
setup_observability=true
setup_database=true
setup_workload=true
setup_loadgen=false
setup_dns=true
```

5. Apply:

```bash
terraform init
terraform validate
terraform plan -var-file=env.tfvars -out=tfplan
terraform apply tfplan
```

Expected result: `springboot_url` reaches the application through the Gateway, the application
uses PostgreSQL Flex, and `grafana_dashboard_url` opens the managed dashboard. Provisioning
does not import source data. Follow the separate rehearsal and cutover workflow after target
validation; keep HPA disabled throughout migration.

<LinkCard
  title="Implemented topology and prerequisites"
  description="Review the exact resource definitions and operational boundaries in the Spring Boot Replatform repository."
  href="https://github.com/stackitcloud/stackit-cmf-replatform-springboot-k8s#readme"
/>
