---
title: "Replatform Spring Boot from VM to Kubernetes with Terraform"
description: "Run the same Spring Boot JAR on STACKIT Kubernetes with PostgreSQL Flex, Gateway API, DNS, managed telemetry, and rehearsed database cutover and rollback."
scfAsset:
  managed: false
  category: "runbook"
  external: false
  tags: ["design-and-mobilize", "use-cases", "runnable-example", "replatform", "kubernetes", "terraform", "ske", "spring-boot", "postgresql"]
  maintainers:
    - user: "lukas.weberruss"
      role: true
      website: true
source_url: "https://framework.stackit.cloud/migration/assetcontainer/stackit/replatform-automation-spring-boot-vm-to-kubernetes-terraform/"
source_file: "docs/migration/assetcontainer/stackit/replatform-automation-spring-boot-vm-to-kubernetes-terraform.mdx"
---

## Overview

This asset applies the Migration Framework to a Spring Boot and PostgreSQL Replatform:
the application moves from a VM service to STACKIT Kubernetes Engine (SKE), and its
database moves from self-managed PostgreSQL to STACKIT PostgreSQL Flex. The business
function and application JAR stay unchanged; the runtime and database operating models change.

## Reference implementation

The reference repository is the source of truth for Terraform, Helm charts, pinned artifacts,
migration scripts, and validation. Use a reviewed revision containing the Gateway API and
`scripts/migrate_postgres.py` workflows described here; an older revision with a direct
database-import Job does not implement this procedure.

<LinkCard
  title="STACKIT Spring Boot Kubernetes Replatform repository"
  description="Open the Terraform, Gateway API, PostgreSQL migration, and Observability implementation used throughout this asset."
  href="https://github.com/stackitcloud/stackit-cmf-replatform-springboot-k8s"
/>

## Replatform scope in this asset

- **Runtime**: the identical Spring Music Spring Boot 2.4.0 JAR runs on Java 11 in a Kubernetes Deployment instead of under systemd.
- **Data**: PostgreSQL Flex supplies dedicated application and rehearsal databases; JDBC and migration clients require TLS.
- **Traffic**: Envoy Gateway, Gateway API HTTPRoutes, and SKE-managed ExternalDNS replace the VM endpoint.
- **Operations**: Kubernetes health and resource controls replace host-service management; managed telemetry covers cluster, application, and database signals.
- **Migration**: source evidence, isolated rehearsal, an explicitly approved cutover, and verified database rollback remain separate from infrastructure provisioning.

Cloud Foundry, Object Storage, microservice decomposition, and application modernization are not
part of this implementation. The old sample application demonstrates platform substitution,
not a recommendation to deploy an unsupported application stack in production.

## Reference architecture diagram

Review the architecture asset before choosing capacity and network controls. It separates the
implemented topology from production extensions such as public HTTPS, highly available workers,
and protected metrics.

<LinkCard
  title="Spring Boot on SKE with PostgreSQL Flex and Gateway API"
  description="Review the implemented topology, runtime and data boundaries, and separately qualified production extensions."
  href="/migration/assetcontainer/stackit/architecture-spring-boot-kubernetes-paas-data-object-storage/"
/>

## End-to-end flow

1. Confirm Replatform suitability, source compatibility, landing-zone readiness, and ownership.
2. Prepare a trusted PostgreSQL dump and integrity manifest independently of target provisioning.
3. Review and apply Terraform for SKE, PostgreSQL Flex, Gateway, DNS, workload, and Observability.
4. Validate the target, then rehearse the final source dump in the isolated rehearsal database.
5. Freeze source writes, approve downtime, and run the gated cutover with a protected target backup.
6. Accept application and data evidence or restore the pre-cutover target; switch traffic through the approved operator procedure.
7. Retain evidence through stabilization and use representative telemetry for later optimization.

## Prepare the walkthrough workspace

Use an isolated Linux lab environment with Git, Terraform, kubectl, curl, jq, getent,
Python 3.11 or newer, and the PostgreSQL server and client tools. The Rehost sample scripts
also require `runuser`, `sha256sum`, a `postgres` OS account, and root privileges to create
and validate a temporary local database. Run the sample commands in that prepared lab
environment, not on a production database host. Keep the generated private artifacts
readable by the operator running the migration; do not make them world-readable.

Obtain reviewed commit IDs for both repositories from the reference maintainer and export
them as `REHOST_REVISION` and `REPLATFORM_REVISION` before continuing. The Replatform revision
must contain the Gateway API and gated migration workflow. Do not assume the remote default
branch already contains the locally tested implementation; if the approved revision is not
available, stop and obtain it before attempting the walkthrough.

Replace both placeholders with the reviewed full 40-character commit IDs, then set them
in the same Bash terminal:

```bash
export REHOST_REVISION="REPLACE_WITH_REVIEWED_REHOST_COMMIT_ID"
export REPLATFORM_REVISION="REPLACE_WITH_REVIEWED_REPLATFORM_COMMIT_ID"
```

Run the following complete block from an empty working directory. The `if` check rejects
missing, empty, or malformed IDs, including unchanged placeholders. Each `&&` runs the next
command only if the previous one succeeded. Git checks whether the commits are available;
the format check alone does not verify approval or repository contents.

```bash
if [[ ! ${REHOST_REVISION:-} =~ ^[0-9a-fA-F]{40}$ ||
      ! ${REPLATFORM_REVISION:-} =~ ^[0-9a-fA-F]{40}$ ]]; then
  printf '%s\n' "Set both revision variables to reviewed full 40-character commit IDs." >&2
  false
else
  umask 077 &&
    git clone https://github.com/stackitcloud/stackit-cmf-Rehost-springboot.git &&
    git clone https://github.com/stackitcloud/stackit-cmf-replatform-springboot-k8s.git &&
    git -C stackit-cmf-Rehost-springboot checkout --detach "$REHOST_REVISION" &&
    git -C stackit-cmf-replatform-springboot-k8s checkout --detach "$REPLATFORM_REVISION" &&
    test -f stackit-cmf-replatform-springboot-k8s/scripts/migrate_postgres.py &&
    test -f stackit-cmf-replatform-springboot-k8s/scripts/validate_gateway.sh &&
    cd stackit-cmf-replatform-springboot-k8s &&
    printf '%s\n' "Workspace ready. Continue from this Replatform checkout." || {
      printf '%s\n' "Preparation failed. Resolve the error before continuing." >&2
      false
    }
fi
```

Continue only after `Workspace ready` appears. On failure the terminal stays open; later
preparation commands are skipped. Existing or partially cloned directories are not removed
or overwritten: inspect them and preserve local changes before retrying in a new empty
working directory. On success, the terminal is in the Replatform checkout for the next steps.

Use an approved STACKIT project, service account, DNS delegation, SKE capacity, and protected
Terraform backend. Obtain credentials through the approved secret channel, never from this
Trail. The following commands assume this directory layout and a reviewed configuration;
they do not establish a validated greenfield production deployment.

## Prepare source evidence

Qualify a consistent source dump and its manifest before data enters the migration workflow.

The Rehost reference supplies `scripts/create_source_dump.sh` and
`scripts/validate_source_dump.sh` for its reproducible sample. Run them from that repository.
Its `artifacts` directory supplies `source-postgresql.dump` and
`source-postgresql.manifest` to the Replatform workflow. The manifest records version 1,
`table=public.album`, `row_count`, `album_fingerprint`, and `dump_sha256`.

For the reproducible sample only, run the following from the Replatform checkout in the
prepared lab environment. The scripts create a temporary PostgreSQL instance from the
versioned sample SQL, export it, then perform an independent test restore. Use a fresh
artifact directory; do not overwrite evidence from a migration already in progress.

```bash
umask 077
pushd ../stackit-cmf-Rehost-springboot
bash scripts/create_source_dump.sh
bash scripts/validate_source_dump.sh
popd
```

Expect successful validation of eight rows and a matching fingerprint. These commands do
not read a source VM. Keep using these exact artifacts for rehearsal and cutover.

For a real source, replace the sample generator with an approved export procedure: freeze all
writers and derive the custom-format dump and manifest from the same consistent source snapshot.
Do not mistake the generated eight-album sample for an export of an arbitrary running VM.
Verify PostgreSQL compatibility, extensions, ownership, and schema dependencies before export.

Only trusted dumps may be restored because they execute SQL. This implementation migrates the
`public` application schema and checks `public.album`; it deliberately excludes Flex-managed
schemas. Other workloads require their own invariants and an adapted schema scope.

<LinkCard
  title="Spring Boot Rehost source and sample export"
  description="Use the Rehost repository for the identical application artifact and reproducible PostgreSQL source-evidence tools."
  href="https://github.com/stackitcloud/stackit-cmf-Rehost-springboot"
/>

## Provision the target

Provision the target from a reviewed plan with explicit project, capacity, access, and DNS inputs.

Use Terraform, kubectl, curl, jq, getent, and Python 3.11 or newer on Linux. Copy
`env.tfvars.example` to `env.tfvars` and adapt the actual variables in that file. Keep
the tested provider lock file and immutable image and JAR references. Confirm SKE version
availability, node-pool capacity, project permissions, and DNS delegation before planning.

> From the STACKIT docs: [Lifecycle of Kubernetes Engine › Kubernetes end-of-life dates](https://docs.stackit.cloud/products/runtime/kubernetes-engine/reference/lifecycle/#kubernetes-end-of-life-dates) (Source updated 24.08.2026, copied 05.10.2026)

Starting with Kubernetes v1.33, we remove minor versions on the [patch day](https://docs.stackit.cloud/products/runtime/kubernetes-engine/reference/lifecycle/#operating-system-patch-days) that precedes the upstream maintenance end-of-life (EOL) date. The following table below lists the upstream EOL date for each Kubernetes minor version and the corresponding expiration date in SKE:

| Kubernetes minor version | End-of-life date | Expiration in SKE |
| --- | --- | --- |
| v1.32 | 2026-02-28 | 2026-04-15 |
| v1.33 | 2026-06-28 | 2026-06-10 |
| v1.34 | 2026-10-27 | 2026-10-14 |
| v1.35 | 2027-02-28 | 2027-02-10 |

Please refer to the official Kubernetes Release History for up-to-date announcements of new versions.

Prefer an approved Application Landing Zone project. Set `create_project = false` and provide
its project ID and service account key path. Project creation is an alternative requiring an
approved parent container and permissions; it is not a replacement for landing-zone governance.
Never put credentials, state, saved plans, or migration evidence in version control.

For a new checkout, create the private variable file without overwriting an existing one:

```bash
umask 077
test -e env.tfvars || cp env.tfvars.example env.tfvars
chmod 600 env.tfvars
```

Edit this file before planning: supply the approved project and service-account path,
region, supported SKE version, available node-pool flavor and zone, and delegated DNS
settings from the repository example. Review backend access and locking, quotas, costs,
and the HTTP/public-metrics limitations. The next section's feature flags are not a
complete environment configuration.

## Unified automation flags

The optional common wrapper maps `setup_project`, `setup_observability`, `setup_database`,
`setup_workload`, `setup_loadgen`, and `setup_dns` to the repository's Terraform switches.
These select provisioning scope only: enabling the database does not authorize data replacement
and never replaces the separate migration approval gate.

## Minimal target configuration

These values select the complete workload and database path. They supplement, rather than
replace, the project, region, node-pool, and DNS values in the repository example.

```hcl
deploy_workload                   = true
dns_enabled                       = true
enable_postgres_flex              = true
postgres_flex_target_database     = "springmusic"
postgres_flex_target_app_acl_cidrs = []
observability_enabled             = true
create_observability_instance     = true
create_grafana_dashboard          = true
enable_springboot_hpa              = false
enable_load_generator             = false
deploy_postgres_migration_job      = false
```

Keep HPA and load generation disabled during migration. Choose alert settings deliberately;
the end-to-end test did not validate alert delivery. `springboot_image` selects the Java runtime,
not an unrelated prebuilt application image. The init container downloads the commit-pinned
Rehost JAR and verifies its SHA-256 before startup. Mirror immutable artifacts into approved
artifact and image services for production.

Run from the Replatform repository and review the saved plan before applying:

```bash
umask 077
terraform init
terraform validate
terraform plan -var-file=env.tfvars -out=tfplan
terraform apply tfplan
bash scripts/validate_gateway.sh
```

## Access control and temporary migration ACL extension

With empty application ACL inputs, Terraform uses the SKE cluster's actual egress CIDRs for
PostgreSQL Flex. Explicit application or legacy ACL values override that default and must be
reviewed. Do not permit `0.0.0.0/0`.

The temporary PostgreSQL client runs inside SKE and receives the dump through kubectl. It does
not connect directly to the source VM, and no source or workstation CIDR needs temporary Flex
access. JDBC and database tools use `sslmode=require`; this requires encryption but does not
provide the hostname verification of `verify-full`. Protect credentials in Kubernetes Secrets
and the Terraform backend, and validate stronger certificate verification where required.

## Rehearse the migration

Restore the final dump into the isolated rehearsal database and require matching evidence less than 24 hours old.

After infrastructure apply completes, run an isolated restore into `springmusic_rehearsal`.
Adjust the source directory to the approved artifacts; keep the evidence path private.

```bash
python3 scripts/migrate_postgres.py rehearse \
  --artifacts ../stackit-cmf-Rehost-springboot/artifacts \
  --evidence .tmp/migration-run
```

Rehearsal validates the manifest, dump checksum, target identity, row count, and fingerprint
without replacing the application database. After the source write freeze, rehearse the final
dump again. Cutover requires matching evidence from less than 24 hours ago. A successful
rehearsal of an older or different dump is not approval for the final input.

## VM PostgreSQL to PostgreSQL Flex migration option

The old `deploy_postgres_migration_job = true` path is disabled by validation. Use the gated
workflow instead. Stop HPA, load generation, and all other target writers. Suspend Terraform
and GitOps reconciliation while the script controls the Deployment replica count. Its local
lock protects one checkout, not concurrent operators on different machines.

```bash
python3 scripts/migrate_postgres.py cutover \
  --artifacts ../stackit-cmf-Rehost-springboot/artifacts \
  --evidence .tmp/migration-run \
  --source-write-frozen --confirm-target springmusic
```

The source-write flag is an operator attestation, not an automatic source shutdown. Cutover
scales the application to zero, saves and checksums the pre-cutover target, proves that backup
by restoring it into the rehearsal database, and only then restores the source transactionally.
It verifies data before restarting the original replica count. Failure leaves the application
stopped for investigation. Preserve the evidence journal and backup; do not overwrite them to retry.

## Validate and roll back

Compare the database evidence with the source manifest, check application behavior through
the Gateway, and confirm both metrics jobs are healthy. Traffic switching and business acceptance
remain operator-controlled steps; the script does not change the source application's endpoint.

To restore the protected pre-cutover target database:

```bash
python3 scripts/migrate_postgres.py rollback \
  --evidence .tmp/migration-run --confirm-target springmusic
```

Rollback checks target identity and backup integrity, saves the current target separately, restores
the original data, and verifies its fingerprint before restarting. Post-cutover writes are not
merged; retain the pre-rollback dump for explicit reconciliation. This is target-database rollback,
not automatic failback to the source VM.

## Database metrics visibility in Observability

Terraform manages the `SCF Replatform` Grafana folder and eight-panel dashboard against the
existing `Thanos` datasource. Use `grafana_dashboard_url` to open it. Cluster CPU, cluster memory,
running pods, application requests, and PostgreSQL health and pressure support acceptance and
later optimization. No manual dashboard import is required.

Application metrics come from the pod-local Boot 2 Actuator through the metrics adapter on
port 9090; the PostgreSQL exporter serves port 9187. Check both actual scrape results, not only
dashboard rendering. Missing telemetry is an investigation trigger, never proof of zero load.

## Validation with kubectl

Export a short-lived kubeconfig with private permissions and inspect the default namespace:

```bash
umask 077
mkdir -p .tmp
terraform output -raw kubeconfig > .tmp/replatform.kubeconfig
export KUBECONFIG="$PWD/.tmp/replatform.kubeconfig"
kubectl get deploy,svc,pods -n springboot
kubectl get gateway,httproute -n springboot
kubectl rollout status deployment/springboot -n springboot
bash scripts/validate_gateway.sh
```

The Gateway validator checks acceptance, resolved route references, DNS, and the application
response. Remove the local kubeconfig after use and obtain a fresh one when it expires.
Do not treat successful rollout alone as data or business acceptance.

## Backup and recovery boundary

Keep migration rollback distinct from Flex service recovery and retain protected evidence outside ephemeral execution environments.

Flex retention is configured explicitly, with a 32-day default in this reference. Managed database
backups and the migration pre-cutover dump serve different purposes. Rehearsing the latter does
not prove managed-service restore, point-in-time recovery, or application disaster recovery.
Assign recovery ownership and test the required service recovery path separately.

Retain protected evidence and backups outside an ephemeral dev container until the rollback
window closes. After a killed migration process, inspect leftover `springmusic-migration-*`
pods before resuming; do not use Terraform to restart an unverified target.

## What this asset demonstrates

This asset demonstrates how to preserve application behavior while changing the runtime
and database operating models:

- **Platform substitution**: run the same Spring Boot JAR on SKE, connect it to PostgreSQL Flex over TLS, and expose it through Gateway API and DNS.
- **Controlled data migration**: qualify a source dump and manifest, rehearse an isolated restore, and require explicit approval and a verified target backup before cutover. Restore the pre-cutover target when rollback is required.
- **Independent validation**: check data integrity, application responses, Gateway and DNS readiness, and actual scrape results. Review the Terraform plan for unexplained drift rather than treating successful provisioning as migration acceptance.
- **Repeatable observability**: manage the Observability integration and eight-panel Grafana dashboard through Terraform. Use application and database signals for acceptance, stabilization, and later optimization.

## Out of scope

This evidence does not establish a complete greenfield replay, migration from a live production
source, zero downtime, high availability, public Gateway TLS, interactive IDP login, alert delivery,
or managed Flex recovery. The tested single-worker HTTP setup exposes unauthenticated metrics;
resolve those production requirements before using sensitive data. Upgrade the sample application
and validate an appropriate supported Kubernetes release as separate controlled changes.

<LinkCard
  title="Reference configuration, scripts, and validation evidence"
  description="Use the repository README and versioned implementation for exact prerequisites, variables, commands, and supported recovery boundaries."
  href="https://github.com/stackitcloud/stackit-cmf-replatform-springboot-k8s#readme"
/>
