Skip to content
Beta

Rehost to STACKIT: Spring Boot with Terraform and Ansible

Last updated on

Stackit LogoStackit Logo
STACKIT

Rehost to STACKIT: Spring Boot with Terraform and Ansible

Rehost Spring Boot and PostgreSQL to a STACKIT VM with Terraform and Ansible: prepare inputs, provision, rehearse, cut over, validate, and hand over operations.

LIFT

Rehost Strategy

Use Discovery evidence to confirm that preserving the Spring Boot JAR, systemd service model, and PostgreSQL engine on a VM meets the migration goals; Kubernetes, Cloud Foundry, or PostgreSQL Flex would instead be Replatform. Derive the delivery sequence from that decision: Landing Zone, Terraform infrastructure, Ansible configuration, separate PostgreSQL migration, and the rehearsed runbook.

Design and mobilizeDesignRehost In 2 trails
R-strategy migration method Decision flow from discovery to production with the seven R-strategies: Relocate, Rehost, Replatform, Repurchase, Refactor, Retain, and Retire. R-strategy migration methodFrom discovery and path selection through the seven R-strategies to validation, transition, and production.DiscoveryDiscoveryAssess / prioritizeAssess / prioritizeDetermine migration pathDetermine migration pathValidationValidationTransitionTransitionProductionProductionRelocateRelocate(move VM)Define Landing ZoneDefine Landing ZoneUse migration toolsUse migration toolsAUTOMATEMANUALInstallInstallConfigConfigDeployDeployValidation & handoverRehostingRehosting(move application)Define Landing ZoneDefine Landing ZoneUse migration toolsUse migration toolsAUTOMATEMANUALInstallInstallConfigConfigDeployDeployReplatformingReplatforming(lift and reshape)Define Landing ZoneDefine Landing ZoneMap Target PlatformMap Target PlatformAdapt Platform StackAdapt Platform StackRepurchasingRepurchasing(replace, drop and shop)Purchase COTS/SaaS and licensingPurchase COTS/SaaS and licensingMigrate business processMigrate business processRefactoringRefactoring(re-architecting applications)Redesign application/ infrastructure architectureRedesign application/ infrastructure architectureApp code developmentApp code developmentFull ALM/SDLCFull ALM/SDLCIntegrationIntegrationRetain/moveRetain/movekeep for now or move laterRetire/decommissionRetire/decommissionLanding zone foundationLanding zone foundationShared platform base for all paths
R-strategy method with Rehost as the application-level lift-and-shift path

Rehost (lift-and-shift) migrates workloads with minimal application change. It is primarily a run strategy to reduce transition risk and accelerate migration throughput.

In practical terms, Rehost is application-wave oriented: each workload gets a defined target runtime mapping, cutover path, and runbook package for repeatable factory delivery.

Relocate and Rehost are often used interchangeably, but in this framework they are separated on purpose.

  • Rehost in STACKIT: Application-level lift-and-shift with explicit target runtime mapping, cutover, rollback, and runbook standardization.
  • Relocate in STACKIT: Estate-level move of existing virtualization patterns with minimal reshaping of workload runtime behavior.
  • When to prefer Rehost: When migration waves require repeatable runbooks and stable run operations at scale across many applications.
  • When to prefer Relocate: When urgent movement of VM estates is the primary goal and modernization is deferred by design.
  • Strict migration timeline with limited engineering capacity for redesign.
  • Legacy workloads that are difficult to refactor in the current program phase.
  • Business stability priority where functional behavior must remain largely unchanged.
  • Factory scale objective where repeatable migration procedures are required across many systems.

Infrastructure mapping

Define target compute, storage, and network profiles with explicit compatibility checks.

Data and cutover path

Design transfer windows, consistency checks, and rollback triggers.

Stateful workload handling

Separate application artifact rollout from database migration sequencing to reduce cutover risk.

Security and compliance

Map identity controls, encryption requirements, and evidence checkpoints.

Operational handover

Ensure runbooks cover Day-1 operations and incident workflows after migration.

  1. Baseline current runtime dependencies and non-functional requirements.
  2. Define target runtime mapping and migration sequence.
  3. Design data movement and cutover orchestration.
  4. Validate runbook quality with dry-run checkpoints.
  5. Approve production migration with release and business sign-off.

In Rehost scenarios, the data migration path depends on whether the workload is stateless or stateful:

  • Stateless workloads: Focus on application deployment and configuration.
  • Stateful workloads: Require a coordinated data movement strategy alongside the application migration.

For stateful migration waves, split the path into two streams:

  1. Infrastructure and application stream: Provision the target environment, deploy the application, and prepare the target data store.
  2. Data stream: Export source data, transfer to the target, restore, and validate.
  1. Export data from the source system using platform-native or tool-specific methods.
  2. Transfer data to the target environment or intermediate staging area.
  3. Configure the target environment to ingest the migrated data.
  4. Run the restoration process and verify data integrity.
  5. Validate application connectivity and functional behavior before final cutover.
  • Approved design decision record with scope, assumptions, and governance sign-off.
  • Validation evidence package for security, compliance, and operational readiness.
  • Strategy-specific migration runbook draft from the Design phase.
  • Handover package for Migration Factory Setup and wave planning.
  • Rehost decision rationale and constraints.
  • Target runtime mapping.
  • Data movement and cutover plan.
  • Runbook with validation and rollback checkpoints.
  • Post-wave stabilization checklist.

Define landing zone requirements and controls as the start point for Rehost run. Clarify network, identity, backup, and monitoring prerequisites before migration sequencing is approved so Rehost waves can run with predictable operations quality.

Define the automated Rehost path used for repeatable wave throughput. In the automated Rehost path, infrastructure and application setup are provisioned as code so wave delivery stays repeatable and auditable.

  • Provision VM target with IaC: Create networks, security groups, compute instances, and base storage through Terraform or OpenTofu.

  • Install and configure application with automation: Use Ansible playbooks for package install, service setup, and baseline configuration.

  • Apply environment-specific parameters: Inject target variables, secrets references, and endpoint mappings in a controlled automation run.

  • Run automated validation and cutover gates: Execute health checks, migration pre-checks, rollback checkpoints, and release approvals before live switch.

  • Runbook Blueprint
  • Migration Plan
Asset title
Framework
Asset type

The runbook asset documents the PostgreSQL flags and the optional dump-based data restore path.

Describe manual installation steps for exception workloads.

  • Create and prepare target VM manually: Provision the VM through Portal or CLI, attach required storage, and apply OS hardening and patch baseline.

  • Install runtime and dependencies manually: Install required runtime packages, system libraries, and service users/groups according to product installation guidance.

  • Install application in the classic way: Perform guided installation steps (for example installer or setup wizard flow) to reproduce the source deployment model on the target VM.

  • Validate base install readiness: Confirm service startup, file permissions, required ports, DNS reachability, and outbound connectivity.

  • Cloud Design Patterns
  • Runbook Blueprint

Define manual runtime configuration and controls.

  • Mirror source configuration for target context: Recreate application settings from the source environment and adapt them to target endpoints, DNS, certificates, and service integrations.

  • Apply security and access settings: Configure service credentials, secret handling, and least-privilege access for target operation.

  • Align operational defaults: Configure logging targets, metrics exporters, backup schedules, and retention baselines.

  • Validate configuration parity: Run smoke checks to confirm the target instance behaves as a functional equivalent of the source baseline.

  • Runbook Blueprint

Define manual deployment sequencing and release checks.

  • Plan final migration window: Align freeze windows, communication checkpoints, and rollback authority for production switch.

  • Run final data migration: Execute last data sync or restore steps and confirm consistency checks before go-live.

  • Activate production traffic: Perform controlled live switch to the target environment and verify critical user and integration paths.

  • Confirm handover readiness: Record evidence, close open risks, and transfer ownership for Day-1 operations.

  • Migration Plan
  • Runbook Blueprint
OPS

Target Architecture

This pattern represents the validated low-change target for the Spring Boot Rehost example. The JAR continues to run as a systemd service, while PostgreSQL remains self-managed on the same VM. The runtime and database operating models therefore stay VM-centric.

The baseline is intentionally one VM. It demonstrates repeatable migration controls and operations readiness, not application or database high availability.

  • Low change tolerance: business behavior must remain stable during migration.
  • Short migration windows: runtime relocation should be predictable and repeatable.
  • Operations continuity: teams keep VM-centric operating procedures while moving to STACKIT.
InternetApplication ProjectPublic IPUbuntu VMObservabilityBackup ArchiveSpring Boot JAR + systemdSelf-managed PostgreSQLNode exporter restricted HTTP/SSHlocalhost SQLrestricted scrapeboot-volume backup
  • Restrict ingress explicitly: allow SSH and application traffic only from approved source CIDRs; allow exporter traffic only from STACKIT service ranges.
  • Keep credentials out of state and inventory: pass the PostgreSQL password through the process environment and store the generated runtime environment file as root-only.
  • Define observability minimum set: include infrastructure and application health metrics before go-live.
  • Separate recovery layers: use the database pre-restore dump for cutover rollback and Server Backup for VM-level disaster recovery.
  • State the availability boundary: one VM with a local database has a shared failure domain. Add a load balancer or second node only with a separately designed database and consistency model.
  • Use explicit security groups and ingress rules: expose only required ports and protocols.

Confirm which volumes the backup policy covers and rehearse recovery independently of cutover rollback. The available server-backup features do not by themselves prove PostgreSQL consistency or the application’s recovery objectives.

From the STACKIT docsFeatures and benefits › FeaturesSource updated 13.11.2025 · copied 06.10.2026
  • Automated, monitored server backups with easy recovery
  • Create backups for one, multiple, or all volumes connected to a server at any time
  • The advanced custom backup schedules enable automated backups to be created
  • Freely select the retention period after which backups are automatically deleted
  • Partly restore certain files by seamlessly restoring a volume backup to a new volume
What is this?

This section is copied from the STACKIT docs automatically, several times a day. It cannot be changed here. Changes belong in the STACKIT docs.

Code & registry github.com STACKIT CMF Rehost Spring Boot repository Open the repository
  1. Copy the example file: cp env.tfvars.example env.tfvars
  2. Set required values:
create_project = true
target_project_name = "cmf-rehost-springboot"
target_project_owner_email = "owner@sa.stackit.cloud"
parent_container_id = "cmf-parent-container-id"
service_account_key_path = "/path/to/stackit-sa-key.json"
run_ansible = true
jar_local_path = "ansible/files/springboot-app.jar"
availability_zone = "eu01-1"
machine_type = "g2i.2"
ssh_allowed_cidr = "203.0.113.10/32"
app_allowed_cidr = "203.0.113.10/32"
enable_observability = true
enable_node_exporter = true
enable_local_postgresql = true
enable_server_backup = true
  1. Optional CMF feature wrapper (flags.env):
setup_project=true
setup_observability=true
setup_database=false
setup_workload=true
setup_loadgen=false
setup_dns=false
  1. Apply:
Terminal window
terraform init
terraform apply -var-file=env.tfvars

Expected result: application_url serves the Spring Boot app directly from the VM on the configured application port. PostgreSQL listens for the application on localhost, Observability scrapes node exporter, and the boot volume has an enabled daily backup schedule.

This baseline has no automatic failover. A load-balancer or multi-VM variant is useful only after application session handling, PostgreSQL placement, write consistency, health checks, TLS, and traffic switching have been designed and tested together. Treat that as a separate architecture decision, not as an implicit property of this Rehost path.

LIVE

Reference Implementation

The Migration Framework defines the strategy, design, landing-zone, migration, and operations principles for Rehost workloads. This asset applies those principles to one concrete, runnable Spring Boot and PostgreSQL implementation on STACKIT.

The maintained repository is the source of truth for Terraform, Ansible, application artifacts, migration scripts, validation, and runtime configuration. This asset explains how to use that implementation without duplicating its complete source code.

Code & registry github.com STACKIT CMF Rehost Spring Boot repository Open the runnable Terraform and Ansible reference implementation for the Spring Boot and PostgreSQL Rehost path. Open the repository

This example represents an application Rehost path for a Spring Boot workload (Spring Music sample). The migration focus is the runtime relocation of the application to STACKIT:

  • Infrastructure Rehost: Terraform provisions one STACKIT VM, its boot volume, network, public IP, security group, SSH key, Observability resources, and an optional Server Backup schedule.
  • Application Rehost: Ansible installs Java, deploys the Spring Boot JAR, and manages it with systemd without changing the application runtime model.
  • Stateful Rehost: Ansible installs self-managed PostgreSQL on the same VM. The database engine is not changed to a managed service as part of this path.
  • Controlled migration: The repository provides separate rehearsal, cutover, validation, and rollback workflows with machine-readable evidence.

The validated baseline deliberately does not include an application load balancer, DNS switch, multiple VMs, Kubernetes, Cloud Foundry, or PostgreSQL Flex. Add those only as separately designed and tested extensions; they are not implied by this Rehost example.

  • Provisioning: Terraform resources for network, security, one VM, boot volume, SSH key, and public IP.
  • Configuration bridge: Terraform triggers Ansible after infrastructure provisioning.
  • Application deployment: Ansible deploys a concrete Spring Boot artifact and configures a systemd service.
  • VM-local database path: PostgreSQL installation, application role and database bootstrap, and a dump-based restore with ownership and data-integrity checks.
  • Operations baseline: Node exporter metrics are scraped by STACKIT Observability, and Terraform can enable daily Server Backup for the VM boot volume.
  • Migration controls: Source dump generation, independent restore validation, rehearsal, cutover, rollback verification, rollback execution, and evidence capture.
  • Deployable sample artifact: A ready-to-run JAR is included in the repository and used by default deployment settings.

To keep automation behavior consistent across CMF examples, use this common flag model in your Terraform variable set:

  • setup_project: create/use project context.
  • setup_observability: enable/disable observability resources.
  • setup_database: enable/disable optional VM-local PostgreSQL path.
  • setup_workload: enable/disable workload installation on VM.
  • setup_loadgen: enable/disable optional synthetic load generation.
  • setup_dns: accepted by the shared wrapper but unsupported in this baseline.

In the current Rehost repository, these map to existing switches (create_project, enable_observability, enable_local_postgresql, and load-generation related flags).

The following diagram shows the implemented and tested target, not a future high-availability variant.

Approved operator sourceSTACKIT ProjectUbuntu VMObservabilityBackup ArchiveSpring Boot JAR + systemdSelf-managed PostgreSQL restricted HTTP/SSHlocalhost SQLrestricted metrics scrapeboot-volume backup

Use an isolated Linux lab with Git, Terraform, Ansible, ShellCheck, SSH/SCP, curl, jq, and PostgreSQL server/client tools including pg_config. The sample dump scripts use runuser and the local postgres OS account and require root privileges in that lab. When Server Backup is enabled, the cutover workflow also needs an authenticated STACKIT CLI. Use the Terraform version that created an existing saved plan; plan files are version-specific.

Start in a parent directory without an existing checkout of the same name. This revision contains the migration workflows and declarative Observability management. Its JAR is unchanged from the artifact pinned by the Replatform reference:

Terminal window
umask 077 &&
git clone https://github.com/stackitcloud/stackit-cmf-Rehost-springboot.git &&
git -C stackit-cmf-Rehost-springboot checkout --detach b9225eb35c64b9ef4761c208fadb9a2356431793 &&
test -f stackit-cmf-Rehost-springboot/scripts/run_cutover.sh &&
cd stackit-cmf-Rehost-springboot &&
printf '%s\n' "Workspace ready. Continue from this Rehost checkout." || {
printf '%s\n' "Preparation failed. Resolve the error before continuing." >&2
false
}

Run the following steps from that checkout only after preparation succeeds. Preserve an existing checkout, state and evidence rather than replacing them. Keep credentials, plans, state, dumps, inventory and evidence private and outside version control; do not enable shell tracing.

Create the private variable file only when it does not already exist:

Terminal window
umask 077
test -e env.tfvars || cp env.tfvars.example env.tfvars
chmod 600 env.tfvars

Edit the file before planning. Configure the approved existing project or project-creation scope, service-account key path, SSH key pair, available image, availability zone, VM flavor and storage. Restrict SSH and application ingress to the source CIDRs actually observed on the target path. Verify the SSH host key through a trusted channel before migration scripts use strict host checking. Set SSH_KEY and SSH_USER for those scripts when they differ from ~/.ssh/id_rsa and ubuntu; keep them consistent with Terraform’s private_ssh_key_path and ssh_user.

Enable VM-local PostgreSQL, Observability and Server Backup for this walkthrough. For the initial runtime deployment, leave postgresql_restore_after_copy = false and the source dump path empty: provisioning must not import the source before rehearsal and cutover approval. Supply the database password through the approved secret mechanism as TF_VAR_postgresql_app_password and retain it securely for subsequent plans; do not put it in the variable file, shell history or documentation.

Confirm permissions, quota, cost approval and the private Terraform backend before initialization. This workflow assumes a prepared execution host, not a complete lab installation procedure.

Create a custom-format source dump and record its SHA-256 checksum, expected row count, and a workload-specific deterministic fingerprint. The repository includes a reproducible eight-row sample:

Terminal window
./scripts/create_source_dump.sh
./scripts/validate_source_dump.sh

The validation script restores into a separate local PostgreSQL cluster before the dump can enter a migration rehearsal. These scripts generate the bundled sample in temporary local clusters; they do not export a running application or the STACKIT VM. Use a fresh artifact directory and do not overwrite a dump already approved for migration. For a real source, export it under the agreed write-freeze procedure and record equivalent checksum, count and fingerprint evidence.

Configure an existing STACKIT project or project creation, restrict SSH and application ingress to approved source CIDRs, and pass the database password through TF_VAR_postgresql_app_password rather than a variable file. Review a saved plan before apply.

Terminal window
terraform init &&
./scripts/check.sh &&
terraform plan -input=false -var-file=env.tfvars -out=tfplan

Stop if initialization, checks or planning fail. Review resource changes, target project, ingress, cost and the disabled initial restore before explicitly applying that saved plan:

Terminal window
terraform apply tfplan

Terraform runs Ansible after provisioning. A successful apply proves completion of that orchestration, not acceptance of migrated data. Check the runtime before beginning rehearsal:

Terminal window
./scripts/validate_deployment.sh

Run the rehearsal against a temporary database on the target VM:

Terminal window
./scripts/run_migration_rehearsal.sh

The rehearsal checks the source evidence, restores the dump, compares row count and fingerprint, and removes its temporary database without changing the production springmusic database.

Set the expected source evidence in env.tfvars:

enable_local_postgresql = true
postgresql_source_dump_local_path = "artifacts/source-postgresql.dump"
postgresql_restore_after_copy = true
postgresql_expected_album_count = 8
postgresql_expected_album_fingerprint = "<source-fingerprint>"

Then execute the explicit approval gate:

Terminal window
./scripts/run_cutover.sh --confirm

The script requires a fully available Server Backup no older than 24 hours, creates a saved Terraform plan that replaces only the Ansible orchestration resource, rejects unrelated infrastructure changes, applies the restore, validates data and runtime behavior, and requires a final no-op Terraform plan. Retries of the same source-dump hash preserve the original database rollback point.

Validation checks the source row count and fingerprint, table ownership, a transactionally rolled-back write as the application role, application reachability, services, and the protected rollback dump.

Terminal window
./scripts/validate_migration.sh
./scripts/validate_deployment.sh
./scripts/verify_rollback.sh

If an approved rollback trigger is met during the rollback window, preserve the current target database and restore the original pre-cutover dump:

Terminal window
./scripts/rollback_postgresql.sh --confirm

With enable_server_backup = true, Terraform enables STACKIT Server Backup and a daily schedule for the VM boot volume. The default retention is 14 days. Cutover requires a fully available backup no older than 24 hours. Backup creation and status were validated against the real target; an in-place Server Backup restore remains a disruptive disaster-recovery operation and must be rehearsed in a separate recovery environment.

The Server Backup complements the PostgreSQL pre-restore dump. It does not replace database-level consistency checks or the application rollback procedure.

Follow workspace preparation, private target configuration, source evidence and reviewed provisioning in that order. Rehearse before the approved cutover and retain the acceptance evidence afterwards. The repository scripts invoke terraform explicitly; an OpenTofu-only execution host needs a separately reviewed adaptation, not just replacement of commands in this page.

The implementation provisions STACKIT Observability and scrapes node exporter. The Grafana provider manages the included dashboard in the SCF Rehost folder with the existing Thanos datasource, waiting for instance readiness instead of silently skipping dashboard creation.

Verify live VM, Spring Boot and PostgreSQL metrics through grafana_dashboard_url, then require a no-op plan. Seven panels cover the active baseline; the synthetic request panel is included only when the local load generator is enabled. Synthetic requests do not represent all application traffic. The deployment validation also checks exporter metrics and service health after apply and database restore.

Initial Grafana admin credentials remain a temporary authentication dependency. Protect state and saved plans. Notification receivers, alert-routing ownership, application logs and distributed tracing require separate configuration and acceptance before production use.

This example can be run in two valid setup modes.

  • With an existing Application Landing Zone: Reuse the already defined project context and service account setup from your landing zone implementation. Use the known project ID as deployment target (for example with create_project = false and project_id = "...") and the service account JSON configured for that landing zone scope.
  • Standalone without existing landing zone project: Let the example create a dedicated project. In this mode, set create_project = true and provide parent_container_id with an existing folder/container where the service account has sufficient permissions.

For landing-zone design guidance, see Application Landing Zone.

Use this as a minimal starting point in env.tfvars.

create_project = true
target_project_name = "cmf-rehost-springboot"
target_project_owner_email = "owner@example.com"
parent_container_id = "cmf-xxxxxxxx"
service_account_key_path = "~/.ssh/cmf-sa.json"
public_ssh_key_path = "~/.ssh/id_rsa.pub"
private_ssh_key_path = "~/.ssh/id_rsa"
availability_zone = "eu01-1"
machine_type = "g2i.2"
ssh_allowed_cidr = "203.0.113.10/32"
app_allowed_cidr = "203.0.113.10/32"
enable_local_postgresql = true
enable_observability = true
enable_server_backup = true
  • Default artifact path: ansible/files/springboot-app.jar
  • Default variable: jar_local_path points to the same file.
  • Alternative artifact: Replace the file or override jar_local_path.

When the artifact changes, Terraform detects the checksum difference and re-runs the Ansible deployment step.

Every rehearsal, cutover, and rollback writes an evidence.env file below artifacts/evidence/<timestamp>-<mode>/. Accept a cutover only when the evidence records the expected checksum, row count, fingerprint, target VM, successful runtime validation, and final Terraform no-op.

Terminal window
./scripts/validate_migration.sh
./scripts/validate_deployment.sh
terraform plan -var-file=env.tfvars -detailed-exitcode

Use the same approved source path, restore flag, expected count and fingerprint as the completed cutover. Exit code 0 means no changes, 2 means changes are proposed, and 1 means an error. A plan alone is not proof of apply. The cutover workflow records status=passed and terraform_noop=true only after applying its saved plan and completing data and runtime validation.

  • Prepare: Approve the target design, access paths, source evidence and recovery responsibilities.
  • Provision: Review and apply the infrastructure plan, then verify the VM runtime independently.
  • Rehearse: Restore into an isolated database and verify integrity without changing the application database.
  • Cut over: Freeze source writes, approve the final dump and allow only the intended orchestration change.
  • Accept or recover: Require data, runtime and no-op evidence; invoke the agreed rollback before its deadline when acceptance fails.
  • Handover: Transfer monitoring, backup ownership, evidence and source-retention decisions before optimization.

The Replatform reference reuses this repository’s pinned Spring Music JAR and sample-data generator. Its sample walkthrough therefore needs this checkout and validated dump artifacts, but not a newly provisioned Rehost VM. Do not create another VM solely to generate the local sample.

For an actual migration from the Rehost VM to SKE and PostgreSQL Flex, verify that the VM is currently healthy, freeze its application writes, export its real PostgreSQL database and qualify that export. A historical successful Rehost apply does not establish current reachability or make the local sample equivalent to live VM data. Source export, acceptance and source failback need their own approved procedure.

  • Keep operational procedures aligned with the runbook and migration governance.
  • For production usage, adapt security controls, sizing, image selection, and life cycle automation.
BASE

Prepare the Workspace

The Migration Framework defines the strategy, design, landing-zone, migration, and operations principles for Rehost workloads. This asset applies those principles to one concrete, runnable Spring Boot and PostgreSQL implementation on STACKIT.

The maintained repository is the source of truth for Terraform, Ansible, application artifacts, migration scripts, validation, and runtime configuration. This asset explains how to use that implementation without duplicating its complete source code.

Code & registry github.com STACKIT CMF Rehost Spring Boot repository Open the runnable Terraform and Ansible reference implementation for the Spring Boot and PostgreSQL Rehost path. Open the repository

This example represents an application Rehost path for a Spring Boot workload (Spring Music sample). The migration focus is the runtime relocation of the application to STACKIT:

  • Infrastructure Rehost: Terraform provisions one STACKIT VM, its boot volume, network, public IP, security group, SSH key, Observability resources, and an optional Server Backup schedule.
  • Application Rehost: Ansible installs Java, deploys the Spring Boot JAR, and manages it with systemd without changing the application runtime model.
  • Stateful Rehost: Ansible installs self-managed PostgreSQL on the same VM. The database engine is not changed to a managed service as part of this path.
  • Controlled migration: The repository provides separate rehearsal, cutover, validation, and rollback workflows with machine-readable evidence.

The validated baseline deliberately does not include an application load balancer, DNS switch, multiple VMs, Kubernetes, Cloud Foundry, or PostgreSQL Flex. Add those only as separately designed and tested extensions; they are not implied by this Rehost example.

  • Provisioning: Terraform resources for network, security, one VM, boot volume, SSH key, and public IP.
  • Configuration bridge: Terraform triggers Ansible after infrastructure provisioning.
  • Application deployment: Ansible deploys a concrete Spring Boot artifact and configures a systemd service.
  • VM-local database path: PostgreSQL installation, application role and database bootstrap, and a dump-based restore with ownership and data-integrity checks.
  • Operations baseline: Node exporter metrics are scraped by STACKIT Observability, and Terraform can enable daily Server Backup for the VM boot volume.
  • Migration controls: Source dump generation, independent restore validation, rehearsal, cutover, rollback verification, rollback execution, and evidence capture.
  • Deployable sample artifact: A ready-to-run JAR is included in the repository and used by default deployment settings.

To keep automation behavior consistent across CMF examples, use this common flag model in your Terraform variable set:

  • setup_project: create/use project context.
  • setup_observability: enable/disable observability resources.
  • setup_database: enable/disable optional VM-local PostgreSQL path.
  • setup_workload: enable/disable workload installation on VM.
  • setup_loadgen: enable/disable optional synthetic load generation.
  • setup_dns: accepted by the shared wrapper but unsupported in this baseline.

In the current Rehost repository, these map to existing switches (create_project, enable_observability, enable_local_postgresql, and load-generation related flags).

The following diagram shows the implemented and tested target, not a future high-availability variant.

Approved operator sourceSTACKIT ProjectUbuntu VMObservabilityBackup ArchiveSpring Boot JAR + systemdSelf-managed PostgreSQL restricted HTTP/SSHlocalhost SQLrestricted metrics scrapeboot-volume backup

Use an isolated Linux lab with Git, Terraform, Ansible, ShellCheck, SSH/SCP, curl, jq, and PostgreSQL server/client tools including pg_config. The sample dump scripts use runuser and the local postgres OS account and require root privileges in that lab. When Server Backup is enabled, the cutover workflow also needs an authenticated STACKIT CLI. Use the Terraform version that created an existing saved plan; plan files are version-specific.

Start in a parent directory without an existing checkout of the same name. This revision contains the migration workflows and declarative Observability management. Its JAR is unchanged from the artifact pinned by the Replatform reference:

Terminal window
umask 077 &&
git clone https://github.com/stackitcloud/stackit-cmf-Rehost-springboot.git &&
git -C stackit-cmf-Rehost-springboot checkout --detach b9225eb35c64b9ef4761c208fadb9a2356431793 &&
test -f stackit-cmf-Rehost-springboot/scripts/run_cutover.sh &&
cd stackit-cmf-Rehost-springboot &&
printf '%s\n' "Workspace ready. Continue from this Rehost checkout." || {
printf '%s\n' "Preparation failed. Resolve the error before continuing." >&2
false
}

Run the following steps from that checkout only after preparation succeeds. Preserve an existing checkout, state and evidence rather than replacing them. Keep credentials, plans, state, dumps, inventory and evidence private and outside version control; do not enable shell tracing.

Create the private variable file only when it does not already exist:

Terminal window
umask 077
test -e env.tfvars || cp env.tfvars.example env.tfvars
chmod 600 env.tfvars

Edit the file before planning. Configure the approved existing project or project-creation scope, service-account key path, SSH key pair, available image, availability zone, VM flavor and storage. Restrict SSH and application ingress to the source CIDRs actually observed on the target path. Verify the SSH host key through a trusted channel before migration scripts use strict host checking. Set SSH_KEY and SSH_USER for those scripts when they differ from ~/.ssh/id_rsa and ubuntu; keep them consistent with Terraform’s private_ssh_key_path and ssh_user.

Enable VM-local PostgreSQL, Observability and Server Backup for this walkthrough. For the initial runtime deployment, leave postgresql_restore_after_copy = false and the source dump path empty: provisioning must not import the source before rehearsal and cutover approval. Supply the database password through the approved secret mechanism as TF_VAR_postgresql_app_password and retain it securely for subsequent plans; do not put it in the variable file, shell history or documentation.

Confirm permissions, quota, cost approval and the private Terraform backend before initialization. This workflow assumes a prepared execution host, not a complete lab installation procedure.

Create a custom-format source dump and record its SHA-256 checksum, expected row count, and a workload-specific deterministic fingerprint. The repository includes a reproducible eight-row sample:

Terminal window
./scripts/create_source_dump.sh
./scripts/validate_source_dump.sh

The validation script restores into a separate local PostgreSQL cluster before the dump can enter a migration rehearsal. These scripts generate the bundled sample in temporary local clusters; they do not export a running application or the STACKIT VM. Use a fresh artifact directory and do not overwrite a dump already approved for migration. For a real source, export it under the agreed write-freeze procedure and record equivalent checksum, count and fingerprint evidence.

Configure an existing STACKIT project or project creation, restrict SSH and application ingress to approved source CIDRs, and pass the database password through TF_VAR_postgresql_app_password rather than a variable file. Review a saved plan before apply.

Terminal window
terraform init &&
./scripts/check.sh &&
terraform plan -input=false -var-file=env.tfvars -out=tfplan

Stop if initialization, checks or planning fail. Review resource changes, target project, ingress, cost and the disabled initial restore before explicitly applying that saved plan:

Terminal window
terraform apply tfplan

Terraform runs Ansible after provisioning. A successful apply proves completion of that orchestration, not acceptance of migrated data. Check the runtime before beginning rehearsal:

Terminal window
./scripts/validate_deployment.sh

Run the rehearsal against a temporary database on the target VM:

Terminal window
./scripts/run_migration_rehearsal.sh

The rehearsal checks the source evidence, restores the dump, compares row count and fingerprint, and removes its temporary database without changing the production springmusic database.

Set the expected source evidence in env.tfvars:

enable_local_postgresql = true
postgresql_source_dump_local_path = "artifacts/source-postgresql.dump"
postgresql_restore_after_copy = true
postgresql_expected_album_count = 8
postgresql_expected_album_fingerprint = "<source-fingerprint>"

Then execute the explicit approval gate:

Terminal window
./scripts/run_cutover.sh --confirm

The script requires a fully available Server Backup no older than 24 hours, creates a saved Terraform plan that replaces only the Ansible orchestration resource, rejects unrelated infrastructure changes, applies the restore, validates data and runtime behavior, and requires a final no-op Terraform plan. Retries of the same source-dump hash preserve the original database rollback point.

Validation checks the source row count and fingerprint, table ownership, a transactionally rolled-back write as the application role, application reachability, services, and the protected rollback dump.

Terminal window
./scripts/validate_migration.sh
./scripts/validate_deployment.sh
./scripts/verify_rollback.sh

If an approved rollback trigger is met during the rollback window, preserve the current target database and restore the original pre-cutover dump:

Terminal window
./scripts/rollback_postgresql.sh --confirm

With enable_server_backup = true, Terraform enables STACKIT Server Backup and a daily schedule for the VM boot volume. The default retention is 14 days. Cutover requires a fully available backup no older than 24 hours. Backup creation and status were validated against the real target; an in-place Server Backup restore remains a disruptive disaster-recovery operation and must be rehearsed in a separate recovery environment.

The Server Backup complements the PostgreSQL pre-restore dump. It does not replace database-level consistency checks or the application rollback procedure.

Follow workspace preparation, private target configuration, source evidence and reviewed provisioning in that order. Rehearse before the approved cutover and retain the acceptance evidence afterwards. The repository scripts invoke terraform explicitly; an OpenTofu-only execution host needs a separately reviewed adaptation, not just replacement of commands in this page.

The implementation provisions STACKIT Observability and scrapes node exporter. The Grafana provider manages the included dashboard in the SCF Rehost folder with the existing Thanos datasource, waiting for instance readiness instead of silently skipping dashboard creation.

Verify live VM, Spring Boot and PostgreSQL metrics through grafana_dashboard_url, then require a no-op plan. Seven panels cover the active baseline; the synthetic request panel is included only when the local load generator is enabled. Synthetic requests do not represent all application traffic. The deployment validation also checks exporter metrics and service health after apply and database restore.

Initial Grafana admin credentials remain a temporary authentication dependency. Protect state and saved plans. Notification receivers, alert-routing ownership, application logs and distributed tracing require separate configuration and acceptance before production use.

This example can be run in two valid setup modes.

  • With an existing Application Landing Zone: Reuse the already defined project context and service account setup from your landing zone implementation. Use the known project ID as deployment target (for example with create_project = false and project_id = "...") and the service account JSON configured for that landing zone scope.
  • Standalone without existing landing zone project: Let the example create a dedicated project. In this mode, set create_project = true and provide parent_container_id with an existing folder/container where the service account has sufficient permissions.

For landing-zone design guidance, see Application Landing Zone.

Use this as a minimal starting point in env.tfvars.

create_project = true
target_project_name = "cmf-rehost-springboot"
target_project_owner_email = "owner@example.com"
parent_container_id = "cmf-xxxxxxxx"
service_account_key_path = "~/.ssh/cmf-sa.json"
public_ssh_key_path = "~/.ssh/id_rsa.pub"
private_ssh_key_path = "~/.ssh/id_rsa"
availability_zone = "eu01-1"
machine_type = "g2i.2"
ssh_allowed_cidr = "203.0.113.10/32"
app_allowed_cidr = "203.0.113.10/32"
enable_local_postgresql = true
enable_observability = true
enable_server_backup = true
  • Default artifact path: ansible/files/springboot-app.jar
  • Default variable: jar_local_path points to the same file.
  • Alternative artifact: Replace the file or override jar_local_path.

When the artifact changes, Terraform detects the checksum difference and re-runs the Ansible deployment step.

Every rehearsal, cutover, and rollback writes an evidence.env file below artifacts/evidence/<timestamp>-<mode>/. Accept a cutover only when the evidence records the expected checksum, row count, fingerprint, target VM, successful runtime validation, and final Terraform no-op.

Terminal window
./scripts/validate_migration.sh
./scripts/validate_deployment.sh
terraform plan -var-file=env.tfvars -detailed-exitcode

Use the same approved source path, restore flag, expected count and fingerprint as the completed cutover. Exit code 0 means no changes, 2 means changes are proposed, and 1 means an error. A plan alone is not proof of apply. The cutover workflow records status=passed and terraform_noop=true only after applying its saved plan and completing data and runtime validation.

  • Prepare: Approve the target design, access paths, source evidence and recovery responsibilities.
  • Provision: Review and apply the infrastructure plan, then verify the VM runtime independently.
  • Rehearse: Restore into an isolated database and verify integrity without changing the application database.
  • Cut over: Freeze source writes, approve the final dump and allow only the intended orchestration change.
  • Accept or recover: Require data, runtime and no-op evidence; invoke the agreed rollback before its deadline when acceptance fails.
  • Handover: Transfer monitoring, backup ownership, evidence and source-retention decisions before optimization.

The Replatform reference reuses this repository’s pinned Spring Music JAR and sample-data generator. Its sample walkthrough therefore needs this checkout and validated dump artifacts, but not a newly provisioned Rehost VM. Do not create another VM solely to generate the local sample.

For an actual migration from the Rehost VM to SKE and PostgreSQL Flex, verify that the VM is currently healthy, freeze its application writes, export its real PostgreSQL database and qualify that export. A historical successful Rehost apply does not establish current reachability or make the local sample equivalent to live VM data. Source export, acceptance and source failback need their own approved procedure.

  • Keep operational procedures aligned with the runbook and migration governance.
  • For production usage, adapt security controls, sizing, image selection, and life cycle automation.
SAFE

Configure Private Target Inputs

The Migration Framework defines the strategy, design, landing-zone, migration, and operations principles for Rehost workloads. This asset applies those principles to one concrete, runnable Spring Boot and PostgreSQL implementation on STACKIT.

The maintained repository is the source of truth for Terraform, Ansible, application artifacts, migration scripts, validation, and runtime configuration. This asset explains how to use that implementation without duplicating its complete source code.

Code & registry github.com STACKIT CMF Rehost Spring Boot repository Open the runnable Terraform and Ansible reference implementation for the Spring Boot and PostgreSQL Rehost path. Open the repository

This example represents an application Rehost path for a Spring Boot workload (Spring Music sample). The migration focus is the runtime relocation of the application to STACKIT:

  • Infrastructure Rehost: Terraform provisions one STACKIT VM, its boot volume, network, public IP, security group, SSH key, Observability resources, and an optional Server Backup schedule.
  • Application Rehost: Ansible installs Java, deploys the Spring Boot JAR, and manages it with systemd without changing the application runtime model.
  • Stateful Rehost: Ansible installs self-managed PostgreSQL on the same VM. The database engine is not changed to a managed service as part of this path.
  • Controlled migration: The repository provides separate rehearsal, cutover, validation, and rollback workflows with machine-readable evidence.

The validated baseline deliberately does not include an application load balancer, DNS switch, multiple VMs, Kubernetes, Cloud Foundry, or PostgreSQL Flex. Add those only as separately designed and tested extensions; they are not implied by this Rehost example.

  • Provisioning: Terraform resources for network, security, one VM, boot volume, SSH key, and public IP.
  • Configuration bridge: Terraform triggers Ansible after infrastructure provisioning.
  • Application deployment: Ansible deploys a concrete Spring Boot artifact and configures a systemd service.
  • VM-local database path: PostgreSQL installation, application role and database bootstrap, and a dump-based restore with ownership and data-integrity checks.
  • Operations baseline: Node exporter metrics are scraped by STACKIT Observability, and Terraform can enable daily Server Backup for the VM boot volume.
  • Migration controls: Source dump generation, independent restore validation, rehearsal, cutover, rollback verification, rollback execution, and evidence capture.
  • Deployable sample artifact: A ready-to-run JAR is included in the repository and used by default deployment settings.

To keep automation behavior consistent across CMF examples, use this common flag model in your Terraform variable set:

  • setup_project: create/use project context.
  • setup_observability: enable/disable observability resources.
  • setup_database: enable/disable optional VM-local PostgreSQL path.
  • setup_workload: enable/disable workload installation on VM.
  • setup_loadgen: enable/disable optional synthetic load generation.
  • setup_dns: accepted by the shared wrapper but unsupported in this baseline.

In the current Rehost repository, these map to existing switches (create_project, enable_observability, enable_local_postgresql, and load-generation related flags).

The following diagram shows the implemented and tested target, not a future high-availability variant.

Approved operator sourceSTACKIT ProjectUbuntu VMObservabilityBackup ArchiveSpring Boot JAR + systemdSelf-managed PostgreSQL restricted HTTP/SSHlocalhost SQLrestricted metrics scrapeboot-volume backup

Use an isolated Linux lab with Git, Terraform, Ansible, ShellCheck, SSH/SCP, curl, jq, and PostgreSQL server/client tools including pg_config. The sample dump scripts use runuser and the local postgres OS account and require root privileges in that lab. When Server Backup is enabled, the cutover workflow also needs an authenticated STACKIT CLI. Use the Terraform version that created an existing saved plan; plan files are version-specific.

Start in a parent directory without an existing checkout of the same name. This revision contains the migration workflows and declarative Observability management. Its JAR is unchanged from the artifact pinned by the Replatform reference:

Terminal window
umask 077 &&
git clone https://github.com/stackitcloud/stackit-cmf-Rehost-springboot.git &&
git -C stackit-cmf-Rehost-springboot checkout --detach b9225eb35c64b9ef4761c208fadb9a2356431793 &&
test -f stackit-cmf-Rehost-springboot/scripts/run_cutover.sh &&
cd stackit-cmf-Rehost-springboot &&
printf '%s\n' "Workspace ready. Continue from this Rehost checkout." || {
printf '%s\n' "Preparation failed. Resolve the error before continuing." >&2
false
}

Run the following steps from that checkout only after preparation succeeds. Preserve an existing checkout, state and evidence rather than replacing them. Keep credentials, plans, state, dumps, inventory and evidence private and outside version control; do not enable shell tracing.

Create the private variable file only when it does not already exist:

Terminal window
umask 077
test -e env.tfvars || cp env.tfvars.example env.tfvars
chmod 600 env.tfvars

Edit the file before planning. Configure the approved existing project or project-creation scope, service-account key path, SSH key pair, available image, availability zone, VM flavor and storage. Restrict SSH and application ingress to the source CIDRs actually observed on the target path. Verify the SSH host key through a trusted channel before migration scripts use strict host checking. Set SSH_KEY and SSH_USER for those scripts when they differ from ~/.ssh/id_rsa and ubuntu; keep them consistent with Terraform’s private_ssh_key_path and ssh_user.

Enable VM-local PostgreSQL, Observability and Server Backup for this walkthrough. For the initial runtime deployment, leave postgresql_restore_after_copy = false and the source dump path empty: provisioning must not import the source before rehearsal and cutover approval. Supply the database password through the approved secret mechanism as TF_VAR_postgresql_app_password and retain it securely for subsequent plans; do not put it in the variable file, shell history or documentation.

Confirm permissions, quota, cost approval and the private Terraform backend before initialization. This workflow assumes a prepared execution host, not a complete lab installation procedure.

Create a custom-format source dump and record its SHA-256 checksum, expected row count, and a workload-specific deterministic fingerprint. The repository includes a reproducible eight-row sample:

Terminal window
./scripts/create_source_dump.sh
./scripts/validate_source_dump.sh

The validation script restores into a separate local PostgreSQL cluster before the dump can enter a migration rehearsal. These scripts generate the bundled sample in temporary local clusters; they do not export a running application or the STACKIT VM. Use a fresh artifact directory and do not overwrite a dump already approved for migration. For a real source, export it under the agreed write-freeze procedure and record equivalent checksum, count and fingerprint evidence.

Configure an existing STACKIT project or project creation, restrict SSH and application ingress to approved source CIDRs, and pass the database password through TF_VAR_postgresql_app_password rather than a variable file. Review a saved plan before apply.

Terminal window
terraform init &&
./scripts/check.sh &&
terraform plan -input=false -var-file=env.tfvars -out=tfplan

Stop if initialization, checks or planning fail. Review resource changes, target project, ingress, cost and the disabled initial restore before explicitly applying that saved plan:

Terminal window
terraform apply tfplan

Terraform runs Ansible after provisioning. A successful apply proves completion of that orchestration, not acceptance of migrated data. Check the runtime before beginning rehearsal:

Terminal window
./scripts/validate_deployment.sh

Run the rehearsal against a temporary database on the target VM:

Terminal window
./scripts/run_migration_rehearsal.sh

The rehearsal checks the source evidence, restores the dump, compares row count and fingerprint, and removes its temporary database without changing the production springmusic database.

Set the expected source evidence in env.tfvars:

enable_local_postgresql = true
postgresql_source_dump_local_path = "artifacts/source-postgresql.dump"
postgresql_restore_after_copy = true
postgresql_expected_album_count = 8
postgresql_expected_album_fingerprint = "<source-fingerprint>"

Then execute the explicit approval gate:

Terminal window
./scripts/run_cutover.sh --confirm

The script requires a fully available Server Backup no older than 24 hours, creates a saved Terraform plan that replaces only the Ansible orchestration resource, rejects unrelated infrastructure changes, applies the restore, validates data and runtime behavior, and requires a final no-op Terraform plan. Retries of the same source-dump hash preserve the original database rollback point.

Validation checks the source row count and fingerprint, table ownership, a transactionally rolled-back write as the application role, application reachability, services, and the protected rollback dump.

Terminal window
./scripts/validate_migration.sh
./scripts/validate_deployment.sh
./scripts/verify_rollback.sh

If an approved rollback trigger is met during the rollback window, preserve the current target database and restore the original pre-cutover dump:

Terminal window
./scripts/rollback_postgresql.sh --confirm

With enable_server_backup = true, Terraform enables STACKIT Server Backup and a daily schedule for the VM boot volume. The default retention is 14 days. Cutover requires a fully available backup no older than 24 hours. Backup creation and status were validated against the real target; an in-place Server Backup restore remains a disruptive disaster-recovery operation and must be rehearsed in a separate recovery environment.

The Server Backup complements the PostgreSQL pre-restore dump. It does not replace database-level consistency checks or the application rollback procedure.

Follow workspace preparation, private target configuration, source evidence and reviewed provisioning in that order. Rehearse before the approved cutover and retain the acceptance evidence afterwards. The repository scripts invoke terraform explicitly; an OpenTofu-only execution host needs a separately reviewed adaptation, not just replacement of commands in this page.

The implementation provisions STACKIT Observability and scrapes node exporter. The Grafana provider manages the included dashboard in the SCF Rehost folder with the existing Thanos datasource, waiting for instance readiness instead of silently skipping dashboard creation.

Verify live VM, Spring Boot and PostgreSQL metrics through grafana_dashboard_url, then require a no-op plan. Seven panels cover the active baseline; the synthetic request panel is included only when the local load generator is enabled. Synthetic requests do not represent all application traffic. The deployment validation also checks exporter metrics and service health after apply and database restore.

Initial Grafana admin credentials remain a temporary authentication dependency. Protect state and saved plans. Notification receivers, alert-routing ownership, application logs and distributed tracing require separate configuration and acceptance before production use.

This example can be run in two valid setup modes.

  • With an existing Application Landing Zone: Reuse the already defined project context and service account setup from your landing zone implementation. Use the known project ID as deployment target (for example with create_project = false and project_id = "...") and the service account JSON configured for that landing zone scope.
  • Standalone without existing landing zone project: Let the example create a dedicated project. In this mode, set create_project = true and provide parent_container_id with an existing folder/container where the service account has sufficient permissions.

For landing-zone design guidance, see Application Landing Zone.

Use this as a minimal starting point in env.tfvars.

create_project = true
target_project_name = "cmf-rehost-springboot"
target_project_owner_email = "owner@example.com"
parent_container_id = "cmf-xxxxxxxx"
service_account_key_path = "~/.ssh/cmf-sa.json"
public_ssh_key_path = "~/.ssh/id_rsa.pub"
private_ssh_key_path = "~/.ssh/id_rsa"
availability_zone = "eu01-1"
machine_type = "g2i.2"
ssh_allowed_cidr = "203.0.113.10/32"
app_allowed_cidr = "203.0.113.10/32"
enable_local_postgresql = true
enable_observability = true
enable_server_backup = true
  • Default artifact path: ansible/files/springboot-app.jar
  • Default variable: jar_local_path points to the same file.
  • Alternative artifact: Replace the file or override jar_local_path.

When the artifact changes, Terraform detects the checksum difference and re-runs the Ansible deployment step.

Every rehearsal, cutover, and rollback writes an evidence.env file below artifacts/evidence/<timestamp>-<mode>/. Accept a cutover only when the evidence records the expected checksum, row count, fingerprint, target VM, successful runtime validation, and final Terraform no-op.

Terminal window
./scripts/validate_migration.sh
./scripts/validate_deployment.sh
terraform plan -var-file=env.tfvars -detailed-exitcode

Use the same approved source path, restore flag, expected count and fingerprint as the completed cutover. Exit code 0 means no changes, 2 means changes are proposed, and 1 means an error. A plan alone is not proof of apply. The cutover workflow records status=passed and terraform_noop=true only after applying its saved plan and completing data and runtime validation.

  • Prepare: Approve the target design, access paths, source evidence and recovery responsibilities.
  • Provision: Review and apply the infrastructure plan, then verify the VM runtime independently.
  • Rehearse: Restore into an isolated database and verify integrity without changing the application database.
  • Cut over: Freeze source writes, approve the final dump and allow only the intended orchestration change.
  • Accept or recover: Require data, runtime and no-op evidence; invoke the agreed rollback before its deadline when acceptance fails.
  • Handover: Transfer monitoring, backup ownership, evidence and source-retention decisions before optimization.

The Replatform reference reuses this repository’s pinned Spring Music JAR and sample-data generator. Its sample walkthrough therefore needs this checkout and validated dump artifacts, but not a newly provisioned Rehost VM. Do not create another VM solely to generate the local sample.

For an actual migration from the Rehost VM to SKE and PostgreSQL Flex, verify that the VM is currently healthy, freeze its application writes, export its real PostgreSQL database and qualify that export. A historical successful Rehost apply does not establish current reachability or make the local sample equivalent to live VM data. Source export, acceptance and source failback need their own approved procedure.

  • Keep operational procedures aligned with the runbook and migration governance.
  • For production usage, adapt security controls, sizing, image selection, and life cycle automation.
SAFE

Prepare Source Evidence

The Migration Framework defines the strategy, design, landing-zone, migration, and operations principles for Rehost workloads. This asset applies those principles to one concrete, runnable Spring Boot and PostgreSQL implementation on STACKIT.

The maintained repository is the source of truth for Terraform, Ansible, application artifacts, migration scripts, validation, and runtime configuration. This asset explains how to use that implementation without duplicating its complete source code.

Code & registry github.com STACKIT CMF Rehost Spring Boot repository Open the runnable Terraform and Ansible reference implementation for the Spring Boot and PostgreSQL Rehost path. Open the repository

This example represents an application Rehost path for a Spring Boot workload (Spring Music sample). The migration focus is the runtime relocation of the application to STACKIT:

  • Infrastructure Rehost: Terraform provisions one STACKIT VM, its boot volume, network, public IP, security group, SSH key, Observability resources, and an optional Server Backup schedule.
  • Application Rehost: Ansible installs Java, deploys the Spring Boot JAR, and manages it with systemd without changing the application runtime model.
  • Stateful Rehost: Ansible installs self-managed PostgreSQL on the same VM. The database engine is not changed to a managed service as part of this path.
  • Controlled migration: The repository provides separate rehearsal, cutover, validation, and rollback workflows with machine-readable evidence.

The validated baseline deliberately does not include an application load balancer, DNS switch, multiple VMs, Kubernetes, Cloud Foundry, or PostgreSQL Flex. Add those only as separately designed and tested extensions; they are not implied by this Rehost example.

  • Provisioning: Terraform resources for network, security, one VM, boot volume, SSH key, and public IP.
  • Configuration bridge: Terraform triggers Ansible after infrastructure provisioning.
  • Application deployment: Ansible deploys a concrete Spring Boot artifact and configures a systemd service.
  • VM-local database path: PostgreSQL installation, application role and database bootstrap, and a dump-based restore with ownership and data-integrity checks.
  • Operations baseline: Node exporter metrics are scraped by STACKIT Observability, and Terraform can enable daily Server Backup for the VM boot volume.
  • Migration controls: Source dump generation, independent restore validation, rehearsal, cutover, rollback verification, rollback execution, and evidence capture.
  • Deployable sample artifact: A ready-to-run JAR is included in the repository and used by default deployment settings.

To keep automation behavior consistent across CMF examples, use this common flag model in your Terraform variable set:

  • setup_project: create/use project context.
  • setup_observability: enable/disable observability resources.
  • setup_database: enable/disable optional VM-local PostgreSQL path.
  • setup_workload: enable/disable workload installation on VM.
  • setup_loadgen: enable/disable optional synthetic load generation.
  • setup_dns: accepted by the shared wrapper but unsupported in this baseline.

In the current Rehost repository, these map to existing switches (create_project, enable_observability, enable_local_postgresql, and load-generation related flags).

The following diagram shows the implemented and tested target, not a future high-availability variant.

Approved operator sourceSTACKIT ProjectUbuntu VMObservabilityBackup ArchiveSpring Boot JAR + systemdSelf-managed PostgreSQL restricted HTTP/SSHlocalhost SQLrestricted metrics scrapeboot-volume backup

Use an isolated Linux lab with Git, Terraform, Ansible, ShellCheck, SSH/SCP, curl, jq, and PostgreSQL server/client tools including pg_config. The sample dump scripts use runuser and the local postgres OS account and require root privileges in that lab. When Server Backup is enabled, the cutover workflow also needs an authenticated STACKIT CLI. Use the Terraform version that created an existing saved plan; plan files are version-specific.

Start in a parent directory without an existing checkout of the same name. This revision contains the migration workflows and declarative Observability management. Its JAR is unchanged from the artifact pinned by the Replatform reference:

Terminal window
umask 077 &&
git clone https://github.com/stackitcloud/stackit-cmf-Rehost-springboot.git &&
git -C stackit-cmf-Rehost-springboot checkout --detach b9225eb35c64b9ef4761c208fadb9a2356431793 &&
test -f stackit-cmf-Rehost-springboot/scripts/run_cutover.sh &&
cd stackit-cmf-Rehost-springboot &&
printf '%s\n' "Workspace ready. Continue from this Rehost checkout." || {
printf '%s\n' "Preparation failed. Resolve the error before continuing." >&2
false
}

Run the following steps from that checkout only after preparation succeeds. Preserve an existing checkout, state and evidence rather than replacing them. Keep credentials, plans, state, dumps, inventory and evidence private and outside version control; do not enable shell tracing.

Create the private variable file only when it does not already exist:

Terminal window
umask 077
test -e env.tfvars || cp env.tfvars.example env.tfvars
chmod 600 env.tfvars

Edit the file before planning. Configure the approved existing project or project-creation scope, service-account key path, SSH key pair, available image, availability zone, VM flavor and storage. Restrict SSH and application ingress to the source CIDRs actually observed on the target path. Verify the SSH host key through a trusted channel before migration scripts use strict host checking. Set SSH_KEY and SSH_USER for those scripts when they differ from ~/.ssh/id_rsa and ubuntu; keep them consistent with Terraform’s private_ssh_key_path and ssh_user.

Enable VM-local PostgreSQL, Observability and Server Backup for this walkthrough. For the initial runtime deployment, leave postgresql_restore_after_copy = false and the source dump path empty: provisioning must not import the source before rehearsal and cutover approval. Supply the database password through the approved secret mechanism as TF_VAR_postgresql_app_password and retain it securely for subsequent plans; do not put it in the variable file, shell history or documentation.

Confirm permissions, quota, cost approval and the private Terraform backend before initialization. This workflow assumes a prepared execution host, not a complete lab installation procedure.

Create a custom-format source dump and record its SHA-256 checksum, expected row count, and a workload-specific deterministic fingerprint. The repository includes a reproducible eight-row sample:

Terminal window
./scripts/create_source_dump.sh
./scripts/validate_source_dump.sh

The validation script restores into a separate local PostgreSQL cluster before the dump can enter a migration rehearsal. These scripts generate the bundled sample in temporary local clusters; they do not export a running application or the STACKIT VM. Use a fresh artifact directory and do not overwrite a dump already approved for migration. For a real source, export it under the agreed write-freeze procedure and record equivalent checksum, count and fingerprint evidence.

Configure an existing STACKIT project or project creation, restrict SSH and application ingress to approved source CIDRs, and pass the database password through TF_VAR_postgresql_app_password rather than a variable file. Review a saved plan before apply.

Terminal window
terraform init &&
./scripts/check.sh &&
terraform plan -input=false -var-file=env.tfvars -out=tfplan

Stop if initialization, checks or planning fail. Review resource changes, target project, ingress, cost and the disabled initial restore before explicitly applying that saved plan:

Terminal window
terraform apply tfplan

Terraform runs Ansible after provisioning. A successful apply proves completion of that orchestration, not acceptance of migrated data. Check the runtime before beginning rehearsal:

Terminal window
./scripts/validate_deployment.sh

Run the rehearsal against a temporary database on the target VM:

Terminal window
./scripts/run_migration_rehearsal.sh

The rehearsal checks the source evidence, restores the dump, compares row count and fingerprint, and removes its temporary database without changing the production springmusic database.

Set the expected source evidence in env.tfvars:

enable_local_postgresql = true
postgresql_source_dump_local_path = "artifacts/source-postgresql.dump"
postgresql_restore_after_copy = true
postgresql_expected_album_count = 8
postgresql_expected_album_fingerprint = "<source-fingerprint>"

Then execute the explicit approval gate:

Terminal window
./scripts/run_cutover.sh --confirm

The script requires a fully available Server Backup no older than 24 hours, creates a saved Terraform plan that replaces only the Ansible orchestration resource, rejects unrelated infrastructure changes, applies the restore, validates data and runtime behavior, and requires a final no-op Terraform plan. Retries of the same source-dump hash preserve the original database rollback point.

Validation checks the source row count and fingerprint, table ownership, a transactionally rolled-back write as the application role, application reachability, services, and the protected rollback dump.

Terminal window
./scripts/validate_migration.sh
./scripts/validate_deployment.sh
./scripts/verify_rollback.sh

If an approved rollback trigger is met during the rollback window, preserve the current target database and restore the original pre-cutover dump:

Terminal window
./scripts/rollback_postgresql.sh --confirm

With enable_server_backup = true, Terraform enables STACKIT Server Backup and a daily schedule for the VM boot volume. The default retention is 14 days. Cutover requires a fully available backup no older than 24 hours. Backup creation and status were validated against the real target; an in-place Server Backup restore remains a disruptive disaster-recovery operation and must be rehearsed in a separate recovery environment.

The Server Backup complements the PostgreSQL pre-restore dump. It does not replace database-level consistency checks or the application rollback procedure.

Follow workspace preparation, private target configuration, source evidence and reviewed provisioning in that order. Rehearse before the approved cutover and retain the acceptance evidence afterwards. The repository scripts invoke terraform explicitly; an OpenTofu-only execution host needs a separately reviewed adaptation, not just replacement of commands in this page.

The implementation provisions STACKIT Observability and scrapes node exporter. The Grafana provider manages the included dashboard in the SCF Rehost folder with the existing Thanos datasource, waiting for instance readiness instead of silently skipping dashboard creation.

Verify live VM, Spring Boot and PostgreSQL metrics through grafana_dashboard_url, then require a no-op plan. Seven panels cover the active baseline; the synthetic request panel is included only when the local load generator is enabled. Synthetic requests do not represent all application traffic. The deployment validation also checks exporter metrics and service health after apply and database restore.

Initial Grafana admin credentials remain a temporary authentication dependency. Protect state and saved plans. Notification receivers, alert-routing ownership, application logs and distributed tracing require separate configuration and acceptance before production use.

This example can be run in two valid setup modes.

  • With an existing Application Landing Zone: Reuse the already defined project context and service account setup from your landing zone implementation. Use the known project ID as deployment target (for example with create_project = false and project_id = "...") and the service account JSON configured for that landing zone scope.
  • Standalone without existing landing zone project: Let the example create a dedicated project. In this mode, set create_project = true and provide parent_container_id with an existing folder/container where the service account has sufficient permissions.

For landing-zone design guidance, see Application Landing Zone.

Use this as a minimal starting point in env.tfvars.

create_project = true
target_project_name = "cmf-rehost-springboot"
target_project_owner_email = "owner@example.com"
parent_container_id = "cmf-xxxxxxxx"
service_account_key_path = "~/.ssh/cmf-sa.json"
public_ssh_key_path = "~/.ssh/id_rsa.pub"
private_ssh_key_path = "~/.ssh/id_rsa"
availability_zone = "eu01-1"
machine_type = "g2i.2"
ssh_allowed_cidr = "203.0.113.10/32"
app_allowed_cidr = "203.0.113.10/32"
enable_local_postgresql = true
enable_observability = true
enable_server_backup = true
  • Default artifact path: ansible/files/springboot-app.jar
  • Default variable: jar_local_path points to the same file.
  • Alternative artifact: Replace the file or override jar_local_path.

When the artifact changes, Terraform detects the checksum difference and re-runs the Ansible deployment step.

Every rehearsal, cutover, and rollback writes an evidence.env file below artifacts/evidence/<timestamp>-<mode>/. Accept a cutover only when the evidence records the expected checksum, row count, fingerprint, target VM, successful runtime validation, and final Terraform no-op.

Terminal window
./scripts/validate_migration.sh
./scripts/validate_deployment.sh
terraform plan -var-file=env.tfvars -detailed-exitcode

Use the same approved source path, restore flag, expected count and fingerprint as the completed cutover. Exit code 0 means no changes, 2 means changes are proposed, and 1 means an error. A plan alone is not proof of apply. The cutover workflow records status=passed and terraform_noop=true only after applying its saved plan and completing data and runtime validation.

  • Prepare: Approve the target design, access paths, source evidence and recovery responsibilities.
  • Provision: Review and apply the infrastructure plan, then verify the VM runtime independently.
  • Rehearse: Restore into an isolated database and verify integrity without changing the application database.
  • Cut over: Freeze source writes, approve the final dump and allow only the intended orchestration change.
  • Accept or recover: Require data, runtime and no-op evidence; invoke the agreed rollback before its deadline when acceptance fails.
  • Handover: Transfer monitoring, backup ownership, evidence and source-retention decisions before optimization.

The Replatform reference reuses this repository’s pinned Spring Music JAR and sample-data generator. Its sample walkthrough therefore needs this checkout and validated dump artifacts, but not a newly provisioned Rehost VM. Do not create another VM solely to generate the local sample.

For an actual migration from the Rehost VM to SKE and PostgreSQL Flex, verify that the VM is currently healthy, freeze its application writes, export its real PostgreSQL database and qualify that export. A historical successful Rehost apply does not establish current reachability or make the local sample equivalent to live VM data. Source export, acceptance and source failback need their own approved procedure.

  • Keep operational procedures aligned with the runbook and migration governance.
  • For production usage, adapt security controls, sizing, image selection, and life cycle automation.
BASE

Provision the Target

The Migration Framework defines the strategy, design, landing-zone, migration, and operations principles for Rehost workloads. This asset applies those principles to one concrete, runnable Spring Boot and PostgreSQL implementation on STACKIT.

The maintained repository is the source of truth for Terraform, Ansible, application artifacts, migration scripts, validation, and runtime configuration. This asset explains how to use that implementation without duplicating its complete source code.

Code & registry github.com STACKIT CMF Rehost Spring Boot repository Open the runnable Terraform and Ansible reference implementation for the Spring Boot and PostgreSQL Rehost path. Open the repository

This example represents an application Rehost path for a Spring Boot workload (Spring Music sample). The migration focus is the runtime relocation of the application to STACKIT:

  • Infrastructure Rehost: Terraform provisions one STACKIT VM, its boot volume, network, public IP, security group, SSH key, Observability resources, and an optional Server Backup schedule.
  • Application Rehost: Ansible installs Java, deploys the Spring Boot JAR, and manages it with systemd without changing the application runtime model.
  • Stateful Rehost: Ansible installs self-managed PostgreSQL on the same VM. The database engine is not changed to a managed service as part of this path.
  • Controlled migration: The repository provides separate rehearsal, cutover, validation, and rollback workflows with machine-readable evidence.

The validated baseline deliberately does not include an application load balancer, DNS switch, multiple VMs, Kubernetes, Cloud Foundry, or PostgreSQL Flex. Add those only as separately designed and tested extensions; they are not implied by this Rehost example.

  • Provisioning: Terraform resources for network, security, one VM, boot volume, SSH key, and public IP.
  • Configuration bridge: Terraform triggers Ansible after infrastructure provisioning.
  • Application deployment: Ansible deploys a concrete Spring Boot artifact and configures a systemd service.
  • VM-local database path: PostgreSQL installation, application role and database bootstrap, and a dump-based restore with ownership and data-integrity checks.
  • Operations baseline: Node exporter metrics are scraped by STACKIT Observability, and Terraform can enable daily Server Backup for the VM boot volume.
  • Migration controls: Source dump generation, independent restore validation, rehearsal, cutover, rollback verification, rollback execution, and evidence capture.
  • Deployable sample artifact: A ready-to-run JAR is included in the repository and used by default deployment settings.

To keep automation behavior consistent across CMF examples, use this common flag model in your Terraform variable set:

  • setup_project: create/use project context.
  • setup_observability: enable/disable observability resources.
  • setup_database: enable/disable optional VM-local PostgreSQL path.
  • setup_workload: enable/disable workload installation on VM.
  • setup_loadgen: enable/disable optional synthetic load generation.
  • setup_dns: accepted by the shared wrapper but unsupported in this baseline.

In the current Rehost repository, these map to existing switches (create_project, enable_observability, enable_local_postgresql, and load-generation related flags).

The following diagram shows the implemented and tested target, not a future high-availability variant.

Approved operator sourceSTACKIT ProjectUbuntu VMObservabilityBackup ArchiveSpring Boot JAR + systemdSelf-managed PostgreSQL restricted HTTP/SSHlocalhost SQLrestricted metrics scrapeboot-volume backup

Use an isolated Linux lab with Git, Terraform, Ansible, ShellCheck, SSH/SCP, curl, jq, and PostgreSQL server/client tools including pg_config. The sample dump scripts use runuser and the local postgres OS account and require root privileges in that lab. When Server Backup is enabled, the cutover workflow also needs an authenticated STACKIT CLI. Use the Terraform version that created an existing saved plan; plan files are version-specific.

Start in a parent directory without an existing checkout of the same name. This revision contains the migration workflows and declarative Observability management. Its JAR is unchanged from the artifact pinned by the Replatform reference:

Terminal window
umask 077 &&
git clone https://github.com/stackitcloud/stackit-cmf-Rehost-springboot.git &&
git -C stackit-cmf-Rehost-springboot checkout --detach b9225eb35c64b9ef4761c208fadb9a2356431793 &&
test -f stackit-cmf-Rehost-springboot/scripts/run_cutover.sh &&
cd stackit-cmf-Rehost-springboot &&
printf '%s\n' "Workspace ready. Continue from this Rehost checkout." || {
printf '%s\n' "Preparation failed. Resolve the error before continuing." >&2
false
}

Run the following steps from that checkout only after preparation succeeds. Preserve an existing checkout, state and evidence rather than replacing them. Keep credentials, plans, state, dumps, inventory and evidence private and outside version control; do not enable shell tracing.

Create the private variable file only when it does not already exist:

Terminal window
umask 077
test -e env.tfvars || cp env.tfvars.example env.tfvars
chmod 600 env.tfvars

Edit the file before planning. Configure the approved existing project or project-creation scope, service-account key path, SSH key pair, available image, availability zone, VM flavor and storage. Restrict SSH and application ingress to the source CIDRs actually observed on the target path. Verify the SSH host key through a trusted channel before migration scripts use strict host checking. Set SSH_KEY and SSH_USER for those scripts when they differ from ~/.ssh/id_rsa and ubuntu; keep them consistent with Terraform’s private_ssh_key_path and ssh_user.

Enable VM-local PostgreSQL, Observability and Server Backup for this walkthrough. For the initial runtime deployment, leave postgresql_restore_after_copy = false and the source dump path empty: provisioning must not import the source before rehearsal and cutover approval. Supply the database password through the approved secret mechanism as TF_VAR_postgresql_app_password and retain it securely for subsequent plans; do not put it in the variable file, shell history or documentation.

Confirm permissions, quota, cost approval and the private Terraform backend before initialization. This workflow assumes a prepared execution host, not a complete lab installation procedure.

Create a custom-format source dump and record its SHA-256 checksum, expected row count, and a workload-specific deterministic fingerprint. The repository includes a reproducible eight-row sample:

Terminal window
./scripts/create_source_dump.sh
./scripts/validate_source_dump.sh

The validation script restores into a separate local PostgreSQL cluster before the dump can enter a migration rehearsal. These scripts generate the bundled sample in temporary local clusters; they do not export a running application or the STACKIT VM. Use a fresh artifact directory and do not overwrite a dump already approved for migration. For a real source, export it under the agreed write-freeze procedure and record equivalent checksum, count and fingerprint evidence.

Configure an existing STACKIT project or project creation, restrict SSH and application ingress to approved source CIDRs, and pass the database password through TF_VAR_postgresql_app_password rather than a variable file. Review a saved plan before apply.

Terminal window
terraform init &&
./scripts/check.sh &&
terraform plan -input=false -var-file=env.tfvars -out=tfplan

Stop if initialization, checks or planning fail. Review resource changes, target project, ingress, cost and the disabled initial restore before explicitly applying that saved plan:

Terminal window
terraform apply tfplan

Terraform runs Ansible after provisioning. A successful apply proves completion of that orchestration, not acceptance of migrated data. Check the runtime before beginning rehearsal:

Terminal window
./scripts/validate_deployment.sh

Run the rehearsal against a temporary database on the target VM:

Terminal window
./scripts/run_migration_rehearsal.sh

The rehearsal checks the source evidence, restores the dump, compares row count and fingerprint, and removes its temporary database without changing the production springmusic database.

Set the expected source evidence in env.tfvars:

enable_local_postgresql = true
postgresql_source_dump_local_path = "artifacts/source-postgresql.dump"
postgresql_restore_after_copy = true
postgresql_expected_album_count = 8
postgresql_expected_album_fingerprint = "<source-fingerprint>"

Then execute the explicit approval gate:

Terminal window
./scripts/run_cutover.sh --confirm

The script requires a fully available Server Backup no older than 24 hours, creates a saved Terraform plan that replaces only the Ansible orchestration resource, rejects unrelated infrastructure changes, applies the restore, validates data and runtime behavior, and requires a final no-op Terraform plan. Retries of the same source-dump hash preserve the original database rollback point.

Validation checks the source row count and fingerprint, table ownership, a transactionally rolled-back write as the application role, application reachability, services, and the protected rollback dump.

Terminal window
./scripts/validate_migration.sh
./scripts/validate_deployment.sh
./scripts/verify_rollback.sh

If an approved rollback trigger is met during the rollback window, preserve the current target database and restore the original pre-cutover dump:

Terminal window
./scripts/rollback_postgresql.sh --confirm

With enable_server_backup = true, Terraform enables STACKIT Server Backup and a daily schedule for the VM boot volume. The default retention is 14 days. Cutover requires a fully available backup no older than 24 hours. Backup creation and status were validated against the real target; an in-place Server Backup restore remains a disruptive disaster-recovery operation and must be rehearsed in a separate recovery environment.

The Server Backup complements the PostgreSQL pre-restore dump. It does not replace database-level consistency checks or the application rollback procedure.

Follow workspace preparation, private target configuration, source evidence and reviewed provisioning in that order. Rehearse before the approved cutover and retain the acceptance evidence afterwards. The repository scripts invoke terraform explicitly; an OpenTofu-only execution host needs a separately reviewed adaptation, not just replacement of commands in this page.

The implementation provisions STACKIT Observability and scrapes node exporter. The Grafana provider manages the included dashboard in the SCF Rehost folder with the existing Thanos datasource, waiting for instance readiness instead of silently skipping dashboard creation.

Verify live VM, Spring Boot and PostgreSQL metrics through grafana_dashboard_url, then require a no-op plan. Seven panels cover the active baseline; the synthetic request panel is included only when the local load generator is enabled. Synthetic requests do not represent all application traffic. The deployment validation also checks exporter metrics and service health after apply and database restore.

Initial Grafana admin credentials remain a temporary authentication dependency. Protect state and saved plans. Notification receivers, alert-routing ownership, application logs and distributed tracing require separate configuration and acceptance before production use.

This example can be run in two valid setup modes.

  • With an existing Application Landing Zone: Reuse the already defined project context and service account setup from your landing zone implementation. Use the known project ID as deployment target (for example with create_project = false and project_id = "...") and the service account JSON configured for that landing zone scope.
  • Standalone without existing landing zone project: Let the example create a dedicated project. In this mode, set create_project = true and provide parent_container_id with an existing folder/container where the service account has sufficient permissions.

For landing-zone design guidance, see Application Landing Zone.

Use this as a minimal starting point in env.tfvars.

create_project = true
target_project_name = "cmf-rehost-springboot"
target_project_owner_email = "owner@example.com"
parent_container_id = "cmf-xxxxxxxx"
service_account_key_path = "~/.ssh/cmf-sa.json"
public_ssh_key_path = "~/.ssh/id_rsa.pub"
private_ssh_key_path = "~/.ssh/id_rsa"
availability_zone = "eu01-1"
machine_type = "g2i.2"
ssh_allowed_cidr = "203.0.113.10/32"
app_allowed_cidr = "203.0.113.10/32"
enable_local_postgresql = true
enable_observability = true
enable_server_backup = true
  • Default artifact path: ansible/files/springboot-app.jar
  • Default variable: jar_local_path points to the same file.
  • Alternative artifact: Replace the file or override jar_local_path.

When the artifact changes, Terraform detects the checksum difference and re-runs the Ansible deployment step.

Every rehearsal, cutover, and rollback writes an evidence.env file below artifacts/evidence/<timestamp>-<mode>/. Accept a cutover only when the evidence records the expected checksum, row count, fingerprint, target VM, successful runtime validation, and final Terraform no-op.

Terminal window
./scripts/validate_migration.sh
./scripts/validate_deployment.sh
terraform plan -var-file=env.tfvars -detailed-exitcode

Use the same approved source path, restore flag, expected count and fingerprint as the completed cutover. Exit code 0 means no changes, 2 means changes are proposed, and 1 means an error. A plan alone is not proof of apply. The cutover workflow records status=passed and terraform_noop=true only after applying its saved plan and completing data and runtime validation.

  • Prepare: Approve the target design, access paths, source evidence and recovery responsibilities.
  • Provision: Review and apply the infrastructure plan, then verify the VM runtime independently.
  • Rehearse: Restore into an isolated database and verify integrity without changing the application database.
  • Cut over: Freeze source writes, approve the final dump and allow only the intended orchestration change.
  • Accept or recover: Require data, runtime and no-op evidence; invoke the agreed rollback before its deadline when acceptance fails.
  • Handover: Transfer monitoring, backup ownership, evidence and source-retention decisions before optimization.

The Replatform reference reuses this repository’s pinned Spring Music JAR and sample-data generator. Its sample walkthrough therefore needs this checkout and validated dump artifacts, but not a newly provisioned Rehost VM. Do not create another VM solely to generate the local sample.

For an actual migration from the Rehost VM to SKE and PostgreSQL Flex, verify that the VM is currently healthy, freeze its application writes, export its real PostgreSQL database and qualify that export. A historical successful Rehost apply does not establish current reachability or make the local sample equivalent to live VM data. Source export, acceptance and source failback need their own approved procedure.

  • Keep operational procedures aligned with the runbook and migration governance.
  • For production usage, adapt security controls, sizing, image selection, and life cycle automation.
STEP

Rehearse and Approve

The Migration Framework defines the strategy, design, landing-zone, migration, and operations principles for Rehost workloads. This asset applies those principles to one concrete, runnable Spring Boot and PostgreSQL implementation on STACKIT.

The maintained repository is the source of truth for Terraform, Ansible, application artifacts, migration scripts, validation, and runtime configuration. This asset explains how to use that implementation without duplicating its complete source code.

Code & registry github.com STACKIT CMF Rehost Spring Boot repository Open the runnable Terraform and Ansible reference implementation for the Spring Boot and PostgreSQL Rehost path. Open the repository

This example represents an application Rehost path for a Spring Boot workload (Spring Music sample). The migration focus is the runtime relocation of the application to STACKIT:

  • Infrastructure Rehost: Terraform provisions one STACKIT VM, its boot volume, network, public IP, security group, SSH key, Observability resources, and an optional Server Backup schedule.
  • Application Rehost: Ansible installs Java, deploys the Spring Boot JAR, and manages it with systemd without changing the application runtime model.
  • Stateful Rehost: Ansible installs self-managed PostgreSQL on the same VM. The database engine is not changed to a managed service as part of this path.
  • Controlled migration: The repository provides separate rehearsal, cutover, validation, and rollback workflows with machine-readable evidence.

The validated baseline deliberately does not include an application load balancer, DNS switch, multiple VMs, Kubernetes, Cloud Foundry, or PostgreSQL Flex. Add those only as separately designed and tested extensions; they are not implied by this Rehost example.

  • Provisioning: Terraform resources for network, security, one VM, boot volume, SSH key, and public IP.
  • Configuration bridge: Terraform triggers Ansible after infrastructure provisioning.
  • Application deployment: Ansible deploys a concrete Spring Boot artifact and configures a systemd service.
  • VM-local database path: PostgreSQL installation, application role and database bootstrap, and a dump-based restore with ownership and data-integrity checks.
  • Operations baseline: Node exporter metrics are scraped by STACKIT Observability, and Terraform can enable daily Server Backup for the VM boot volume.
  • Migration controls: Source dump generation, independent restore validation, rehearsal, cutover, rollback verification, rollback execution, and evidence capture.
  • Deployable sample artifact: A ready-to-run JAR is included in the repository and used by default deployment settings.

To keep automation behavior consistent across CMF examples, use this common flag model in your Terraform variable set:

  • setup_project: create/use project context.
  • setup_observability: enable/disable observability resources.
  • setup_database: enable/disable optional VM-local PostgreSQL path.
  • setup_workload: enable/disable workload installation on VM.
  • setup_loadgen: enable/disable optional synthetic load generation.
  • setup_dns: accepted by the shared wrapper but unsupported in this baseline.

In the current Rehost repository, these map to existing switches (create_project, enable_observability, enable_local_postgresql, and load-generation related flags).

The following diagram shows the implemented and tested target, not a future high-availability variant.

Approved operator sourceSTACKIT ProjectUbuntu VMObservabilityBackup ArchiveSpring Boot JAR + systemdSelf-managed PostgreSQL restricted HTTP/SSHlocalhost SQLrestricted metrics scrapeboot-volume backup

Use an isolated Linux lab with Git, Terraform, Ansible, ShellCheck, SSH/SCP, curl, jq, and PostgreSQL server/client tools including pg_config. The sample dump scripts use runuser and the local postgres OS account and require root privileges in that lab. When Server Backup is enabled, the cutover workflow also needs an authenticated STACKIT CLI. Use the Terraform version that created an existing saved plan; plan files are version-specific.

Start in a parent directory without an existing checkout of the same name. This revision contains the migration workflows and declarative Observability management. Its JAR is unchanged from the artifact pinned by the Replatform reference:

Terminal window
umask 077 &&
git clone https://github.com/stackitcloud/stackit-cmf-Rehost-springboot.git &&
git -C stackit-cmf-Rehost-springboot checkout --detach b9225eb35c64b9ef4761c208fadb9a2356431793 &&
test -f stackit-cmf-Rehost-springboot/scripts/run_cutover.sh &&
cd stackit-cmf-Rehost-springboot &&
printf '%s\n' "Workspace ready. Continue from this Rehost checkout." || {
printf '%s\n' "Preparation failed. Resolve the error before continuing." >&2
false
}

Run the following steps from that checkout only after preparation succeeds. Preserve an existing checkout, state and evidence rather than replacing them. Keep credentials, plans, state, dumps, inventory and evidence private and outside version control; do not enable shell tracing.

Create the private variable file only when it does not already exist:

Terminal window
umask 077
test -e env.tfvars || cp env.tfvars.example env.tfvars
chmod 600 env.tfvars

Edit the file before planning. Configure the approved existing project or project-creation scope, service-account key path, SSH key pair, available image, availability zone, VM flavor and storage. Restrict SSH and application ingress to the source CIDRs actually observed on the target path. Verify the SSH host key through a trusted channel before migration scripts use strict host checking. Set SSH_KEY and SSH_USER for those scripts when they differ from ~/.ssh/id_rsa and ubuntu; keep them consistent with Terraform’s private_ssh_key_path and ssh_user.

Enable VM-local PostgreSQL, Observability and Server Backup for this walkthrough. For the initial runtime deployment, leave postgresql_restore_after_copy = false and the source dump path empty: provisioning must not import the source before rehearsal and cutover approval. Supply the database password through the approved secret mechanism as TF_VAR_postgresql_app_password and retain it securely for subsequent plans; do not put it in the variable file, shell history or documentation.

Confirm permissions, quota, cost approval and the private Terraform backend before initialization. This workflow assumes a prepared execution host, not a complete lab installation procedure.

Create a custom-format source dump and record its SHA-256 checksum, expected row count, and a workload-specific deterministic fingerprint. The repository includes a reproducible eight-row sample:

Terminal window
./scripts/create_source_dump.sh
./scripts/validate_source_dump.sh

The validation script restores into a separate local PostgreSQL cluster before the dump can enter a migration rehearsal. These scripts generate the bundled sample in temporary local clusters; they do not export a running application or the STACKIT VM. Use a fresh artifact directory and do not overwrite a dump already approved for migration. For a real source, export it under the agreed write-freeze procedure and record equivalent checksum, count and fingerprint evidence.

Configure an existing STACKIT project or project creation, restrict SSH and application ingress to approved source CIDRs, and pass the database password through TF_VAR_postgresql_app_password rather than a variable file. Review a saved plan before apply.

Terminal window
terraform init &&
./scripts/check.sh &&
terraform plan -input=false -var-file=env.tfvars -out=tfplan

Stop if initialization, checks or planning fail. Review resource changes, target project, ingress, cost and the disabled initial restore before explicitly applying that saved plan:

Terminal window
terraform apply tfplan

Terraform runs Ansible after provisioning. A successful apply proves completion of that orchestration, not acceptance of migrated data. Check the runtime before beginning rehearsal:

Terminal window
./scripts/validate_deployment.sh

Run the rehearsal against a temporary database on the target VM:

Terminal window
./scripts/run_migration_rehearsal.sh

The rehearsal checks the source evidence, restores the dump, compares row count and fingerprint, and removes its temporary database without changing the production springmusic database.

Set the expected source evidence in env.tfvars:

enable_local_postgresql = true
postgresql_source_dump_local_path = "artifacts/source-postgresql.dump"
postgresql_restore_after_copy = true
postgresql_expected_album_count = 8
postgresql_expected_album_fingerprint = "<source-fingerprint>"

Then execute the explicit approval gate:

Terminal window
./scripts/run_cutover.sh --confirm

The script requires a fully available Server Backup no older than 24 hours, creates a saved Terraform plan that replaces only the Ansible orchestration resource, rejects unrelated infrastructure changes, applies the restore, validates data and runtime behavior, and requires a final no-op Terraform plan. Retries of the same source-dump hash preserve the original database rollback point.

Validation checks the source row count and fingerprint, table ownership, a transactionally rolled-back write as the application role, application reachability, services, and the protected rollback dump.

Terminal window
./scripts/validate_migration.sh
./scripts/validate_deployment.sh
./scripts/verify_rollback.sh

If an approved rollback trigger is met during the rollback window, preserve the current target database and restore the original pre-cutover dump:

Terminal window
./scripts/rollback_postgresql.sh --confirm

With enable_server_backup = true, Terraform enables STACKIT Server Backup and a daily schedule for the VM boot volume. The default retention is 14 days. Cutover requires a fully available backup no older than 24 hours. Backup creation and status were validated against the real target; an in-place Server Backup restore remains a disruptive disaster-recovery operation and must be rehearsed in a separate recovery environment.

The Server Backup complements the PostgreSQL pre-restore dump. It does not replace database-level consistency checks or the application rollback procedure.

Follow workspace preparation, private target configuration, source evidence and reviewed provisioning in that order. Rehearse before the approved cutover and retain the acceptance evidence afterwards. The repository scripts invoke terraform explicitly; an OpenTofu-only execution host needs a separately reviewed adaptation, not just replacement of commands in this page.

The implementation provisions STACKIT Observability and scrapes node exporter. The Grafana provider manages the included dashboard in the SCF Rehost folder with the existing Thanos datasource, waiting for instance readiness instead of silently skipping dashboard creation.

Verify live VM, Spring Boot and PostgreSQL metrics through grafana_dashboard_url, then require a no-op plan. Seven panels cover the active baseline; the synthetic request panel is included only when the local load generator is enabled. Synthetic requests do not represent all application traffic. The deployment validation also checks exporter metrics and service health after apply and database restore.

Initial Grafana admin credentials remain a temporary authentication dependency. Protect state and saved plans. Notification receivers, alert-routing ownership, application logs and distributed tracing require separate configuration and acceptance before production use.

This example can be run in two valid setup modes.

  • With an existing Application Landing Zone: Reuse the already defined project context and service account setup from your landing zone implementation. Use the known project ID as deployment target (for example with create_project = false and project_id = "...") and the service account JSON configured for that landing zone scope.
  • Standalone without existing landing zone project: Let the example create a dedicated project. In this mode, set create_project = true and provide parent_container_id with an existing folder/container where the service account has sufficient permissions.

For landing-zone design guidance, see Application Landing Zone.

Use this as a minimal starting point in env.tfvars.

create_project = true
target_project_name = "cmf-rehost-springboot"
target_project_owner_email = "owner@example.com"
parent_container_id = "cmf-xxxxxxxx"
service_account_key_path = "~/.ssh/cmf-sa.json"
public_ssh_key_path = "~/.ssh/id_rsa.pub"
private_ssh_key_path = "~/.ssh/id_rsa"
availability_zone = "eu01-1"
machine_type = "g2i.2"
ssh_allowed_cidr = "203.0.113.10/32"
app_allowed_cidr = "203.0.113.10/32"
enable_local_postgresql = true
enable_observability = true
enable_server_backup = true
  • Default artifact path: ansible/files/springboot-app.jar
  • Default variable: jar_local_path points to the same file.
  • Alternative artifact: Replace the file or override jar_local_path.

When the artifact changes, Terraform detects the checksum difference and re-runs the Ansible deployment step.

Every rehearsal, cutover, and rollback writes an evidence.env file below artifacts/evidence/<timestamp>-<mode>/. Accept a cutover only when the evidence records the expected checksum, row count, fingerprint, target VM, successful runtime validation, and final Terraform no-op.

Terminal window
./scripts/validate_migration.sh
./scripts/validate_deployment.sh
terraform plan -var-file=env.tfvars -detailed-exitcode

Use the same approved source path, restore flag, expected count and fingerprint as the completed cutover. Exit code 0 means no changes, 2 means changes are proposed, and 1 means an error. A plan alone is not proof of apply. The cutover workflow records status=passed and terraform_noop=true only after applying its saved plan and completing data and runtime validation.

  • Prepare: Approve the target design, access paths, source evidence and recovery responsibilities.
  • Provision: Review and apply the infrastructure plan, then verify the VM runtime independently.
  • Rehearse: Restore into an isolated database and verify integrity without changing the application database.
  • Cut over: Freeze source writes, approve the final dump and allow only the intended orchestration change.
  • Accept or recover: Require data, runtime and no-op evidence; invoke the agreed rollback before its deadline when acceptance fails.
  • Handover: Transfer monitoring, backup ownership, evidence and source-retention decisions before optimization.

The Replatform reference reuses this repository’s pinned Spring Music JAR and sample-data generator. Its sample walkthrough therefore needs this checkout and validated dump artifacts, but not a newly provisioned Rehost VM. Do not create another VM solely to generate the local sample.

For an actual migration from the Rehost VM to SKE and PostgreSQL Flex, verify that the VM is currently healthy, freeze its application writes, export its real PostgreSQL database and qualify that export. A historical successful Rehost apply does not establish current reachability or make the local sample equivalent to live VM data. Source export, acceptance and source failback need their own approved procedure.

  • Keep operational procedures aligned with the runbook and migration governance.
  • For production usage, adapt security controls, sizing, image selection, and life cycle automation.
  • Migration strategy: Rehost (lift and shift)
  • Application type: Spring Boot service (JAR), no Kubernetes target
  • Target platform: VM-based runtime on STACKIT
  • Data backend: PostgreSQL
  • In scope: Terraform provisioning, Ansible configuration, source dump evidence, temporary-database rehearsal, controlled restore, runtime validation, rollback, Observability, and Server Backup schedule.
  • Out of scope: Code refactoring, database engine change, load balancing, DNS switch, TLS termination, multi-VM availability, Kubernetes, Cloud Foundry, and PostgreSQL Flex.
  • Assumptions: The source uses a PostgreSQL version compatible with the target restore tools, and approved source CIDRs are known for SSH and application access.
  • Migration lead: Coordinates timeline, checkpoints, and go/no-go decision.
  • Application owner: Validates app behavior and business-critical user journeys.
  • Platform engineer: Prepares the VM, restricted network rules, monitoring, and backup schedule.
  • DB owner: Executes DB backup, restore, consistency checks, and rollback trigger.
  • Operations owner: Accepts handover and owns Day-1/Day-2 incident response.
  • Access readiness: SSH, deployment credentials, DB access, secrets access validated.
  • Baseline captured: Current versions, environment variables, ports, certificates, scheduled jobs documented.
  • Capacity validated: CPU, RAM, disk IOPS, and storage capacity on target VM confirmed.
  • Security controls ready: Firewall rules, IAM mapping, TLS cert chain, and logging in place.
  • Source evidence ready: Dump checksum, expected record count, and deterministic data fingerprint recorded.
  • Rollback readiness: Pre-restore dump path, expected original record count, authority, and deadline agreed.
  1. Create target app user and required filesystem layout.
  2. Install Java runtime and supporting OS packages.
  3. Deploy app artifact to target path.
  4. Configure service unit (systemd) and environment file.
  5. Install self-managed PostgreSQL, create the application role and database, and restrict access to localhost.
  6. Enable node exporter, Observability scraping, and the Server Backup schedule.
  1. Create a custom-format PostgreSQL dump without source ownership or privileges.
  2. Record and validate its checksum, expected row count, and deterministic fingerprint.
  3. Run ./scripts/run_migration_rehearsal.sh against a temporary target database.
  4. Confirm that rehearsal removed its temporary database and left the production target database unchanged.
  5. Verify the original target rollback dump in a separate temporary database.
  1. Freeze source writes and create the final approved dump.
  2. Run ./scripts/run_cutover.sh --confirm with the approved source evidence.
  3. Require the saved Terraform plan to change only the Ansible orchestration resource.
  4. Validate row count, fingerprint, ownership, application-role write behavior, services, and endpoint.
  5. Require a final Terraform no-op plan, record acceptance, and start stabilization watch.
  • Technical health: Spring Boot, PostgreSQL, and node exporter active; local and approved public HTTP checks pass.
  • Functional checks: The application returns the migrated Spring Music records.
  • Data checks: Expected row count and fingerprint match; table ownership belongs to the application role.
  • Security checks: Runtime environment and rollback dump have root-only or database-owner-only permissions.
  • Operations checks: Observability scrape, Server Backup schedule, evidence files, and escalation ownership verified.
  • Critical functional failure: Core business flow unavailable after fix window.
  • Data integrity risk: Mismatch in critical records with no fast remediation.
  • Operational instability: Repeated restarts or unresolved severe alerts.
  1. Invoke the approved rollback decision before the deadline and preserve logs and evidence.
  2. Run ./scripts/rollback_postgresql.sh --confirm to stop Spring Boot and preserve the current target database.
  3. Restore the protected pre-cutover dump and validate the expected original row count.
  4. Restart Spring Boot and validate local and approved public reachability.
  5. Resume the agreed source or target operating state and publish the rollback evidence and decision.
  • Artifacts delivered: Final config set, deployment manifest, validation evidence, rollback log.
  • Ownership transfer: Named on-call owner and escalation route confirmed.
  • Stabilization period: 24-72 hours with enhanced monitoring and daily status check.
  • Exit criteria: No critical alerts, stable key metrics, and business owner sign-off.
LIVE

Cutover

End-to-End Migration Wave Flow Four sequential stages lead from wave approval through preparation and cutover to stabilization and handover. Every wave follows the same controlled factory flow. Secure scope and baseline, execute migration, prove acceptance, and hand over safely to operations. 1 APPROVE Scope and baseline Window, rollback, and ownership Freeze versions and dependencies 2 PREPARE Readiness and R-path Validate source and target Run playbook by archetype 3 CUT OVER Cutover and acceptance Route traffic to STACKIT safely Validate tech, function, operations 4 STABILIZE Learn and hand over Resolve findings in short loops Hand over to Optimize and Operate Traceable wave completion: accepted, stabilized, and fully handed over
Controlled migration wave from readiness through cutover, validation, and handover
  • Migration strategy: Rehost (lift and shift)
  • Application type: Spring Boot service (JAR), no Kubernetes target
  • Target platform: VM-based runtime on STACKIT
  • Data backend: PostgreSQL
  • In scope: Terraform provisioning, Ansible configuration, source dump evidence, temporary-database rehearsal, controlled restore, runtime validation, rollback, Observability, and Server Backup schedule.
  • Out of scope: Code refactoring, database engine change, load balancing, DNS switch, TLS termination, multi-VM availability, Kubernetes, Cloud Foundry, and PostgreSQL Flex.
  • Assumptions: The source uses a PostgreSQL version compatible with the target restore tools, and approved source CIDRs are known for SSH and application access.
  • Migration lead: Coordinates timeline, checkpoints, and go/no-go decision.
  • Application owner: Validates app behavior and business-critical user journeys.
  • Platform engineer: Prepares the VM, restricted network rules, monitoring, and backup schedule.
  • DB owner: Executes DB backup, restore, consistency checks, and rollback trigger.
  • Operations owner: Accepts handover and owns Day-1/Day-2 incident response.
  • Access readiness: SSH, deployment credentials, DB access, secrets access validated.
  • Baseline captured: Current versions, environment variables, ports, certificates, scheduled jobs documented.
  • Capacity validated: CPU, RAM, disk IOPS, and storage capacity on target VM confirmed.
  • Security controls ready: Firewall rules, IAM mapping, TLS cert chain, and logging in place.
  • Source evidence ready: Dump checksum, expected record count, and deterministic data fingerprint recorded.
  • Rollback readiness: Pre-restore dump path, expected original record count, authority, and deadline agreed.
  1. Create target app user and required filesystem layout.
  2. Install Java runtime and supporting OS packages.
  3. Deploy app artifact to target path.
  4. Configure service unit (systemd) and environment file.
  5. Install self-managed PostgreSQL, create the application role and database, and restrict access to localhost.
  6. Enable node exporter, Observability scraping, and the Server Backup schedule.
  1. Create a custom-format PostgreSQL dump without source ownership or privileges.
  2. Record and validate its checksum, expected row count, and deterministic fingerprint.
  3. Run ./scripts/run_migration_rehearsal.sh against a temporary target database.
  4. Confirm that rehearsal removed its temporary database and left the production target database unchanged.
  5. Verify the original target rollback dump in a separate temporary database.
  1. Freeze source writes and create the final approved dump.
  2. Run ./scripts/run_cutover.sh --confirm with the approved source evidence.
  3. Require the saved Terraform plan to change only the Ansible orchestration resource.
  4. Validate row count, fingerprint, ownership, application-role write behavior, services, and endpoint.
  5. Require a final Terraform no-op plan, record acceptance, and start stabilization watch.
  • Technical health: Spring Boot, PostgreSQL, and node exporter active; local and approved public HTTP checks pass.
  • Functional checks: The application returns the migrated Spring Music records.
  • Data checks: Expected row count and fingerprint match; table ownership belongs to the application role.
  • Security checks: Runtime environment and rollback dump have root-only or database-owner-only permissions.
  • Operations checks: Observability scrape, Server Backup schedule, evidence files, and escalation ownership verified.
  • Critical functional failure: Core business flow unavailable after fix window.
  • Data integrity risk: Mismatch in critical records with no fast remediation.
  • Operational instability: Repeated restarts or unresolved severe alerts.
  1. Invoke the approved rollback decision before the deadline and preserve logs and evidence.
  2. Run ./scripts/rollback_postgresql.sh --confirm to stop Spring Boot and preserve the current target database.
  3. Restore the protected pre-cutover dump and validate the expected original row count.
  4. Restart Spring Boot and validate local and approved public reachability.
  5. Resume the agreed source or target operating state and publish the rollback evidence and decision.
  • Artifacts delivered: Final config set, deployment manifest, validation evidence, rollback log.
  • Ownership transfer: Named on-call owner and escalation route confirmed.
  • Stabilization period: 24-72 hours with enhanced monitoring and daily status check.
  • Exit criteria: No critical alerts, stable key metrics, and business owner sign-off.
LIVE

Run the Approved Cutover

The Migration Framework defines the strategy, design, landing-zone, migration, and operations principles for Rehost workloads. This asset applies those principles to one concrete, runnable Spring Boot and PostgreSQL implementation on STACKIT.

The maintained repository is the source of truth for Terraform, Ansible, application artifacts, migration scripts, validation, and runtime configuration. This asset explains how to use that implementation without duplicating its complete source code.

Code & registry github.com STACKIT CMF Rehost Spring Boot repository Open the runnable Terraform and Ansible reference implementation for the Spring Boot and PostgreSQL Rehost path. Open the repository

This example represents an application Rehost path for a Spring Boot workload (Spring Music sample). The migration focus is the runtime relocation of the application to STACKIT:

  • Infrastructure Rehost: Terraform provisions one STACKIT VM, its boot volume, network, public IP, security group, SSH key, Observability resources, and an optional Server Backup schedule.
  • Application Rehost: Ansible installs Java, deploys the Spring Boot JAR, and manages it with systemd without changing the application runtime model.
  • Stateful Rehost: Ansible installs self-managed PostgreSQL on the same VM. The database engine is not changed to a managed service as part of this path.
  • Controlled migration: The repository provides separate rehearsal, cutover, validation, and rollback workflows with machine-readable evidence.

The validated baseline deliberately does not include an application load balancer, DNS switch, multiple VMs, Kubernetes, Cloud Foundry, or PostgreSQL Flex. Add those only as separately designed and tested extensions; they are not implied by this Rehost example.

  • Provisioning: Terraform resources for network, security, one VM, boot volume, SSH key, and public IP.
  • Configuration bridge: Terraform triggers Ansible after infrastructure provisioning.
  • Application deployment: Ansible deploys a concrete Spring Boot artifact and configures a systemd service.
  • VM-local database path: PostgreSQL installation, application role and database bootstrap, and a dump-based restore with ownership and data-integrity checks.
  • Operations baseline: Node exporter metrics are scraped by STACKIT Observability, and Terraform can enable daily Server Backup for the VM boot volume.
  • Migration controls: Source dump generation, independent restore validation, rehearsal, cutover, rollback verification, rollback execution, and evidence capture.
  • Deployable sample artifact: A ready-to-run JAR is included in the repository and used by default deployment settings.

To keep automation behavior consistent across CMF examples, use this common flag model in your Terraform variable set:

  • setup_project: create/use project context.
  • setup_observability: enable/disable observability resources.
  • setup_database: enable/disable optional VM-local PostgreSQL path.
  • setup_workload: enable/disable workload installation on VM.
  • setup_loadgen: enable/disable optional synthetic load generation.
  • setup_dns: accepted by the shared wrapper but unsupported in this baseline.

In the current Rehost repository, these map to existing switches (create_project, enable_observability, enable_local_postgresql, and load-generation related flags).

The following diagram shows the implemented and tested target, not a future high-availability variant.

Approved operator sourceSTACKIT ProjectUbuntu VMObservabilityBackup ArchiveSpring Boot JAR + systemdSelf-managed PostgreSQL restricted HTTP/SSHlocalhost SQLrestricted metrics scrapeboot-volume backup

Use an isolated Linux lab with Git, Terraform, Ansible, ShellCheck, SSH/SCP, curl, jq, and PostgreSQL server/client tools including pg_config. The sample dump scripts use runuser and the local postgres OS account and require root privileges in that lab. When Server Backup is enabled, the cutover workflow also needs an authenticated STACKIT CLI. Use the Terraform version that created an existing saved plan; plan files are version-specific.

Start in a parent directory without an existing checkout of the same name. This revision contains the migration workflows and declarative Observability management. Its JAR is unchanged from the artifact pinned by the Replatform reference:

Terminal window
umask 077 &&
git clone https://github.com/stackitcloud/stackit-cmf-Rehost-springboot.git &&
git -C stackit-cmf-Rehost-springboot checkout --detach b9225eb35c64b9ef4761c208fadb9a2356431793 &&
test -f stackit-cmf-Rehost-springboot/scripts/run_cutover.sh &&
cd stackit-cmf-Rehost-springboot &&
printf '%s\n' "Workspace ready. Continue from this Rehost checkout." || {
printf '%s\n' "Preparation failed. Resolve the error before continuing." >&2
false
}

Run the following steps from that checkout only after preparation succeeds. Preserve an existing checkout, state and evidence rather than replacing them. Keep credentials, plans, state, dumps, inventory and evidence private and outside version control; do not enable shell tracing.

Create the private variable file only when it does not already exist:

Terminal window
umask 077
test -e env.tfvars || cp env.tfvars.example env.tfvars
chmod 600 env.tfvars

Edit the file before planning. Configure the approved existing project or project-creation scope, service-account key path, SSH key pair, available image, availability zone, VM flavor and storage. Restrict SSH and application ingress to the source CIDRs actually observed on the target path. Verify the SSH host key through a trusted channel before migration scripts use strict host checking. Set SSH_KEY and SSH_USER for those scripts when they differ from ~/.ssh/id_rsa and ubuntu; keep them consistent with Terraform’s private_ssh_key_path and ssh_user.

Enable VM-local PostgreSQL, Observability and Server Backup for this walkthrough. For the initial runtime deployment, leave postgresql_restore_after_copy = false and the source dump path empty: provisioning must not import the source before rehearsal and cutover approval. Supply the database password through the approved secret mechanism as TF_VAR_postgresql_app_password and retain it securely for subsequent plans; do not put it in the variable file, shell history or documentation.

Confirm permissions, quota, cost approval and the private Terraform backend before initialization. This workflow assumes a prepared execution host, not a complete lab installation procedure.

Create a custom-format source dump and record its SHA-256 checksum, expected row count, and a workload-specific deterministic fingerprint. The repository includes a reproducible eight-row sample:

Terminal window
./scripts/create_source_dump.sh
./scripts/validate_source_dump.sh

The validation script restores into a separate local PostgreSQL cluster before the dump can enter a migration rehearsal. These scripts generate the bundled sample in temporary local clusters; they do not export a running application or the STACKIT VM. Use a fresh artifact directory and do not overwrite a dump already approved for migration. For a real source, export it under the agreed write-freeze procedure and record equivalent checksum, count and fingerprint evidence.

Configure an existing STACKIT project or project creation, restrict SSH and application ingress to approved source CIDRs, and pass the database password through TF_VAR_postgresql_app_password rather than a variable file. Review a saved plan before apply.

Terminal window
terraform init &&
./scripts/check.sh &&
terraform plan -input=false -var-file=env.tfvars -out=tfplan

Stop if initialization, checks or planning fail. Review resource changes, target project, ingress, cost and the disabled initial restore before explicitly applying that saved plan:

Terminal window
terraform apply tfplan

Terraform runs Ansible after provisioning. A successful apply proves completion of that orchestration, not acceptance of migrated data. Check the runtime before beginning rehearsal:

Terminal window
./scripts/validate_deployment.sh

Run the rehearsal against a temporary database on the target VM:

Terminal window
./scripts/run_migration_rehearsal.sh

The rehearsal checks the source evidence, restores the dump, compares row count and fingerprint, and removes its temporary database without changing the production springmusic database.

Set the expected source evidence in env.tfvars:

enable_local_postgresql = true
postgresql_source_dump_local_path = "artifacts/source-postgresql.dump"
postgresql_restore_after_copy = true
postgresql_expected_album_count = 8
postgresql_expected_album_fingerprint = "<source-fingerprint>"

Then execute the explicit approval gate:

Terminal window
./scripts/run_cutover.sh --confirm

The script requires a fully available Server Backup no older than 24 hours, creates a saved Terraform plan that replaces only the Ansible orchestration resource, rejects unrelated infrastructure changes, applies the restore, validates data and runtime behavior, and requires a final no-op Terraform plan. Retries of the same source-dump hash preserve the original database rollback point.

Validation checks the source row count and fingerprint, table ownership, a transactionally rolled-back write as the application role, application reachability, services, and the protected rollback dump.

Terminal window
./scripts/validate_migration.sh
./scripts/validate_deployment.sh
./scripts/verify_rollback.sh

If an approved rollback trigger is met during the rollback window, preserve the current target database and restore the original pre-cutover dump:

Terminal window
./scripts/rollback_postgresql.sh --confirm

With enable_server_backup = true, Terraform enables STACKIT Server Backup and a daily schedule for the VM boot volume. The default retention is 14 days. Cutover requires a fully available backup no older than 24 hours. Backup creation and status were validated against the real target; an in-place Server Backup restore remains a disruptive disaster-recovery operation and must be rehearsed in a separate recovery environment.

The Server Backup complements the PostgreSQL pre-restore dump. It does not replace database-level consistency checks or the application rollback procedure.

Follow workspace preparation, private target configuration, source evidence and reviewed provisioning in that order. Rehearse before the approved cutover and retain the acceptance evidence afterwards. The repository scripts invoke terraform explicitly; an OpenTofu-only execution host needs a separately reviewed adaptation, not just replacement of commands in this page.

The implementation provisions STACKIT Observability and scrapes node exporter. The Grafana provider manages the included dashboard in the SCF Rehost folder with the existing Thanos datasource, waiting for instance readiness instead of silently skipping dashboard creation.

Verify live VM, Spring Boot and PostgreSQL metrics through grafana_dashboard_url, then require a no-op plan. Seven panels cover the active baseline; the synthetic request panel is included only when the local load generator is enabled. Synthetic requests do not represent all application traffic. The deployment validation also checks exporter metrics and service health after apply and database restore.

Initial Grafana admin credentials remain a temporary authentication dependency. Protect state and saved plans. Notification receivers, alert-routing ownership, application logs and distributed tracing require separate configuration and acceptance before production use.

This example can be run in two valid setup modes.

  • With an existing Application Landing Zone: Reuse the already defined project context and service account setup from your landing zone implementation. Use the known project ID as deployment target (for example with create_project = false and project_id = "...") and the service account JSON configured for that landing zone scope.
  • Standalone without existing landing zone project: Let the example create a dedicated project. In this mode, set create_project = true and provide parent_container_id with an existing folder/container where the service account has sufficient permissions.

For landing-zone design guidance, see Application Landing Zone.

Use this as a minimal starting point in env.tfvars.

create_project = true
target_project_name = "cmf-rehost-springboot"
target_project_owner_email = "owner@example.com"
parent_container_id = "cmf-xxxxxxxx"
service_account_key_path = "~/.ssh/cmf-sa.json"
public_ssh_key_path = "~/.ssh/id_rsa.pub"
private_ssh_key_path = "~/.ssh/id_rsa"
availability_zone = "eu01-1"
machine_type = "g2i.2"
ssh_allowed_cidr = "203.0.113.10/32"
app_allowed_cidr = "203.0.113.10/32"
enable_local_postgresql = true
enable_observability = true
enable_server_backup = true
  • Default artifact path: ansible/files/springboot-app.jar
  • Default variable: jar_local_path points to the same file.
  • Alternative artifact: Replace the file or override jar_local_path.

When the artifact changes, Terraform detects the checksum difference and re-runs the Ansible deployment step.

Every rehearsal, cutover, and rollback writes an evidence.env file below artifacts/evidence/<timestamp>-<mode>/. Accept a cutover only when the evidence records the expected checksum, row count, fingerprint, target VM, successful runtime validation, and final Terraform no-op.

Terminal window
./scripts/validate_migration.sh
./scripts/validate_deployment.sh
terraform plan -var-file=env.tfvars -detailed-exitcode

Use the same approved source path, restore flag, expected count and fingerprint as the completed cutover. Exit code 0 means no changes, 2 means changes are proposed, and 1 means an error. A plan alone is not proof of apply. The cutover workflow records status=passed and terraform_noop=true only after applying its saved plan and completing data and runtime validation.

  • Prepare: Approve the target design, access paths, source evidence and recovery responsibilities.
  • Provision: Review and apply the infrastructure plan, then verify the VM runtime independently.
  • Rehearse: Restore into an isolated database and verify integrity without changing the application database.
  • Cut over: Freeze source writes, approve the final dump and allow only the intended orchestration change.
  • Accept or recover: Require data, runtime and no-op evidence; invoke the agreed rollback before its deadline when acceptance fails.
  • Handover: Transfer monitoring, backup ownership, evidence and source-retention decisions before optimization.

The Replatform reference reuses this repository’s pinned Spring Music JAR and sample-data generator. Its sample walkthrough therefore needs this checkout and validated dump artifacts, but not a newly provisioned Rehost VM. Do not create another VM solely to generate the local sample.

For an actual migration from the Rehost VM to SKE and PostgreSQL Flex, verify that the VM is currently healthy, freeze its application writes, export its real PostgreSQL database and qualify that export. A historical successful Rehost apply does not establish current reachability or make the local sample equivalent to live VM data. Source export, acceptance and source failback need their own approved procedure.

  • Keep operational procedures aligned with the runbook and migration governance.
  • For production usage, adapt security controls, sizing, image selection, and life cycle automation.
SAFE

Validate or Roll Back

  • Migration strategy: Rehost (lift and shift)
  • Application type: Spring Boot service (JAR), no Kubernetes target
  • Target platform: VM-based runtime on STACKIT
  • Data backend: PostgreSQL
  • In scope: Terraform provisioning, Ansible configuration, source dump evidence, temporary-database rehearsal, controlled restore, runtime validation, rollback, Observability, and Server Backup schedule.
  • Out of scope: Code refactoring, database engine change, load balancing, DNS switch, TLS termination, multi-VM availability, Kubernetes, Cloud Foundry, and PostgreSQL Flex.
  • Assumptions: The source uses a PostgreSQL version compatible with the target restore tools, and approved source CIDRs are known for SSH and application access.
  • Migration lead: Coordinates timeline, checkpoints, and go/no-go decision.
  • Application owner: Validates app behavior and business-critical user journeys.
  • Platform engineer: Prepares the VM, restricted network rules, monitoring, and backup schedule.
  • DB owner: Executes DB backup, restore, consistency checks, and rollback trigger.
  • Operations owner: Accepts handover and owns Day-1/Day-2 incident response.
  • Access readiness: SSH, deployment credentials, DB access, secrets access validated.
  • Baseline captured: Current versions, environment variables, ports, certificates, scheduled jobs documented.
  • Capacity validated: CPU, RAM, disk IOPS, and storage capacity on target VM confirmed.
  • Security controls ready: Firewall rules, IAM mapping, TLS cert chain, and logging in place.
  • Source evidence ready: Dump checksum, expected record count, and deterministic data fingerprint recorded.
  • Rollback readiness: Pre-restore dump path, expected original record count, authority, and deadline agreed.
  1. Create target app user and required filesystem layout.
  2. Install Java runtime and supporting OS packages.
  3. Deploy app artifact to target path.
  4. Configure service unit (systemd) and environment file.
  5. Install self-managed PostgreSQL, create the application role and database, and restrict access to localhost.
  6. Enable node exporter, Observability scraping, and the Server Backup schedule.
  1. Create a custom-format PostgreSQL dump without source ownership or privileges.
  2. Record and validate its checksum, expected row count, and deterministic fingerprint.
  3. Run ./scripts/run_migration_rehearsal.sh against a temporary target database.
  4. Confirm that rehearsal removed its temporary database and left the production target database unchanged.
  5. Verify the original target rollback dump in a separate temporary database.
  1. Freeze source writes and create the final approved dump.
  2. Run ./scripts/run_cutover.sh --confirm with the approved source evidence.
  3. Require the saved Terraform plan to change only the Ansible orchestration resource.
  4. Validate row count, fingerprint, ownership, application-role write behavior, services, and endpoint.
  5. Require a final Terraform no-op plan, record acceptance, and start stabilization watch.
  • Technical health: Spring Boot, PostgreSQL, and node exporter active; local and approved public HTTP checks pass.
  • Functional checks: The application returns the migrated Spring Music records.
  • Data checks: Expected row count and fingerprint match; table ownership belongs to the application role.
  • Security checks: Runtime environment and rollback dump have root-only or database-owner-only permissions.
  • Operations checks: Observability scrape, Server Backup schedule, evidence files, and escalation ownership verified.
  • Critical functional failure: Core business flow unavailable after fix window.
  • Data integrity risk: Mismatch in critical records with no fast remediation.
  • Operational instability: Repeated restarts or unresolved severe alerts.
  1. Invoke the approved rollback decision before the deadline and preserve logs and evidence.
  2. Run ./scripts/rollback_postgresql.sh --confirm to stop Spring Boot and preserve the current target database.
  3. Restore the protected pre-cutover dump and validate the expected original row count.
  4. Restart Spring Boot and validate local and approved public reachability.
  5. Resume the agreed source or target operating state and publish the rollback evidence and decision.
  • Artifacts delivered: Final config set, deployment manifest, validation evidence, rollback log.
  • Ownership transfer: Named on-call owner and escalation route confirmed.
  • Stabilization period: 24-72 hours with enhanced monitoring and daily status check.
  • Exit criteria: No critical alerts, stable key metrics, and business owner sign-off.
  • Migration strategy: Rehost (lift and shift)
  • Application type: Spring Boot service (JAR), no Kubernetes target
  • Target platform: VM-based runtime on STACKIT
  • Data backend: PostgreSQL
  • In scope: Terraform provisioning, Ansible configuration, source dump evidence, temporary-database rehearsal, controlled restore, runtime validation, rollback, Observability, and Server Backup schedule.
  • Out of scope: Code refactoring, database engine change, load balancing, DNS switch, TLS termination, multi-VM availability, Kubernetes, Cloud Foundry, and PostgreSQL Flex.
  • Assumptions: The source uses a PostgreSQL version compatible with the target restore tools, and approved source CIDRs are known for SSH and application access.
  • Migration lead: Coordinates timeline, checkpoints, and go/no-go decision.
  • Application owner: Validates app behavior and business-critical user journeys.
  • Platform engineer: Prepares the VM, restricted network rules, monitoring, and backup schedule.
  • DB owner: Executes DB backup, restore, consistency checks, and rollback trigger.
  • Operations owner: Accepts handover and owns Day-1/Day-2 incident response.
  • Access readiness: SSH, deployment credentials, DB access, secrets access validated.
  • Baseline captured: Current versions, environment variables, ports, certificates, scheduled jobs documented.
  • Capacity validated: CPU, RAM, disk IOPS, and storage capacity on target VM confirmed.
  • Security controls ready: Firewall rules, IAM mapping, TLS cert chain, and logging in place.
  • Source evidence ready: Dump checksum, expected record count, and deterministic data fingerprint recorded.
  • Rollback readiness: Pre-restore dump path, expected original record count, authority, and deadline agreed.
  1. Create target app user and required filesystem layout.
  2. Install Java runtime and supporting OS packages.
  3. Deploy app artifact to target path.
  4. Configure service unit (systemd) and environment file.
  5. Install self-managed PostgreSQL, create the application role and database, and restrict access to localhost.
  6. Enable node exporter, Observability scraping, and the Server Backup schedule.
  1. Create a custom-format PostgreSQL dump without source ownership or privileges.
  2. Record and validate its checksum, expected row count, and deterministic fingerprint.
  3. Run ./scripts/run_migration_rehearsal.sh against a temporary target database.
  4. Confirm that rehearsal removed its temporary database and left the production target database unchanged.
  5. Verify the original target rollback dump in a separate temporary database.
  1. Freeze source writes and create the final approved dump.
  2. Run ./scripts/run_cutover.sh --confirm with the approved source evidence.
  3. Require the saved Terraform plan to change only the Ansible orchestration resource.
  4. Validate row count, fingerprint, ownership, application-role write behavior, services, and endpoint.
  5. Require a final Terraform no-op plan, record acceptance, and start stabilization watch.
  • Technical health: Spring Boot, PostgreSQL, and node exporter active; local and approved public HTTP checks pass.
  • Functional checks: The application returns the migrated Spring Music records.
  • Data checks: Expected row count and fingerprint match; table ownership belongs to the application role.
  • Security checks: Runtime environment and rollback dump have root-only or database-owner-only permissions.
  • Operations checks: Observability scrape, Server Backup schedule, evidence files, and escalation ownership verified.
  • Critical functional failure: Core business flow unavailable after fix window.
  • Data integrity risk: Mismatch in critical records with no fast remediation.
  • Operational instability: Repeated restarts or unresolved severe alerts.
  1. Invoke the approved rollback decision before the deadline and preserve logs and evidence.
  2. Run ./scripts/rollback_postgresql.sh --confirm to stop Spring Boot and preserve the current target database.
  3. Restore the protected pre-cutover dump and validate the expected original row count.
  4. Restart Spring Boot and validate local and approved public reachability.
  5. Resume the agreed source or target operating state and publish the rollback evidence and decision.
  • Artifacts delivered: Final config set, deployment manifest, validation evidence, rollback log.
  • Ownership transfer: Named on-call owner and escalation route confirmed.
  • Stabilization period: 24-72 hours with enhanced monitoring and daily status check.
  • Exit criteria: No critical alerts, stable key metrics, and business owner sign-off.
SAFE

Verify Data and Runtime

The Migration Framework defines the strategy, design, landing-zone, migration, and operations principles for Rehost workloads. This asset applies those principles to one concrete, runnable Spring Boot and PostgreSQL implementation on STACKIT.

The maintained repository is the source of truth for Terraform, Ansible, application artifacts, migration scripts, validation, and runtime configuration. This asset explains how to use that implementation without duplicating its complete source code.

Code & registry github.com STACKIT CMF Rehost Spring Boot repository Open the runnable Terraform and Ansible reference implementation for the Spring Boot and PostgreSQL Rehost path. Open the repository

This example represents an application Rehost path for a Spring Boot workload (Spring Music sample). The migration focus is the runtime relocation of the application to STACKIT:

  • Infrastructure Rehost: Terraform provisions one STACKIT VM, its boot volume, network, public IP, security group, SSH key, Observability resources, and an optional Server Backup schedule.
  • Application Rehost: Ansible installs Java, deploys the Spring Boot JAR, and manages it with systemd without changing the application runtime model.
  • Stateful Rehost: Ansible installs self-managed PostgreSQL on the same VM. The database engine is not changed to a managed service as part of this path.
  • Controlled migration: The repository provides separate rehearsal, cutover, validation, and rollback workflows with machine-readable evidence.

The validated baseline deliberately does not include an application load balancer, DNS switch, multiple VMs, Kubernetes, Cloud Foundry, or PostgreSQL Flex. Add those only as separately designed and tested extensions; they are not implied by this Rehost example.

  • Provisioning: Terraform resources for network, security, one VM, boot volume, SSH key, and public IP.
  • Configuration bridge: Terraform triggers Ansible after infrastructure provisioning.
  • Application deployment: Ansible deploys a concrete Spring Boot artifact and configures a systemd service.
  • VM-local database path: PostgreSQL installation, application role and database bootstrap, and a dump-based restore with ownership and data-integrity checks.
  • Operations baseline: Node exporter metrics are scraped by STACKIT Observability, and Terraform can enable daily Server Backup for the VM boot volume.
  • Migration controls: Source dump generation, independent restore validation, rehearsal, cutover, rollback verification, rollback execution, and evidence capture.
  • Deployable sample artifact: A ready-to-run JAR is included in the repository and used by default deployment settings.

To keep automation behavior consistent across CMF examples, use this common flag model in your Terraform variable set:

  • setup_project: create/use project context.
  • setup_observability: enable/disable observability resources.
  • setup_database: enable/disable optional VM-local PostgreSQL path.
  • setup_workload: enable/disable workload installation on VM.
  • setup_loadgen: enable/disable optional synthetic load generation.
  • setup_dns: accepted by the shared wrapper but unsupported in this baseline.

In the current Rehost repository, these map to existing switches (create_project, enable_observability, enable_local_postgresql, and load-generation related flags).

The following diagram shows the implemented and tested target, not a future high-availability variant.

Approved operator sourceSTACKIT ProjectUbuntu VMObservabilityBackup ArchiveSpring Boot JAR + systemdSelf-managed PostgreSQL restricted HTTP/SSHlocalhost SQLrestricted metrics scrapeboot-volume backup

Use an isolated Linux lab with Git, Terraform, Ansible, ShellCheck, SSH/SCP, curl, jq, and PostgreSQL server/client tools including pg_config. The sample dump scripts use runuser and the local postgres OS account and require root privileges in that lab. When Server Backup is enabled, the cutover workflow also needs an authenticated STACKIT CLI. Use the Terraform version that created an existing saved plan; plan files are version-specific.

Start in a parent directory without an existing checkout of the same name. This revision contains the migration workflows and declarative Observability management. Its JAR is unchanged from the artifact pinned by the Replatform reference:

Terminal window
umask 077 &&
git clone https://github.com/stackitcloud/stackit-cmf-Rehost-springboot.git &&
git -C stackit-cmf-Rehost-springboot checkout --detach b9225eb35c64b9ef4761c208fadb9a2356431793 &&
test -f stackit-cmf-Rehost-springboot/scripts/run_cutover.sh &&
cd stackit-cmf-Rehost-springboot &&
printf '%s\n' "Workspace ready. Continue from this Rehost checkout." || {
printf '%s\n' "Preparation failed. Resolve the error before continuing." >&2
false
}

Run the following steps from that checkout only after preparation succeeds. Preserve an existing checkout, state and evidence rather than replacing them. Keep credentials, plans, state, dumps, inventory and evidence private and outside version control; do not enable shell tracing.

Create the private variable file only when it does not already exist:

Terminal window
umask 077
test -e env.tfvars || cp env.tfvars.example env.tfvars
chmod 600 env.tfvars

Edit the file before planning. Configure the approved existing project or project-creation scope, service-account key path, SSH key pair, available image, availability zone, VM flavor and storage. Restrict SSH and application ingress to the source CIDRs actually observed on the target path. Verify the SSH host key through a trusted channel before migration scripts use strict host checking. Set SSH_KEY and SSH_USER for those scripts when they differ from ~/.ssh/id_rsa and ubuntu; keep them consistent with Terraform’s private_ssh_key_path and ssh_user.

Enable VM-local PostgreSQL, Observability and Server Backup for this walkthrough. For the initial runtime deployment, leave postgresql_restore_after_copy = false and the source dump path empty: provisioning must not import the source before rehearsal and cutover approval. Supply the database password through the approved secret mechanism as TF_VAR_postgresql_app_password and retain it securely for subsequent plans; do not put it in the variable file, shell history or documentation.

Confirm permissions, quota, cost approval and the private Terraform backend before initialization. This workflow assumes a prepared execution host, not a complete lab installation procedure.

Create a custom-format source dump and record its SHA-256 checksum, expected row count, and a workload-specific deterministic fingerprint. The repository includes a reproducible eight-row sample:

Terminal window
./scripts/create_source_dump.sh
./scripts/validate_source_dump.sh

The validation script restores into a separate local PostgreSQL cluster before the dump can enter a migration rehearsal. These scripts generate the bundled sample in temporary local clusters; they do not export a running application or the STACKIT VM. Use a fresh artifact directory and do not overwrite a dump already approved for migration. For a real source, export it under the agreed write-freeze procedure and record equivalent checksum, count and fingerprint evidence.

Configure an existing STACKIT project or project creation, restrict SSH and application ingress to approved source CIDRs, and pass the database password through TF_VAR_postgresql_app_password rather than a variable file. Review a saved plan before apply.

Terminal window
terraform init &&
./scripts/check.sh &&
terraform plan -input=false -var-file=env.tfvars -out=tfplan

Stop if initialization, checks or planning fail. Review resource changes, target project, ingress, cost and the disabled initial restore before explicitly applying that saved plan:

Terminal window
terraform apply tfplan

Terraform runs Ansible after provisioning. A successful apply proves completion of that orchestration, not acceptance of migrated data. Check the runtime before beginning rehearsal:

Terminal window
./scripts/validate_deployment.sh

Run the rehearsal against a temporary database on the target VM:

Terminal window
./scripts/run_migration_rehearsal.sh

The rehearsal checks the source evidence, restores the dump, compares row count and fingerprint, and removes its temporary database without changing the production springmusic database.

Set the expected source evidence in env.tfvars:

enable_local_postgresql = true
postgresql_source_dump_local_path = "artifacts/source-postgresql.dump"
postgresql_restore_after_copy = true
postgresql_expected_album_count = 8
postgresql_expected_album_fingerprint = "<source-fingerprint>"

Then execute the explicit approval gate:

Terminal window
./scripts/run_cutover.sh --confirm

The script requires a fully available Server Backup no older than 24 hours, creates a saved Terraform plan that replaces only the Ansible orchestration resource, rejects unrelated infrastructure changes, applies the restore, validates data and runtime behavior, and requires a final no-op Terraform plan. Retries of the same source-dump hash preserve the original database rollback point.

Validation checks the source row count and fingerprint, table ownership, a transactionally rolled-back write as the application role, application reachability, services, and the protected rollback dump.

Terminal window
./scripts/validate_migration.sh
./scripts/validate_deployment.sh
./scripts/verify_rollback.sh

If an approved rollback trigger is met during the rollback window, preserve the current target database and restore the original pre-cutover dump:

Terminal window
./scripts/rollback_postgresql.sh --confirm

With enable_server_backup = true, Terraform enables STACKIT Server Backup and a daily schedule for the VM boot volume. The default retention is 14 days. Cutover requires a fully available backup no older than 24 hours. Backup creation and status were validated against the real target; an in-place Server Backup restore remains a disruptive disaster-recovery operation and must be rehearsed in a separate recovery environment.

The Server Backup complements the PostgreSQL pre-restore dump. It does not replace database-level consistency checks or the application rollback procedure.

Follow workspace preparation, private target configuration, source evidence and reviewed provisioning in that order. Rehearse before the approved cutover and retain the acceptance evidence afterwards. The repository scripts invoke terraform explicitly; an OpenTofu-only execution host needs a separately reviewed adaptation, not just replacement of commands in this page.

The implementation provisions STACKIT Observability and scrapes node exporter. The Grafana provider manages the included dashboard in the SCF Rehost folder with the existing Thanos datasource, waiting for instance readiness instead of silently skipping dashboard creation.

Verify live VM, Spring Boot and PostgreSQL metrics through grafana_dashboard_url, then require a no-op plan. Seven panels cover the active baseline; the synthetic request panel is included only when the local load generator is enabled. Synthetic requests do not represent all application traffic. The deployment validation also checks exporter metrics and service health after apply and database restore.

Initial Grafana admin credentials remain a temporary authentication dependency. Protect state and saved plans. Notification receivers, alert-routing ownership, application logs and distributed tracing require separate configuration and acceptance before production use.

This example can be run in two valid setup modes.

  • With an existing Application Landing Zone: Reuse the already defined project context and service account setup from your landing zone implementation. Use the known project ID as deployment target (for example with create_project = false and project_id = "...") and the service account JSON configured for that landing zone scope.
  • Standalone without existing landing zone project: Let the example create a dedicated project. In this mode, set create_project = true and provide parent_container_id with an existing folder/container where the service account has sufficient permissions.

For landing-zone design guidance, see Application Landing Zone.

Use this as a minimal starting point in env.tfvars.

create_project = true
target_project_name = "cmf-rehost-springboot"
target_project_owner_email = "owner@example.com"
parent_container_id = "cmf-xxxxxxxx"
service_account_key_path = "~/.ssh/cmf-sa.json"
public_ssh_key_path = "~/.ssh/id_rsa.pub"
private_ssh_key_path = "~/.ssh/id_rsa"
availability_zone = "eu01-1"
machine_type = "g2i.2"
ssh_allowed_cidr = "203.0.113.10/32"
app_allowed_cidr = "203.0.113.10/32"
enable_local_postgresql = true
enable_observability = true
enable_server_backup = true
  • Default artifact path: ansible/files/springboot-app.jar
  • Default variable: jar_local_path points to the same file.
  • Alternative artifact: Replace the file or override jar_local_path.

When the artifact changes, Terraform detects the checksum difference and re-runs the Ansible deployment step.

Every rehearsal, cutover, and rollback writes an evidence.env file below artifacts/evidence/<timestamp>-<mode>/. Accept a cutover only when the evidence records the expected checksum, row count, fingerprint, target VM, successful runtime validation, and final Terraform no-op.

Terminal window
./scripts/validate_migration.sh
./scripts/validate_deployment.sh
terraform plan -var-file=env.tfvars -detailed-exitcode

Use the same approved source path, restore flag, expected count and fingerprint as the completed cutover. Exit code 0 means no changes, 2 means changes are proposed, and 1 means an error. A plan alone is not proof of apply. The cutover workflow records status=passed and terraform_noop=true only after applying its saved plan and completing data and runtime validation.

  • Prepare: Approve the target design, access paths, source evidence and recovery responsibilities.
  • Provision: Review and apply the infrastructure plan, then verify the VM runtime independently.
  • Rehearse: Restore into an isolated database and verify integrity without changing the application database.
  • Cut over: Freeze source writes, approve the final dump and allow only the intended orchestration change.
  • Accept or recover: Require data, runtime and no-op evidence; invoke the agreed rollback before its deadline when acceptance fails.
  • Handover: Transfer monitoring, backup ownership, evidence and source-retention decisions before optimization.

The Replatform reference reuses this repository’s pinned Spring Music JAR and sample-data generator. Its sample walkthrough therefore needs this checkout and validated dump artifacts, but not a newly provisioned Rehost VM. Do not create another VM solely to generate the local sample.

For an actual migration from the Rehost VM to SKE and PostgreSQL Flex, verify that the VM is currently healthy, freeze its application writes, export its real PostgreSQL database and qualify that export. A historical successful Rehost apply does not establish current reachability or make the local sample equivalent to live VM data. Source export, acceptance and source failback need their own approved procedure.

  • Keep operational procedures aligned with the runbook and migration governance.
  • For production usage, adapt security controls, sizing, image selection, and life cycle automation.
SAFE

Retain Apply and Acceptance Evidence

The Migration Framework defines the strategy, design, landing-zone, migration, and operations principles for Rehost workloads. This asset applies those principles to one concrete, runnable Spring Boot and PostgreSQL implementation on STACKIT.

The maintained repository is the source of truth for Terraform, Ansible, application artifacts, migration scripts, validation, and runtime configuration. This asset explains how to use that implementation without duplicating its complete source code.

Code & registry github.com STACKIT CMF Rehost Spring Boot repository Open the runnable Terraform and Ansible reference implementation for the Spring Boot and PostgreSQL Rehost path. Open the repository

This example represents an application Rehost path for a Spring Boot workload (Spring Music sample). The migration focus is the runtime relocation of the application to STACKIT:

  • Infrastructure Rehost: Terraform provisions one STACKIT VM, its boot volume, network, public IP, security group, SSH key, Observability resources, and an optional Server Backup schedule.
  • Application Rehost: Ansible installs Java, deploys the Spring Boot JAR, and manages it with systemd without changing the application runtime model.
  • Stateful Rehost: Ansible installs self-managed PostgreSQL on the same VM. The database engine is not changed to a managed service as part of this path.
  • Controlled migration: The repository provides separate rehearsal, cutover, validation, and rollback workflows with machine-readable evidence.

The validated baseline deliberately does not include an application load balancer, DNS switch, multiple VMs, Kubernetes, Cloud Foundry, or PostgreSQL Flex. Add those only as separately designed and tested extensions; they are not implied by this Rehost example.

  • Provisioning: Terraform resources for network, security, one VM, boot volume, SSH key, and public IP.
  • Configuration bridge: Terraform triggers Ansible after infrastructure provisioning.
  • Application deployment: Ansible deploys a concrete Spring Boot artifact and configures a systemd service.
  • VM-local database path: PostgreSQL installation, application role and database bootstrap, and a dump-based restore with ownership and data-integrity checks.
  • Operations baseline: Node exporter metrics are scraped by STACKIT Observability, and Terraform can enable daily Server Backup for the VM boot volume.
  • Migration controls: Source dump generation, independent restore validation, rehearsal, cutover, rollback verification, rollback execution, and evidence capture.
  • Deployable sample artifact: A ready-to-run JAR is included in the repository and used by default deployment settings.

To keep automation behavior consistent across CMF examples, use this common flag model in your Terraform variable set:

  • setup_project: create/use project context.
  • setup_observability: enable/disable observability resources.
  • setup_database: enable/disable optional VM-local PostgreSQL path.
  • setup_workload: enable/disable workload installation on VM.
  • setup_loadgen: enable/disable optional synthetic load generation.
  • setup_dns: accepted by the shared wrapper but unsupported in this baseline.

In the current Rehost repository, these map to existing switches (create_project, enable_observability, enable_local_postgresql, and load-generation related flags).

The following diagram shows the implemented and tested target, not a future high-availability variant.

Approved operator sourceSTACKIT ProjectUbuntu VMObservabilityBackup ArchiveSpring Boot JAR + systemdSelf-managed PostgreSQL restricted HTTP/SSHlocalhost SQLrestricted metrics scrapeboot-volume backup

Use an isolated Linux lab with Git, Terraform, Ansible, ShellCheck, SSH/SCP, curl, jq, and PostgreSQL server/client tools including pg_config. The sample dump scripts use runuser and the local postgres OS account and require root privileges in that lab. When Server Backup is enabled, the cutover workflow also needs an authenticated STACKIT CLI. Use the Terraform version that created an existing saved plan; plan files are version-specific.

Start in a parent directory without an existing checkout of the same name. This revision contains the migration workflows and declarative Observability management. Its JAR is unchanged from the artifact pinned by the Replatform reference:

Terminal window
umask 077 &&
git clone https://github.com/stackitcloud/stackit-cmf-Rehost-springboot.git &&
git -C stackit-cmf-Rehost-springboot checkout --detach b9225eb35c64b9ef4761c208fadb9a2356431793 &&
test -f stackit-cmf-Rehost-springboot/scripts/run_cutover.sh &&
cd stackit-cmf-Rehost-springboot &&
printf '%s\n' "Workspace ready. Continue from this Rehost checkout." || {
printf '%s\n' "Preparation failed. Resolve the error before continuing." >&2
false
}

Run the following steps from that checkout only after preparation succeeds. Preserve an existing checkout, state and evidence rather than replacing them. Keep credentials, plans, state, dumps, inventory and evidence private and outside version control; do not enable shell tracing.

Create the private variable file only when it does not already exist:

Terminal window
umask 077
test -e env.tfvars || cp env.tfvars.example env.tfvars
chmod 600 env.tfvars

Edit the file before planning. Configure the approved existing project or project-creation scope, service-account key path, SSH key pair, available image, availability zone, VM flavor and storage. Restrict SSH and application ingress to the source CIDRs actually observed on the target path. Verify the SSH host key through a trusted channel before migration scripts use strict host checking. Set SSH_KEY and SSH_USER for those scripts when they differ from ~/.ssh/id_rsa and ubuntu; keep them consistent with Terraform’s private_ssh_key_path and ssh_user.

Enable VM-local PostgreSQL, Observability and Server Backup for this walkthrough. For the initial runtime deployment, leave postgresql_restore_after_copy = false and the source dump path empty: provisioning must not import the source before rehearsal and cutover approval. Supply the database password through the approved secret mechanism as TF_VAR_postgresql_app_password and retain it securely for subsequent plans; do not put it in the variable file, shell history or documentation.

Confirm permissions, quota, cost approval and the private Terraform backend before initialization. This workflow assumes a prepared execution host, not a complete lab installation procedure.

Create a custom-format source dump and record its SHA-256 checksum, expected row count, and a workload-specific deterministic fingerprint. The repository includes a reproducible eight-row sample:

Terminal window
./scripts/create_source_dump.sh
./scripts/validate_source_dump.sh

The validation script restores into a separate local PostgreSQL cluster before the dump can enter a migration rehearsal. These scripts generate the bundled sample in temporary local clusters; they do not export a running application or the STACKIT VM. Use a fresh artifact directory and do not overwrite a dump already approved for migration. For a real source, export it under the agreed write-freeze procedure and record equivalent checksum, count and fingerprint evidence.

Configure an existing STACKIT project or project creation, restrict SSH and application ingress to approved source CIDRs, and pass the database password through TF_VAR_postgresql_app_password rather than a variable file. Review a saved plan before apply.

Terminal window
terraform init &&
./scripts/check.sh &&
terraform plan -input=false -var-file=env.tfvars -out=tfplan

Stop if initialization, checks or planning fail. Review resource changes, target project, ingress, cost and the disabled initial restore before explicitly applying that saved plan:

Terminal window
terraform apply tfplan

Terraform runs Ansible after provisioning. A successful apply proves completion of that orchestration, not acceptance of migrated data. Check the runtime before beginning rehearsal:

Terminal window
./scripts/validate_deployment.sh

Run the rehearsal against a temporary database on the target VM:

Terminal window
./scripts/run_migration_rehearsal.sh

The rehearsal checks the source evidence, restores the dump, compares row count and fingerprint, and removes its temporary database without changing the production springmusic database.

Set the expected source evidence in env.tfvars:

enable_local_postgresql = true
postgresql_source_dump_local_path = "artifacts/source-postgresql.dump"
postgresql_restore_after_copy = true
postgresql_expected_album_count = 8
postgresql_expected_album_fingerprint = "<source-fingerprint>"

Then execute the explicit approval gate:

Terminal window
./scripts/run_cutover.sh --confirm

The script requires a fully available Server Backup no older than 24 hours, creates a saved Terraform plan that replaces only the Ansible orchestration resource, rejects unrelated infrastructure changes, applies the restore, validates data and runtime behavior, and requires a final no-op Terraform plan. Retries of the same source-dump hash preserve the original database rollback point.

Validation checks the source row count and fingerprint, table ownership, a transactionally rolled-back write as the application role, application reachability, services, and the protected rollback dump.

Terminal window
./scripts/validate_migration.sh
./scripts/validate_deployment.sh
./scripts/verify_rollback.sh

If an approved rollback trigger is met during the rollback window, preserve the current target database and restore the original pre-cutover dump:

Terminal window
./scripts/rollback_postgresql.sh --confirm

With enable_server_backup = true, Terraform enables STACKIT Server Backup and a daily schedule for the VM boot volume. The default retention is 14 days. Cutover requires a fully available backup no older than 24 hours. Backup creation and status were validated against the real target; an in-place Server Backup restore remains a disruptive disaster-recovery operation and must be rehearsed in a separate recovery environment.

The Server Backup complements the PostgreSQL pre-restore dump. It does not replace database-level consistency checks or the application rollback procedure.

Follow workspace preparation, private target configuration, source evidence and reviewed provisioning in that order. Rehearse before the approved cutover and retain the acceptance evidence afterwards. The repository scripts invoke terraform explicitly; an OpenTofu-only execution host needs a separately reviewed adaptation, not just replacement of commands in this page.

The implementation provisions STACKIT Observability and scrapes node exporter. The Grafana provider manages the included dashboard in the SCF Rehost folder with the existing Thanos datasource, waiting for instance readiness instead of silently skipping dashboard creation.

Verify live VM, Spring Boot and PostgreSQL metrics through grafana_dashboard_url, then require a no-op plan. Seven panels cover the active baseline; the synthetic request panel is included only when the local load generator is enabled. Synthetic requests do not represent all application traffic. The deployment validation also checks exporter metrics and service health after apply and database restore.

Initial Grafana admin credentials remain a temporary authentication dependency. Protect state and saved plans. Notification receivers, alert-routing ownership, application logs and distributed tracing require separate configuration and acceptance before production use.

This example can be run in two valid setup modes.

  • With an existing Application Landing Zone: Reuse the already defined project context and service account setup from your landing zone implementation. Use the known project ID as deployment target (for example with create_project = false and project_id = "...") and the service account JSON configured for that landing zone scope.
  • Standalone without existing landing zone project: Let the example create a dedicated project. In this mode, set create_project = true and provide parent_container_id with an existing folder/container where the service account has sufficient permissions.

For landing-zone design guidance, see Application Landing Zone.

Use this as a minimal starting point in env.tfvars.

create_project = true
target_project_name = "cmf-rehost-springboot"
target_project_owner_email = "owner@example.com"
parent_container_id = "cmf-xxxxxxxx"
service_account_key_path = "~/.ssh/cmf-sa.json"
public_ssh_key_path = "~/.ssh/id_rsa.pub"
private_ssh_key_path = "~/.ssh/id_rsa"
availability_zone = "eu01-1"
machine_type = "g2i.2"
ssh_allowed_cidr = "203.0.113.10/32"
app_allowed_cidr = "203.0.113.10/32"
enable_local_postgresql = true
enable_observability = true
enable_server_backup = true
  • Default artifact path: ansible/files/springboot-app.jar
  • Default variable: jar_local_path points to the same file.
  • Alternative artifact: Replace the file or override jar_local_path.

When the artifact changes, Terraform detects the checksum difference and re-runs the Ansible deployment step.

Every rehearsal, cutover, and rollback writes an evidence.env file below artifacts/evidence/<timestamp>-<mode>/. Accept a cutover only when the evidence records the expected checksum, row count, fingerprint, target VM, successful runtime validation, and final Terraform no-op.

Terminal window
./scripts/validate_migration.sh
./scripts/validate_deployment.sh
terraform plan -var-file=env.tfvars -detailed-exitcode

Use the same approved source path, restore flag, expected count and fingerprint as the completed cutover. Exit code 0 means no changes, 2 means changes are proposed, and 1 means an error. A plan alone is not proof of apply. The cutover workflow records status=passed and terraform_noop=true only after applying its saved plan and completing data and runtime validation.

  • Prepare: Approve the target design, access paths, source evidence and recovery responsibilities.
  • Provision: Review and apply the infrastructure plan, then verify the VM runtime independently.
  • Rehearse: Restore into an isolated database and verify integrity without changing the application database.
  • Cut over: Freeze source writes, approve the final dump and allow only the intended orchestration change.
  • Accept or recover: Require data, runtime and no-op evidence; invoke the agreed rollback before its deadline when acceptance fails.
  • Handover: Transfer monitoring, backup ownership, evidence and source-retention decisions before optimization.

The Replatform reference reuses this repository’s pinned Spring Music JAR and sample-data generator. Its sample walkthrough therefore needs this checkout and validated dump artifacts, but not a newly provisioned Rehost VM. Do not create another VM solely to generate the local sample.

For an actual migration from the Rehost VM to SKE and PostgreSQL Flex, verify that the VM is currently healthy, freeze its application writes, export its real PostgreSQL database and qualify that export. A historical successful Rehost apply does not establish current reachability or make the local sample equivalent to live VM data. Source export, acceptance and source failback need their own approved procedure.

  • Keep operational procedures aligned with the runbook and migration governance.
  • For production usage, adapt security controls, sizing, image selection, and life cycle automation.
GOAL

Stabilization

  • Migration strategy: Rehost (lift and shift)
  • Application type: Spring Boot service (JAR), no Kubernetes target
  • Target platform: VM-based runtime on STACKIT
  • Data backend: PostgreSQL
  • In scope: Terraform provisioning, Ansible configuration, source dump evidence, temporary-database rehearsal, controlled restore, runtime validation, rollback, Observability, and Server Backup schedule.
  • Out of scope: Code refactoring, database engine change, load balancing, DNS switch, TLS termination, multi-VM availability, Kubernetes, Cloud Foundry, and PostgreSQL Flex.
  • Assumptions: The source uses a PostgreSQL version compatible with the target restore tools, and approved source CIDRs are known for SSH and application access.
  • Migration lead: Coordinates timeline, checkpoints, and go/no-go decision.
  • Application owner: Validates app behavior and business-critical user journeys.
  • Platform engineer: Prepares the VM, restricted network rules, monitoring, and backup schedule.
  • DB owner: Executes DB backup, restore, consistency checks, and rollback trigger.
  • Operations owner: Accepts handover and owns Day-1/Day-2 incident response.
  • Access readiness: SSH, deployment credentials, DB access, secrets access validated.
  • Baseline captured: Current versions, environment variables, ports, certificates, scheduled jobs documented.
  • Capacity validated: CPU, RAM, disk IOPS, and storage capacity on target VM confirmed.
  • Security controls ready: Firewall rules, IAM mapping, TLS cert chain, and logging in place.
  • Source evidence ready: Dump checksum, expected record count, and deterministic data fingerprint recorded.
  • Rollback readiness: Pre-restore dump path, expected original record count, authority, and deadline agreed.
  1. Create target app user and required filesystem layout.
  2. Install Java runtime and supporting OS packages.
  3. Deploy app artifact to target path.
  4. Configure service unit (systemd) and environment file.
  5. Install self-managed PostgreSQL, create the application role and database, and restrict access to localhost.
  6. Enable node exporter, Observability scraping, and the Server Backup schedule.
  1. Create a custom-format PostgreSQL dump without source ownership or privileges.
  2. Record and validate its checksum, expected row count, and deterministic fingerprint.
  3. Run ./scripts/run_migration_rehearsal.sh against a temporary target database.
  4. Confirm that rehearsal removed its temporary database and left the production target database unchanged.
  5. Verify the original target rollback dump in a separate temporary database.
  1. Freeze source writes and create the final approved dump.
  2. Run ./scripts/run_cutover.sh --confirm with the approved source evidence.
  3. Require the saved Terraform plan to change only the Ansible orchestration resource.
  4. Validate row count, fingerprint, ownership, application-role write behavior, services, and endpoint.
  5. Require a final Terraform no-op plan, record acceptance, and start stabilization watch.
  • Technical health: Spring Boot, PostgreSQL, and node exporter active; local and approved public HTTP checks pass.
  • Functional checks: The application returns the migrated Spring Music records.
  • Data checks: Expected row count and fingerprint match; table ownership belongs to the application role.
  • Security checks: Runtime environment and rollback dump have root-only or database-owner-only permissions.
  • Operations checks: Observability scrape, Server Backup schedule, evidence files, and escalation ownership verified.
  • Critical functional failure: Core business flow unavailable after fix window.
  • Data integrity risk: Mismatch in critical records with no fast remediation.
  • Operational instability: Repeated restarts or unresolved severe alerts.
  1. Invoke the approved rollback decision before the deadline and preserve logs and evidence.
  2. Run ./scripts/rollback_postgresql.sh --confirm to stop Spring Boot and preserve the current target database.
  3. Restore the protected pre-cutover dump and validate the expected original row count.
  4. Restart Spring Boot and validate local and approved public reachability.
  5. Resume the agreed source or target operating state and publish the rollback evidence and decision.
  • Artifacts delivered: Final config set, deployment manifest, validation evidence, rollback log.
  • Ownership transfer: Named on-call owner and escalation route confirmed.
  • Stabilization period: 24-72 hours with enhanced monitoring and daily status check.
  • Exit criteria: No critical alerts, stable key metrics, and business owner sign-off.

The Migration Framework defines the strategy, design, landing-zone, migration, and operations principles for Rehost workloads. This asset applies those principles to one concrete, runnable Spring Boot and PostgreSQL implementation on STACKIT.

The maintained repository is the source of truth for Terraform, Ansible, application artifacts, migration scripts, validation, and runtime configuration. This asset explains how to use that implementation without duplicating its complete source code.

Code & registry github.com STACKIT CMF Rehost Spring Boot repository Open the runnable Terraform and Ansible reference implementation for the Spring Boot and PostgreSQL Rehost path. Open the repository

This example represents an application Rehost path for a Spring Boot workload (Spring Music sample). The migration focus is the runtime relocation of the application to STACKIT:

  • Infrastructure Rehost: Terraform provisions one STACKIT VM, its boot volume, network, public IP, security group, SSH key, Observability resources, and an optional Server Backup schedule.
  • Application Rehost: Ansible installs Java, deploys the Spring Boot JAR, and manages it with systemd without changing the application runtime model.
  • Stateful Rehost: Ansible installs self-managed PostgreSQL on the same VM. The database engine is not changed to a managed service as part of this path.
  • Controlled migration: The repository provides separate rehearsal, cutover, validation, and rollback workflows with machine-readable evidence.

The validated baseline deliberately does not include an application load balancer, DNS switch, multiple VMs, Kubernetes, Cloud Foundry, or PostgreSQL Flex. Add those only as separately designed and tested extensions; they are not implied by this Rehost example.

  • Provisioning: Terraform resources for network, security, one VM, boot volume, SSH key, and public IP.
  • Configuration bridge: Terraform triggers Ansible after infrastructure provisioning.
  • Application deployment: Ansible deploys a concrete Spring Boot artifact and configures a systemd service.
  • VM-local database path: PostgreSQL installation, application role and database bootstrap, and a dump-based restore with ownership and data-integrity checks.
  • Operations baseline: Node exporter metrics are scraped by STACKIT Observability, and Terraform can enable daily Server Backup for the VM boot volume.
  • Migration controls: Source dump generation, independent restore validation, rehearsal, cutover, rollback verification, rollback execution, and evidence capture.
  • Deployable sample artifact: A ready-to-run JAR is included in the repository and used by default deployment settings.

To keep automation behavior consistent across CMF examples, use this common flag model in your Terraform variable set:

  • setup_project: create/use project context.
  • setup_observability: enable/disable observability resources.
  • setup_database: enable/disable optional VM-local PostgreSQL path.
  • setup_workload: enable/disable workload installation on VM.
  • setup_loadgen: enable/disable optional synthetic load generation.
  • setup_dns: accepted by the shared wrapper but unsupported in this baseline.

In the current Rehost repository, these map to existing switches (create_project, enable_observability, enable_local_postgresql, and load-generation related flags).

The following diagram shows the implemented and tested target, not a future high-availability variant.

Approved operator sourceSTACKIT ProjectUbuntu VMObservabilityBackup ArchiveSpring Boot JAR + systemdSelf-managed PostgreSQL restricted HTTP/SSHlocalhost SQLrestricted metrics scrapeboot-volume backup

Use an isolated Linux lab with Git, Terraform, Ansible, ShellCheck, SSH/SCP, curl, jq, and PostgreSQL server/client tools including pg_config. The sample dump scripts use runuser and the local postgres OS account and require root privileges in that lab. When Server Backup is enabled, the cutover workflow also needs an authenticated STACKIT CLI. Use the Terraform version that created an existing saved plan; plan files are version-specific.

Start in a parent directory without an existing checkout of the same name. This revision contains the migration workflows and declarative Observability management. Its JAR is unchanged from the artifact pinned by the Replatform reference:

Terminal window
umask 077 &&
git clone https://github.com/stackitcloud/stackit-cmf-Rehost-springboot.git &&
git -C stackit-cmf-Rehost-springboot checkout --detach b9225eb35c64b9ef4761c208fadb9a2356431793 &&
test -f stackit-cmf-Rehost-springboot/scripts/run_cutover.sh &&
cd stackit-cmf-Rehost-springboot &&
printf '%s\n' "Workspace ready. Continue from this Rehost checkout." || {
printf '%s\n' "Preparation failed. Resolve the error before continuing." >&2
false
}

Run the following steps from that checkout only after preparation succeeds. Preserve an existing checkout, state and evidence rather than replacing them. Keep credentials, plans, state, dumps, inventory and evidence private and outside version control; do not enable shell tracing.

Create the private variable file only when it does not already exist:

Terminal window
umask 077
test -e env.tfvars || cp env.tfvars.example env.tfvars
chmod 600 env.tfvars

Edit the file before planning. Configure the approved existing project or project-creation scope, service-account key path, SSH key pair, available image, availability zone, VM flavor and storage. Restrict SSH and application ingress to the source CIDRs actually observed on the target path. Verify the SSH host key through a trusted channel before migration scripts use strict host checking. Set SSH_KEY and SSH_USER for those scripts when they differ from ~/.ssh/id_rsa and ubuntu; keep them consistent with Terraform’s private_ssh_key_path and ssh_user.

Enable VM-local PostgreSQL, Observability and Server Backup for this walkthrough. For the initial runtime deployment, leave postgresql_restore_after_copy = false and the source dump path empty: provisioning must not import the source before rehearsal and cutover approval. Supply the database password through the approved secret mechanism as TF_VAR_postgresql_app_password and retain it securely for subsequent plans; do not put it in the variable file, shell history or documentation.

Confirm permissions, quota, cost approval and the private Terraform backend before initialization. This workflow assumes a prepared execution host, not a complete lab installation procedure.

Create a custom-format source dump and record its SHA-256 checksum, expected row count, and a workload-specific deterministic fingerprint. The repository includes a reproducible eight-row sample:

Terminal window
./scripts/create_source_dump.sh
./scripts/validate_source_dump.sh

The validation script restores into a separate local PostgreSQL cluster before the dump can enter a migration rehearsal. These scripts generate the bundled sample in temporary local clusters; they do not export a running application or the STACKIT VM. Use a fresh artifact directory and do not overwrite a dump already approved for migration. For a real source, export it under the agreed write-freeze procedure and record equivalent checksum, count and fingerprint evidence.

Configure an existing STACKIT project or project creation, restrict SSH and application ingress to approved source CIDRs, and pass the database password through TF_VAR_postgresql_app_password rather than a variable file. Review a saved plan before apply.

Terminal window
terraform init &&
./scripts/check.sh &&
terraform plan -input=false -var-file=env.tfvars -out=tfplan

Stop if initialization, checks or planning fail. Review resource changes, target project, ingress, cost and the disabled initial restore before explicitly applying that saved plan:

Terminal window
terraform apply tfplan

Terraform runs Ansible after provisioning. A successful apply proves completion of that orchestration, not acceptance of migrated data. Check the runtime before beginning rehearsal:

Terminal window
./scripts/validate_deployment.sh

Run the rehearsal against a temporary database on the target VM:

Terminal window
./scripts/run_migration_rehearsal.sh

The rehearsal checks the source evidence, restores the dump, compares row count and fingerprint, and removes its temporary database without changing the production springmusic database.

Set the expected source evidence in env.tfvars:

enable_local_postgresql = true
postgresql_source_dump_local_path = "artifacts/source-postgresql.dump"
postgresql_restore_after_copy = true
postgresql_expected_album_count = 8
postgresql_expected_album_fingerprint = "<source-fingerprint>"

Then execute the explicit approval gate:

Terminal window
./scripts/run_cutover.sh --confirm

The script requires a fully available Server Backup no older than 24 hours, creates a saved Terraform plan that replaces only the Ansible orchestration resource, rejects unrelated infrastructure changes, applies the restore, validates data and runtime behavior, and requires a final no-op Terraform plan. Retries of the same source-dump hash preserve the original database rollback point.

Validation checks the source row count and fingerprint, table ownership, a transactionally rolled-back write as the application role, application reachability, services, and the protected rollback dump.

Terminal window
./scripts/validate_migration.sh
./scripts/validate_deployment.sh
./scripts/verify_rollback.sh

If an approved rollback trigger is met during the rollback window, preserve the current target database and restore the original pre-cutover dump:

Terminal window
./scripts/rollback_postgresql.sh --confirm

With enable_server_backup = true, Terraform enables STACKIT Server Backup and a daily schedule for the VM boot volume. The default retention is 14 days. Cutover requires a fully available backup no older than 24 hours. Backup creation and status were validated against the real target; an in-place Server Backup restore remains a disruptive disaster-recovery operation and must be rehearsed in a separate recovery environment.

The Server Backup complements the PostgreSQL pre-restore dump. It does not replace database-level consistency checks or the application rollback procedure.

Follow workspace preparation, private target configuration, source evidence and reviewed provisioning in that order. Rehearse before the approved cutover and retain the acceptance evidence afterwards. The repository scripts invoke terraform explicitly; an OpenTofu-only execution host needs a separately reviewed adaptation, not just replacement of commands in this page.

The implementation provisions STACKIT Observability and scrapes node exporter. The Grafana provider manages the included dashboard in the SCF Rehost folder with the existing Thanos datasource, waiting for instance readiness instead of silently skipping dashboard creation.

Verify live VM, Spring Boot and PostgreSQL metrics through grafana_dashboard_url, then require a no-op plan. Seven panels cover the active baseline; the synthetic request panel is included only when the local load generator is enabled. Synthetic requests do not represent all application traffic. The deployment validation also checks exporter metrics and service health after apply and database restore.

Initial Grafana admin credentials remain a temporary authentication dependency. Protect state and saved plans. Notification receivers, alert-routing ownership, application logs and distributed tracing require separate configuration and acceptance before production use.

This example can be run in two valid setup modes.

  • With an existing Application Landing Zone: Reuse the already defined project context and service account setup from your landing zone implementation. Use the known project ID as deployment target (for example with create_project = false and project_id = "...") and the service account JSON configured for that landing zone scope.
  • Standalone without existing landing zone project: Let the example create a dedicated project. In this mode, set create_project = true and provide parent_container_id with an existing folder/container where the service account has sufficient permissions.

For landing-zone design guidance, see Application Landing Zone.

Use this as a minimal starting point in env.tfvars.

create_project = true
target_project_name = "cmf-rehost-springboot"
target_project_owner_email = "owner@example.com"
parent_container_id = "cmf-xxxxxxxx"
service_account_key_path = "~/.ssh/cmf-sa.json"
public_ssh_key_path = "~/.ssh/id_rsa.pub"
private_ssh_key_path = "~/.ssh/id_rsa"
availability_zone = "eu01-1"
machine_type = "g2i.2"
ssh_allowed_cidr = "203.0.113.10/32"
app_allowed_cidr = "203.0.113.10/32"
enable_local_postgresql = true
enable_observability = true
enable_server_backup = true
  • Default artifact path: ansible/files/springboot-app.jar
  • Default variable: jar_local_path points to the same file.
  • Alternative artifact: Replace the file or override jar_local_path.

When the artifact changes, Terraform detects the checksum difference and re-runs the Ansible deployment step.

Every rehearsal, cutover, and rollback writes an evidence.env file below artifacts/evidence/<timestamp>-<mode>/. Accept a cutover only when the evidence records the expected checksum, row count, fingerprint, target VM, successful runtime validation, and final Terraform no-op.

Terminal window
./scripts/validate_migration.sh
./scripts/validate_deployment.sh
terraform plan -var-file=env.tfvars -detailed-exitcode

Use the same approved source path, restore flag, expected count and fingerprint as the completed cutover. Exit code 0 means no changes, 2 means changes are proposed, and 1 means an error. A plan alone is not proof of apply. The cutover workflow records status=passed and terraform_noop=true only after applying its saved plan and completing data and runtime validation.

  • Prepare: Approve the target design, access paths, source evidence and recovery responsibilities.
  • Provision: Review and apply the infrastructure plan, then verify the VM runtime independently.
  • Rehearse: Restore into an isolated database and verify integrity without changing the application database.
  • Cut over: Freeze source writes, approve the final dump and allow only the intended orchestration change.
  • Accept or recover: Require data, runtime and no-op evidence; invoke the agreed rollback before its deadline when acceptance fails.
  • Handover: Transfer monitoring, backup ownership, evidence and source-retention decisions before optimization.

The Replatform reference reuses this repository’s pinned Spring Music JAR and sample-data generator. Its sample walkthrough therefore needs this checkout and validated dump artifacts, but not a newly provisioned Rehost VM. Do not create another VM solely to generate the local sample.

For an actual migration from the Rehost VM to SKE and PostgreSQL Flex, verify that the VM is currently healthy, freeze its application writes, export its real PostgreSQL database and qualify that export. A historical successful Rehost apply does not establish current reachability or make the local sample equivalent to live VM data. Source export, acceptance and source failback need their own approved procedure.

  • Keep operational procedures aligned with the runbook and migration governance.
  • For production usage, adapt security controls, sizing, image selection, and life cycle automation.
GOAL

Optimization

Return from the concrete migration example to the Migration Framework. Start optimization only after stable cutover, use representative production telemetry, implement one controlled change at a time, and validate its effect on reliability, performance, and cost.

MigrateOptimizeOverview In 7 trails

Optimize starts when workloads run on STACKIT and real operating data is available. The module converts post-cutover observations into measurable improvements for performance, stability, and cost efficiency.

Optimize is not a one-time task. It is an iterative cycle that can overlap with early stabilization and post-cutover care.

Many right-sizing and tuning decisions are only reliable under real load patterns. After cutover, teams can use production telemetry to separate assumptions from actual behavior.

  1. Collect runtime evidence: utilization, latency, error rates, throughput, and cost drivers.
  2. Identify bottlenecks and waste patterns at workload, platform, and data layers.
  3. Prioritize actions by business impact, risk reduction, and FinOps effect.
  4. Implement tuning changes in controlled increments.
  5. Validate outcomes against SLO, reliability, and cost targets.
  6. Feed lessons learned into future migration waves and operating standards.
  • Rightsizing: Align compute, storage, and network capacity with actual demand profiles.
  • Performance tuning: Improve latency and throughput through configuration, scaling, and architecture adjustments.
  • Reliability hardening: Reduce incident frequency through better resilience, observability, and failure handling.
  • FinOps controls: Improve cost transparency, remove waste, and optimize run-rate efficiency.

Optimization decisions should be based on runtime evidence, not assumptions. For practical implementation, combine workload telemetry, alerting, and controlled infrastructure changes.

  • Managed observability baseline: Use STACKIT Observability to collect metrics, logs, and traces with Grafana, Prometheus, Thanos, Loki, and Tempo.
  • Detection logic: Define explicit thresholds and observation windows for low utilization and overload conditions.
  • Run path: Apply rightsizing through IaC changes (for example VM flavor changes) with rollback checkpoints.
  • Validation loop: Re-measure SLO, error rates, and run-cost after each tuning increment.
Filters

Within a group every tick widens the list. Groups narrow each other.

Framework

Status

Topics

Asset title
Framework
Asset type

For Replatform workloads on Kubernetes, optimization spans multiple layers and should be coordinated as one control loop.

  • Pod scaling: Use HPA to adapt replica count to workload pressure with explicit min/max limits.
  • Node pool scaling: Keep sufficient cluster headroom and tune machine type (flavor) for CPU/memory density requirements.
  • Ingress scaling: Re-evaluate load balancer service plan when ingress throughput or connection behavior becomes a bottleneck.
  • Storage rightsizing: Select storage classes based on performance requirements for persistent workloads.
  • Validation discipline: Re-check latency, error rate, and cost after every incremental tuning change.

Primary inputs

Cutover reports, incident trends, SLO measurements, telemetry baselines, and cost reports.

Optimization outputs

Prioritized improvement backlog, validated tuning changes, and updated runbook standards.

Governance outcome

Clear trade-off decisions between performance, resilience, and cost with documented ownership.

  • Optimize follows technical migration delivery in Migrate.
  • Optimize can run in parallel with early post-cutover care activities, while ownership for this care model is covered in the Run phase.
  • Deeper architectural redesign remains in Refactor.
OPS

Continue the Same Reference Implementation

This asset continues the same Spring Boot and PostgreSQL reference implementation used for provisioning, migration, cutover, and stabilization. It does not introduce another example or repository. The existing Terraform variables, Ansible configuration, Observability instance, and validation workflows remain the technical baseline for Optimize.

The Optimize extension answers one practical question: how to detect overprovisioning or underprovisioning and then change VM or storage capacity through a controlled IaC workflow.

Code & registry github.com STACKIT CMF Rehost Spring Boot repository Continue with the same Terraform and Ansible reference implementation used by the preceding Rehost migration steps. Open the repository Rehost implementation asset

Use the managed STACKIT Observability stack as evidence source.

  • Prometheus: metric collection
  • Thanos: long-term metric retention
  • Grafana Loki: log analysis
  • Grafana Tempo: distributed traces
  • Grafana: dashboards and visualization

Architecture reference:

Use the dashboard to review infrastructure saturation, application health, request behavior, and alert history together before changing VM capacity.

Grafana dashboard for Rehost Spring Boot observability and VM rightsizing decisions

Define technical thresholds before changing capacity.

  • Candidate for downsizing: CPU p95 under 30% and memory p95 under 50% for at least 14 days.
  • Scale-up candidate: CPU p95 over 75% or memory p95 over 80% during business load windows for at least 3 consecutive days.
  • Stability guardrail: No unresolved critical alerts and no regression in error-rate SLOs.

Keep thresholds workload-specific and validate with business traffic patterns.

Database visibility for optimize decisions

Section titled “Database visibility for optimize decisions”

For stateful Rehost workloads, include database signals in the same dashboard review cycle.

  • Connection pressure: active connection trend and burst behavior.
  • Database growth: database size progression over time.
  • Transaction behavior: commit/rollback trend for stability checks.

Use these metrics together with VM signals to avoid CPU-only or memory-only optimization decisions.

PostgreSQL Flex optimization path for migrated data tiers

Section titled “PostgreSQL Flex optimization path for migrated data tiers”

If the Rehost workload later moves its database tier to PostgreSQL Flex, include database-tier rightsizing and tuning in the same optimize cycle.

Use the offered Flex combinations and storage performance limits to assess a database-tier change. Keep the VM thresholds above workload-specific; the product catalog does not supply acceptance criteria or prove that an infrastructure change is reversible.

From the STACKIT docsFlavors and performance classes › FlavorsSource updated 06.07.2026 · copied 05.10.2026

Notes

  • CPU and RAM is always per node.
  • The system uses up to 15 connections for internal essential processes such as backup, monitoring, etc. These connections will be counted towards the max_connections limit.
What is this?

This section is copied from the STACKIT docs automatically, several times a day. It cannot be changed here. Changes belong in the STACKIT docs.

From the STACKIT docsFlavors and performance classes › Performance ClassesSource updated 06.07.2026 · copied 05.10.2026

Currently, we offer three types of instances. For each type there is a different set of flavors available.

What is this?

This section is copied from the STACKIT docs automatically, several times a day. It cannot be changed here. Changes belong in the STACKIT docs.

  1. Collect baseline metrics and traces for a representative period.
  2. Confirm optimization candidate with dashboards and alert history.
  3. Plan capacity change and rollback checkpoint.
  4. Apply VM size change with Terraform/OpenTofu.
  5. Re-validate latency, error rates, throughput, and cost.
  6. Keep or revert based on objective acceptance criteria.

Example A: Downsize after sustained low utilization

Section titled “Example A: Downsize after sustained low utilization”

Update VM sizing in env.tfvars:

machine_type = "g3i.2"

Apply and inspect plan output:

Terminal window
terraform plan -var-file=env.tfvars
terraform apply -var-file=env.tfvars

Then validate:

  • Service health (systemctl status, synthetic checks)
  • p95 latency and error-rate trend
  • Cost delta in reporting window

Example B: Scale up under sustained overload

Section titled “Example B: Scale up under sustained overload”

Update VM sizing in env.tfvars:

machine_type = "g3i.4"

Apply and validate with the same post-change checks.

If SLOs regress after rightsizing, roll back by restoring the previous machine_type and re-applying IaC. Treat rollback as a standard runbook step, not as an emergency-only path.

  • Depending on platform constraints and machine type, resize can require restart or replacement. Confirm behavior in terraform plan before apply.

In Rehost scenarios, CPU and memory are only one side of rightsizing. Storage performance can also become the limiting factor.

  • When to investigate storage: elevated I/O wait, unstable latency under write-heavy load, or throughput saturation despite available CPU.
  • What to select: a storage service plan and performance class that matches the observed IOPS and throughput profile.
  • Guidance: Block Storage service plans

Select the performance class before provisioning

Section titled “Select the performance class before provisioning”

A Block Storage performance class defines the maximum IOPS and throughput available to the complete volume. Application, database, operating-system, and backup access share this performance envelope. Select the class from measured peak demand, latency requirements, backup activity, and explicit growth headroom before creating the volume.

From the STACKIT docsService plans › Currently available Service Plans (performance classes)Source updated 22.04.2026 · copied 05.10.2026

The following table lists currently available performance classes for the EU01 region:

IOPS - Input/Output Operations per second

Throughput - Throughput in Megabytes per second

Thus, the classes used can be distinguished in detail based on the naming. Example: “Block Storage Premium - Performance Class 2” corresponds to SSD hard disks with max. 1000 IOPS and max. 100 Mbyte/s throughput.

What is this?

This section is copied from the STACKIT docs automatically, several times a day. It cannot be changed here. Changes belong in the STACKIT docs.

For the Rehost baseline, the operating system, Spring Boot application, and PostgreSQL data share the boot volume. Changing its performance class therefore requires a controlled replacement target:

  1. Select the new class from observed IOPS, throughput, latency, and I/O-wait data.
  2. Verify backup and database-level rollback readiness.
  3. Provision the replacement VM and boot volume through IaC with the selected class.
  4. Reapply the Ansible configuration and restore or migrate the workload data.
  5. Validate application behavior, data integrity, storage latency, backup coverage, and cost before switching.

Use a separate data volume when storage capacity or performance must evolve independently from the VM lifecycle. To change its performance class, create a new volume in the required availability model with sufficient capacity, stop writes, migrate and verify the data, switch the attachment or mount, and retain the source volume until acceptance and rollback gates have passed.

Migrate data from Block Storage

Treat storage checks as part of the same Optimize loop and validate latency, error behavior, recovery, and cost impact after any change.

This asset continues the same Spring Boot and PostgreSQL reference implementation used for provisioning, migration, cutover, and stabilization. It does not introduce another example or repository. The existing Terraform variables, Ansible configuration, Observability instance, and validation workflows remain the technical baseline for Optimize.

The Optimize extension answers one practical question: how to detect overprovisioning or underprovisioning and then change VM or storage capacity through a controlled IaC workflow.

Code & registry github.com STACKIT CMF Rehost Spring Boot repository Continue with the same Terraform and Ansible reference implementation used by the preceding Rehost migration steps. Open the repository Rehost implementation asset

Use the managed STACKIT Observability stack as evidence source.

  • Prometheus: metric collection
  • Thanos: long-term metric retention
  • Grafana Loki: log analysis
  • Grafana Tempo: distributed traces
  • Grafana: dashboards and visualization

Architecture reference:

Use the dashboard to review infrastructure saturation, application health, request behavior, and alert history together before changing VM capacity.

Grafana dashboard for Rehost Spring Boot observability and VM rightsizing decisions

Define technical thresholds before changing capacity.

  • Candidate for downsizing: CPU p95 under 30% and memory p95 under 50% for at least 14 days.
  • Scale-up candidate: CPU p95 over 75% or memory p95 over 80% during business load windows for at least 3 consecutive days.
  • Stability guardrail: No unresolved critical alerts and no regression in error-rate SLOs.

Keep thresholds workload-specific and validate with business traffic patterns.

Database visibility for optimize decisions

Section titled “Database visibility for optimize decisions”

For stateful Rehost workloads, include database signals in the same dashboard review cycle.

  • Connection pressure: active connection trend and burst behavior.
  • Database growth: database size progression over time.
  • Transaction behavior: commit/rollback trend for stability checks.

Use these metrics together with VM signals to avoid CPU-only or memory-only optimization decisions.

PostgreSQL Flex optimization path for migrated data tiers

Section titled “PostgreSQL Flex optimization path for migrated data tiers”

If the Rehost workload later moves its database tier to PostgreSQL Flex, include database-tier rightsizing and tuning in the same optimize cycle.

Use the offered Flex combinations and storage performance limits to assess a database-tier change. Keep the VM thresholds above workload-specific; the product catalog does not supply acceptance criteria or prove that an infrastructure change is reversible.

From the STACKIT docsFlavors and performance classes › FlavorsSource updated 06.07.2026 · copied 05.10.2026

Notes

  • CPU and RAM is always per node.
  • The system uses up to 15 connections for internal essential processes such as backup, monitoring, etc. These connections will be counted towards the max_connections limit.
What is this?

This section is copied from the STACKIT docs automatically, several times a day. It cannot be changed here. Changes belong in the STACKIT docs.

From the STACKIT docsFlavors and performance classes › Performance ClassesSource updated 06.07.2026 · copied 05.10.2026

Currently, we offer three types of instances. For each type there is a different set of flavors available.

What is this?

This section is copied from the STACKIT docs automatically, several times a day. It cannot be changed here. Changes belong in the STACKIT docs.

  1. Collect baseline metrics and traces for a representative period.
  2. Confirm optimization candidate with dashboards and alert history.
  3. Plan capacity change and rollback checkpoint.
  4. Apply VM size change with Terraform/OpenTofu.
  5. Re-validate latency, error rates, throughput, and cost.
  6. Keep or revert based on objective acceptance criteria.

Example A: Downsize after sustained low utilization

Section titled “Example A: Downsize after sustained low utilization”

Update VM sizing in env.tfvars:

machine_type = "g3i.2"

Apply and inspect plan output:

Terminal window
terraform plan -var-file=env.tfvars
terraform apply -var-file=env.tfvars

Then validate:

  • Service health (systemctl status, synthetic checks)
  • p95 latency and error-rate trend
  • Cost delta in reporting window

Example B: Scale up under sustained overload

Section titled “Example B: Scale up under sustained overload”

Update VM sizing in env.tfvars:

machine_type = "g3i.4"

Apply and validate with the same post-change checks.

If SLOs regress after rightsizing, roll back by restoring the previous machine_type and re-applying IaC. Treat rollback as a standard runbook step, not as an emergency-only path.

  • Depending on platform constraints and machine type, resize can require restart or replacement. Confirm behavior in terraform plan before apply.

In Rehost scenarios, CPU and memory are only one side of rightsizing. Storage performance can also become the limiting factor.

  • When to investigate storage: elevated I/O wait, unstable latency under write-heavy load, or throughput saturation despite available CPU.
  • What to select: a storage service plan and performance class that matches the observed IOPS and throughput profile.
  • Guidance: Block Storage service plans

Select the performance class before provisioning

Section titled “Select the performance class before provisioning”

A Block Storage performance class defines the maximum IOPS and throughput available to the complete volume. Application, database, operating-system, and backup access share this performance envelope. Select the class from measured peak demand, latency requirements, backup activity, and explicit growth headroom before creating the volume.

From the STACKIT docsService plans › Currently available Service Plans (performance classes)Source updated 22.04.2026 · copied 05.10.2026

The following table lists currently available performance classes for the EU01 region:

IOPS - Input/Output Operations per second

Throughput - Throughput in Megabytes per second

Thus, the classes used can be distinguished in detail based on the naming. Example: “Block Storage Premium - Performance Class 2” corresponds to SSD hard disks with max. 1000 IOPS and max. 100 Mbyte/s throughput.

What is this?

This section is copied from the STACKIT docs automatically, several times a day. It cannot be changed here. Changes belong in the STACKIT docs.

For the Rehost baseline, the operating system, Spring Boot application, and PostgreSQL data share the boot volume. Changing its performance class therefore requires a controlled replacement target:

  1. Select the new class from observed IOPS, throughput, latency, and I/O-wait data.
  2. Verify backup and database-level rollback readiness.
  3. Provision the replacement VM and boot volume through IaC with the selected class.
  4. Reapply the Ansible configuration and restore or migrate the workload data.
  5. Validate application behavior, data integrity, storage latency, backup coverage, and cost before switching.

Use a separate data volume when storage capacity or performance must evolve independently from the VM lifecycle. To change its performance class, create a new volume in the required availability model with sufficient capacity, stop writes, migrate and verify the data, switch the attachment or mount, and retain the source volume until acceptance and rollback gates have passed.

Migrate data from Block Storage

Treat storage checks as part of the same Optimize loop and validate latency, error behavior, recovery, and cost impact after any change.

OPS

Right-size the VM Flavor

This asset continues the same Spring Boot and PostgreSQL reference implementation used for provisioning, migration, cutover, and stabilization. It does not introduce another example or repository. The existing Terraform variables, Ansible configuration, Observability instance, and validation workflows remain the technical baseline for Optimize.

The Optimize extension answers one practical question: how to detect overprovisioning or underprovisioning and then change VM or storage capacity through a controlled IaC workflow.

Code & registry github.com STACKIT CMF Rehost Spring Boot repository Continue with the same Terraform and Ansible reference implementation used by the preceding Rehost migration steps. Open the repository Rehost implementation asset

Use the managed STACKIT Observability stack as evidence source.

  • Prometheus: metric collection
  • Thanos: long-term metric retention
  • Grafana Loki: log analysis
  • Grafana Tempo: distributed traces
  • Grafana: dashboards and visualization

Architecture reference:

Use the dashboard to review infrastructure saturation, application health, request behavior, and alert history together before changing VM capacity.

Grafana dashboard for Rehost Spring Boot observability and VM rightsizing decisions

Define technical thresholds before changing capacity.

  • Candidate for downsizing: CPU p95 under 30% and memory p95 under 50% for at least 14 days.
  • Scale-up candidate: CPU p95 over 75% or memory p95 over 80% during business load windows for at least 3 consecutive days.
  • Stability guardrail: No unresolved critical alerts and no regression in error-rate SLOs.

Keep thresholds workload-specific and validate with business traffic patterns.

Database visibility for optimize decisions

Section titled “Database visibility for optimize decisions”

For stateful Rehost workloads, include database signals in the same dashboard review cycle.

  • Connection pressure: active connection trend and burst behavior.
  • Database growth: database size progression over time.
  • Transaction behavior: commit/rollback trend for stability checks.

Use these metrics together with VM signals to avoid CPU-only or memory-only optimization decisions.

PostgreSQL Flex optimization path for migrated data tiers

Section titled “PostgreSQL Flex optimization path for migrated data tiers”

If the Rehost workload later moves its database tier to PostgreSQL Flex, include database-tier rightsizing and tuning in the same optimize cycle.

Use the offered Flex combinations and storage performance limits to assess a database-tier change. Keep the VM thresholds above workload-specific; the product catalog does not supply acceptance criteria or prove that an infrastructure change is reversible.

From the STACKIT docsFlavors and performance classes › FlavorsSource updated 06.07.2026 · copied 05.10.2026

Notes

  • CPU and RAM is always per node.
  • The system uses up to 15 connections for internal essential processes such as backup, monitoring, etc. These connections will be counted towards the max_connections limit.
What is this?

This section is copied from the STACKIT docs automatically, several times a day. It cannot be changed here. Changes belong in the STACKIT docs.

From the STACKIT docsFlavors and performance classes › Performance ClassesSource updated 06.07.2026 · copied 05.10.2026

Currently, we offer three types of instances. For each type there is a different set of flavors available.

What is this?

This section is copied from the STACKIT docs automatically, several times a day. It cannot be changed here. Changes belong in the STACKIT docs.

  1. Collect baseline metrics and traces for a representative period.
  2. Confirm optimization candidate with dashboards and alert history.
  3. Plan capacity change and rollback checkpoint.
  4. Apply VM size change with Terraform/OpenTofu.
  5. Re-validate latency, error rates, throughput, and cost.
  6. Keep or revert based on objective acceptance criteria.

Example A: Downsize after sustained low utilization

Section titled “Example A: Downsize after sustained low utilization”

Update VM sizing in env.tfvars:

machine_type = "g3i.2"

Apply and inspect plan output:

Terminal window
terraform plan -var-file=env.tfvars
terraform apply -var-file=env.tfvars

Then validate:

  • Service health (systemctl status, synthetic checks)
  • p95 latency and error-rate trend
  • Cost delta in reporting window

Example B: Scale up under sustained overload

Section titled “Example B: Scale up under sustained overload”

Update VM sizing in env.tfvars:

machine_type = "g3i.4"

Apply and validate with the same post-change checks.

If SLOs regress after rightsizing, roll back by restoring the previous machine_type and re-applying IaC. Treat rollback as a standard runbook step, not as an emergency-only path.

  • Depending on platform constraints and machine type, resize can require restart or replacement. Confirm behavior in terraform plan before apply.

In Rehost scenarios, CPU and memory are only one side of rightsizing. Storage performance can also become the limiting factor.

  • When to investigate storage: elevated I/O wait, unstable latency under write-heavy load, or throughput saturation despite available CPU.
  • What to select: a storage service plan and performance class that matches the observed IOPS and throughput profile.
  • Guidance: Block Storage service plans

Select the performance class before provisioning

Section titled “Select the performance class before provisioning”

A Block Storage performance class defines the maximum IOPS and throughput available to the complete volume. Application, database, operating-system, and backup access share this performance envelope. Select the class from measured peak demand, latency requirements, backup activity, and explicit growth headroom before creating the volume.

From the STACKIT docsService plans › Currently available Service Plans (performance classes)Source updated 22.04.2026 · copied 05.10.2026

The following table lists currently available performance classes for the EU01 region:

IOPS - Input/Output Operations per second

Throughput - Throughput in Megabytes per second

Thus, the classes used can be distinguished in detail based on the naming. Example: “Block Storage Premium - Performance Class 2” corresponds to SSD hard disks with max. 1000 IOPS and max. 100 Mbyte/s throughput.

What is this?

This section is copied from the STACKIT docs automatically, several times a day. It cannot be changed here. Changes belong in the STACKIT docs.

For the Rehost baseline, the operating system, Spring Boot application, and PostgreSQL data share the boot volume. Changing its performance class therefore requires a controlled replacement target:

  1. Select the new class from observed IOPS, throughput, latency, and I/O-wait data.
  2. Verify backup and database-level rollback readiness.
  3. Provision the replacement VM and boot volume through IaC with the selected class.
  4. Reapply the Ansible configuration and restore or migrate the workload data.
  5. Validate application behavior, data integrity, storage latency, backup coverage, and cost before switching.

Use a separate data volume when storage capacity or performance must evolve independently from the VM lifecycle. To change its performance class, create a new volume in the required availability model with sufficient capacity, stop writes, migrate and verify the data, switch the attachment or mount, and retain the source volume until acceptance and rollback gates have passed.

Migrate data from Block Storage

Treat storage checks as part of the same Optimize loop and validate latency, error behavior, recovery, and cost impact after any change.

SAFE

Decide the Storage Path

This asset continues the same Spring Boot and PostgreSQL reference implementation used for provisioning, migration, cutover, and stabilization. It does not introduce another example or repository. The existing Terraform variables, Ansible configuration, Observability instance, and validation workflows remain the technical baseline for Optimize.

The Optimize extension answers one practical question: how to detect overprovisioning or underprovisioning and then change VM or storage capacity through a controlled IaC workflow.

Code & registry github.com STACKIT CMF Rehost Spring Boot repository Continue with the same Terraform and Ansible reference implementation used by the preceding Rehost migration steps. Open the repository Rehost implementation asset

Use the managed STACKIT Observability stack as evidence source.

  • Prometheus: metric collection
  • Thanos: long-term metric retention
  • Grafana Loki: log analysis
  • Grafana Tempo: distributed traces
  • Grafana: dashboards and visualization

Architecture reference:

Use the dashboard to review infrastructure saturation, application health, request behavior, and alert history together before changing VM capacity.

Grafana dashboard for Rehost Spring Boot observability and VM rightsizing decisions

Define technical thresholds before changing capacity.

  • Candidate for downsizing: CPU p95 under 30% and memory p95 under 50% for at least 14 days.
  • Scale-up candidate: CPU p95 over 75% or memory p95 over 80% during business load windows for at least 3 consecutive days.
  • Stability guardrail: No unresolved critical alerts and no regression in error-rate SLOs.

Keep thresholds workload-specific and validate with business traffic patterns.

Database visibility for optimize decisions

Section titled “Database visibility for optimize decisions”

For stateful Rehost workloads, include database signals in the same dashboard review cycle.

  • Connection pressure: active connection trend and burst behavior.
  • Database growth: database size progression over time.
  • Transaction behavior: commit/rollback trend for stability checks.

Use these metrics together with VM signals to avoid CPU-only or memory-only optimization decisions.

PostgreSQL Flex optimization path for migrated data tiers

Section titled “PostgreSQL Flex optimization path for migrated data tiers”

If the Rehost workload later moves its database tier to PostgreSQL Flex, include database-tier rightsizing and tuning in the same optimize cycle.

Use the offered Flex combinations and storage performance limits to assess a database-tier change. Keep the VM thresholds above workload-specific; the product catalog does not supply acceptance criteria or prove that an infrastructure change is reversible.

From the STACKIT docsFlavors and performance classes › FlavorsSource updated 06.07.2026 · copied 05.10.2026

Notes

  • CPU and RAM is always per node.
  • The system uses up to 15 connections for internal essential processes such as backup, monitoring, etc. These connections will be counted towards the max_connections limit.
What is this?

This section is copied from the STACKIT docs automatically, several times a day. It cannot be changed here. Changes belong in the STACKIT docs.

From the STACKIT docsFlavors and performance classes › Performance ClassesSource updated 06.07.2026 · copied 05.10.2026

Currently, we offer three types of instances. For each type there is a different set of flavors available.

What is this?

This section is copied from the STACKIT docs automatically, several times a day. It cannot be changed here. Changes belong in the STACKIT docs.

  1. Collect baseline metrics and traces for a representative period.
  2. Confirm optimization candidate with dashboards and alert history.
  3. Plan capacity change and rollback checkpoint.
  4. Apply VM size change with Terraform/OpenTofu.
  5. Re-validate latency, error rates, throughput, and cost.
  6. Keep or revert based on objective acceptance criteria.

Example A: Downsize after sustained low utilization

Section titled “Example A: Downsize after sustained low utilization”

Update VM sizing in env.tfvars:

machine_type = "g3i.2"

Apply and inspect plan output:

Terminal window
terraform plan -var-file=env.tfvars
terraform apply -var-file=env.tfvars

Then validate:

  • Service health (systemctl status, synthetic checks)
  • p95 latency and error-rate trend
  • Cost delta in reporting window

Example B: Scale up under sustained overload

Section titled “Example B: Scale up under sustained overload”

Update VM sizing in env.tfvars:

machine_type = "g3i.4"

Apply and validate with the same post-change checks.

If SLOs regress after rightsizing, roll back by restoring the previous machine_type and re-applying IaC. Treat rollback as a standard runbook step, not as an emergency-only path.

  • Depending on platform constraints and machine type, resize can require restart or replacement. Confirm behavior in terraform plan before apply.

In Rehost scenarios, CPU and memory are only one side of rightsizing. Storage performance can also become the limiting factor.

  • When to investigate storage: elevated I/O wait, unstable latency under write-heavy load, or throughput saturation despite available CPU.
  • What to select: a storage service plan and performance class that matches the observed IOPS and throughput profile.
  • Guidance: Block Storage service plans

Select the performance class before provisioning

Section titled “Select the performance class before provisioning”

A Block Storage performance class defines the maximum IOPS and throughput available to the complete volume. Application, database, operating-system, and backup access share this performance envelope. Select the class from measured peak demand, latency requirements, backup activity, and explicit growth headroom before creating the volume.

From the STACKIT docsService plans › Currently available Service Plans (performance classes)Source updated 22.04.2026 · copied 05.10.2026

The following table lists currently available performance classes for the EU01 region:

IOPS - Input/Output Operations per second

Throughput - Throughput in Megabytes per second

Thus, the classes used can be distinguished in detail based on the naming. Example: “Block Storage Premium - Performance Class 2” corresponds to SSD hard disks with max. 1000 IOPS and max. 100 Mbyte/s throughput.

What is this?

This section is copied from the STACKIT docs automatically, several times a day. It cannot be changed here. Changes belong in the STACKIT docs.

For the Rehost baseline, the operating system, Spring Boot application, and PostgreSQL data share the boot volume. Changing its performance class therefore requires a controlled replacement target:

  1. Select the new class from observed IOPS, throughput, latency, and I/O-wait data.
  2. Verify backup and database-level rollback readiness.
  3. Provision the replacement VM and boot volume through IaC with the selected class.
  4. Reapply the Ansible configuration and restore or migrate the workload data.
  5. Validate application behavior, data integrity, storage latency, backup coverage, and cost before switching.

Use a separate data volume when storage capacity or performance must evolve independently from the VM lifecycle. To change its performance class, create a new volume in the required availability model with sufficient capacity, stop writes, migrate and verify the data, switch the attachment or mount, and retain the source volume until acceptance and rollback gates have passed.

Migrate data from Block Storage

Treat storage checks as part of the same Optimize loop and validate latency, error behavior, recovery, and cost impact after any change.

STACKIT LogoSTACKIT Logo
Rehost Spring Boot with Terraform and Ansible STACKIT · Runbook Open asset ↗

The Migration Framework defines the strategy, design, landing-zone, migration, and operations principles for Rehost workloads. This asset applies those principles to one concrete, runnable Spring Boot and PostgreSQL implementation on STACKIT.

The maintained repository is the source of truth for Terraform, Ansible, application artifacts, migration scripts, validation, and runtime configuration. This asset explains how to use that implementation without duplicating its complete source code.

Code & registry github.com STACKIT CMF Rehost Spring Boot repository Open the runnable Terraform and Ansible reference implementation for the Spring Boot and PostgreSQL Rehost path. Open the repository

This example represents an application Rehost path for a Spring Boot workload (Spring Music sample). The migration focus is the runtime relocation of the application to STACKIT:

  • Infrastructure Rehost: Terraform provisions one STACKIT VM, its boot volume, network, public IP, security group, SSH key, Observability resources, and an optional Server Backup schedule.
  • Application Rehost: Ansible installs Java, deploys the Spring Boot JAR, and manages it with systemd without changing the application runtime model.
  • Stateful Rehost: Ansible installs self-managed PostgreSQL on the same VM. The database engine is not changed to a managed service as part of this path.
  • Controlled migration: The repository provides separate rehearsal, cutover, validation, and rollback workflows with machine-readable evidence.

The validated baseline deliberately does not include an application load balancer, DNS switch, multiple VMs, Kubernetes, Cloud Foundry, or PostgreSQL Flex. Add those only as separately designed and tested extensions; they are not implied by this Rehost example.

  • Provisioning: Terraform resources for network, security, one VM, boot volume, SSH key, and public IP.
  • Configuration bridge: Terraform triggers Ansible after infrastructure provisioning.
  • Application deployment: Ansible deploys a concrete Spring Boot artifact and configures a systemd service.
  • VM-local database path: PostgreSQL installation, application role and database bootstrap, and a dump-based restore with ownership and data-integrity checks.
  • Operations baseline: Node exporter metrics are scraped by STACKIT Observability, and Terraform can enable daily Server Backup for the VM boot volume.
  • Migration controls: Source dump generation, independent restore validation, rehearsal, cutover, rollback verification, rollback execution, and evidence capture.
  • Deployable sample artifact: A ready-to-run JAR is included in the repository and used by default deployment settings.

To keep automation behavior consistent across CMF examples, use this common flag model in your Terraform variable set:

  • setup_project: create/use project context.
  • setup_observability: enable/disable observability resources.
  • setup_database: enable/disable optional VM-local PostgreSQL path.
  • setup_workload: enable/disable workload installation on VM.
  • setup_loadgen: enable/disable optional synthetic load generation.
  • setup_dns: accepted by the shared wrapper but unsupported in this baseline.

In the current Rehost repository, these map to existing switches (create_project, enable_observability, enable_local_postgresql, and load-generation related flags).

The following diagram shows the implemented and tested target, not a future high-availability variant.

Approved operator sourceSTACKIT ProjectUbuntu VMObservabilityBackup ArchiveSpring Boot JAR + systemdSelf-managed PostgreSQL restricted HTTP/SSHlocalhost SQLrestricted metrics scrapeboot-volume backup

Use an isolated Linux lab with Git, Terraform, Ansible, ShellCheck, SSH/SCP, curl, jq, and PostgreSQL server/client tools including pg_config. The sample dump scripts use runuser and the local postgres OS account and require root privileges in that lab. When Server Backup is enabled, the cutover workflow also needs an authenticated STACKIT CLI. Use the Terraform version that created an existing saved plan; plan files are version-specific.

Start in a parent directory without an existing checkout of the same name. This revision contains the migration workflows and declarative Observability management. Its JAR is unchanged from the artifact pinned by the Replatform reference:

Terminal window
umask 077 &&
git clone https://github.com/stackitcloud/stackit-cmf-Rehost-springboot.git &&
git -C stackit-cmf-Rehost-springboot checkout --detach b9225eb35c64b9ef4761c208fadb9a2356431793 &&
test -f stackit-cmf-Rehost-springboot/scripts/run_cutover.sh &&
cd stackit-cmf-Rehost-springboot &&
printf '%s\n' "Workspace ready. Continue from this Rehost checkout." || {
printf '%s\n' "Preparation failed. Resolve the error before continuing." >&2
false
}

Run the following steps from that checkout only after preparation succeeds. Preserve an existing checkout, state and evidence rather than replacing them. Keep credentials, plans, state, dumps, inventory and evidence private and outside version control; do not enable shell tracing.

Create the private variable file only when it does not already exist:

Terminal window
umask 077
test -e env.tfvars || cp env.tfvars.example env.tfvars
chmod 600 env.tfvars

Edit the file before planning. Configure the approved existing project or project-creation scope, service-account key path, SSH key pair, available image, availability zone, VM flavor and storage. Restrict SSH and application ingress to the source CIDRs actually observed on the target path. Verify the SSH host key through a trusted channel before migration scripts use strict host checking. Set SSH_KEY and SSH_USER for those scripts when they differ from ~/.ssh/id_rsa and ubuntu; keep them consistent with Terraform’s private_ssh_key_path and ssh_user.

Enable VM-local PostgreSQL, Observability and Server Backup for this walkthrough. For the initial runtime deployment, leave postgresql_restore_after_copy = false and the source dump path empty: provisioning must not import the source before rehearsal and cutover approval. Supply the database password through the approved secret mechanism as TF_VAR_postgresql_app_password and retain it securely for subsequent plans; do not put it in the variable file, shell history or documentation.

Confirm permissions, quota, cost approval and the private Terraform backend before initialization. This workflow assumes a prepared execution host, not a complete lab installation procedure.

Create a custom-format source dump and record its SHA-256 checksum, expected row count, and a workload-specific deterministic fingerprint. The repository includes a reproducible eight-row sample:

Terminal window
./scripts/create_source_dump.sh
./scripts/validate_source_dump.sh

The validation script restores into a separate local PostgreSQL cluster before the dump can enter a migration rehearsal. These scripts generate the bundled sample in temporary local clusters; they do not export a running application or the STACKIT VM. Use a fresh artifact directory and do not overwrite a dump already approved for migration. For a real source, export it under the agreed write-freeze procedure and record equivalent checksum, count and fingerprint evidence.

Configure an existing STACKIT project or project creation, restrict SSH and application ingress to approved source CIDRs, and pass the database password through TF_VAR_postgresql_app_password rather than a variable file. Review a saved plan before apply.

Terminal window
terraform init &&
./scripts/check.sh &&
terraform plan -input=false -var-file=env.tfvars -out=tfplan

Stop if initialization, checks or planning fail. Review resource changes, target project, ingress, cost and the disabled initial restore before explicitly applying that saved plan:

Terminal window
terraform apply tfplan

Terraform runs Ansible after provisioning. A successful apply proves completion of that orchestration, not acceptance of migrated data. Check the runtime before beginning rehearsal:

Terminal window
./scripts/validate_deployment.sh

Run the rehearsal against a temporary database on the target VM:

Terminal window
./scripts/run_migration_rehearsal.sh

The rehearsal checks the source evidence, restores the dump, compares row count and fingerprint, and removes its temporary database without changing the production springmusic database.

Set the expected source evidence in env.tfvars:

enable_local_postgresql = true
postgresql_source_dump_local_path = "artifacts/source-postgresql.dump"
postgresql_restore_after_copy = true
postgresql_expected_album_count = 8
postgresql_expected_album_fingerprint = "<source-fingerprint>"

Then execute the explicit approval gate:

Terminal window
./scripts/run_cutover.sh --confirm

The script requires a fully available Server Backup no older than 24 hours, creates a saved Terraform plan that replaces only the Ansible orchestration resource, rejects unrelated infrastructure changes, applies the restore, validates data and runtime behavior, and requires a final no-op Terraform plan. Retries of the same source-dump hash preserve the original database rollback point.

Validation checks the source row count and fingerprint, table ownership, a transactionally rolled-back write as the application role, application reachability, services, and the protected rollback dump.

Terminal window
./scripts/validate_migration.sh
./scripts/validate_deployment.sh
./scripts/verify_rollback.sh

If an approved rollback trigger is met during the rollback window, preserve the current target database and restore the original pre-cutover dump:

Terminal window
./scripts/rollback_postgresql.sh --confirm

With enable_server_backup = true, Terraform enables STACKIT Server Backup and a daily schedule for the VM boot volume. The default retention is 14 days. Cutover requires a fully available backup no older than 24 hours. Backup creation and status were validated against the real target; an in-place Server Backup restore remains a disruptive disaster-recovery operation and must be rehearsed in a separate recovery environment.

The Server Backup complements the PostgreSQL pre-restore dump. It does not replace database-level consistency checks or the application rollback procedure.

Follow workspace preparation, private target configuration, source evidence and reviewed provisioning in that order. Rehearse before the approved cutover and retain the acceptance evidence afterwards. The repository scripts invoke terraform explicitly; an OpenTofu-only execution host needs a separately reviewed adaptation, not just replacement of commands in this page.

The implementation provisions STACKIT Observability and scrapes node exporter. The Grafana provider manages the included dashboard in the SCF Rehost folder with the existing Thanos datasource, waiting for instance readiness instead of silently skipping dashboard creation.

Verify live VM, Spring Boot and PostgreSQL metrics through grafana_dashboard_url, then require a no-op plan. Seven panels cover the active baseline; the synthetic request panel is included only when the local load generator is enabled. Synthetic requests do not represent all application traffic. The deployment validation also checks exporter metrics and service health after apply and database restore.

Initial Grafana admin credentials remain a temporary authentication dependency. Protect state and saved plans. Notification receivers, alert-routing ownership, application logs and distributed tracing require separate configuration and acceptance before production use.

This example can be run in two valid setup modes.

  • With an existing Application Landing Zone: Reuse the already defined project context and service account setup from your landing zone implementation. Use the known project ID as deployment target (for example with create_project = false and project_id = "...") and the service account JSON configured for that landing zone scope.
  • Standalone without existing landing zone project: Let the example create a dedicated project. In this mode, set create_project = true and provide parent_container_id with an existing folder/container where the service account has sufficient permissions.

For landing-zone design guidance, see Application Landing Zone.

Use this as a minimal starting point in env.tfvars.

create_project = true
target_project_name = "cmf-rehost-springboot"
target_project_owner_email = "owner@example.com"
parent_container_id = "cmf-xxxxxxxx"
service_account_key_path = "~/.ssh/cmf-sa.json"
public_ssh_key_path = "~/.ssh/id_rsa.pub"
private_ssh_key_path = "~/.ssh/id_rsa"
availability_zone = "eu01-1"
machine_type = "g2i.2"
ssh_allowed_cidr = "203.0.113.10/32"
app_allowed_cidr = "203.0.113.10/32"
enable_local_postgresql = true
enable_observability = true
enable_server_backup = true
  • Default artifact path: ansible/files/springboot-app.jar
  • Default variable: jar_local_path points to the same file.
  • Alternative artifact: Replace the file or override jar_local_path.

When the artifact changes, Terraform detects the checksum difference and re-runs the Ansible deployment step.

Every rehearsal, cutover, and rollback writes an evidence.env file below artifacts/evidence/<timestamp>-<mode>/. Accept a cutover only when the evidence records the expected checksum, row count, fingerprint, target VM, successful runtime validation, and final Terraform no-op.

Terminal window
./scripts/validate_migration.sh
./scripts/validate_deployment.sh
terraform plan -var-file=env.tfvars -detailed-exitcode

Use the same approved source path, restore flag, expected count and fingerprint as the completed cutover. Exit code 0 means no changes, 2 means changes are proposed, and 1 means an error. A plan alone is not proof of apply. The cutover workflow records status=passed and terraform_noop=true only after applying its saved plan and completing data and runtime validation.

  • Prepare: Approve the target design, access paths, source evidence and recovery responsibilities.
  • Provision: Review and apply the infrastructure plan, then verify the VM runtime independently.
  • Rehearse: Restore into an isolated database and verify integrity without changing the application database.
  • Cut over: Freeze source writes, approve the final dump and allow only the intended orchestration change.
  • Accept or recover: Require data, runtime and no-op evidence; invoke the agreed rollback before its deadline when acceptance fails.
  • Handover: Transfer monitoring, backup ownership, evidence and source-retention decisions before optimization.

The Replatform reference reuses this repository’s pinned Spring Music JAR and sample-data generator. Its sample walkthrough therefore needs this checkout and validated dump artifacts, but not a newly provisioned Rehost VM. Do not create another VM solely to generate the local sample.

For an actual migration from the Rehost VM to SKE and PostgreSQL Flex, verify that the VM is currently healthy, freeze its application writes, export its real PostgreSQL database and qualify that export. A historical successful Rehost apply does not establish current reachability or make the local sample equivalent to live VM data. Source export, acceptance and source failback need their own approved procedure.

  • Keep operational procedures aligned with the runbook and migration governance.
  • For production usage, adapt security controls, sizing, image selection, and life cycle automation.
Trail historyAdded Oct 4, 2026LWUpdatedNo updates · 1 bar = 1 week i
Maintainers
LWLukas WeberrußHead of STACKIT Cloud Migration Framework · STACKITOwnerActive 10 of the last 12 weeks · 47 updatesSTACKITwww.linkedin.com/in/lukas-weberruß-a360b081Contributed in STACKIT