AWS → STACKIT · MIGRATION GUIDE
The complete AWS to STACKIT migration guide
Migrate in this order: assess the Terraform, decide what has no like-for-like target, build the network, stand up the data services, move the data, switch the clients, retire the source. This guide follows one 12-resource AWS estate through a StackToIT run and shows, at each step, which file of the output holds the answer and which decisions stay with your architects.
The seven steps
The order is the one the run's own waves and work packages follow.
1. Assess the Terraform
Point StackToIT at the folder with your AWS Terraform. It reads the configuration and its modules offline, builds a dependency graph and assigns each resource a class and a target. Nothing is called in AWS or STACKIT.
2. Read the classes and settle the decisions
DIRECT and RECONFIGURE units are plain work. REPLATFORM, CONTAINERIZE and REDESIGN units change the programming model. Copy decisions-template.yaml, record your answers and run the assessment again with --decisions; the new report shows which answers were applied and which decisions are still open.
3. Build the foundation
Create the project layout, the networks and the security groups. This is wave 2 in the run, after the VPC unit.
4. Stand up the data and messaging services
Apply the PostgreSQL Flex, Valkey, RabbitMQ, MongoDB Flex and Object Storage drafts. They are empty until you move data.
5. Move the data
The run writes a data-move document per stateful unit. StackToIT moves no data; your team runs the commands after a rehearsal.
6. Switch producers, consumers and clients
The run's cutover work packages switch the data consumers, the clients, then the workloads with the message producers and consumers. Reading that numbering as a sequence is our summary; the binding order is the steps each step comes after, listed in execution/manual/actions.md.
7. Verify, keep the way back, retire
Run the checks, record the rollback limit, keep the source for the retention you decide, then retire it.
The commands behind the steps
These are the documented StackToIT commands, each quoted from the public documentation page named above it. The optional discover step adds a read-only AWS inventory to compare with the Terraform; explain traces a unit to its evidence; verify checks a finished run against its manifest.
stacktoit assess ./aws-terraformstacktoit discover commands
stacktoit discover aws ./aws-terraform-stacktoit --region eu-central-1
stacktoit assess ./aws-terraform ./aws-terraform-stacktoitstacktoit explain ./terraform-stacktoit 'UNIT_ADDRESS'stacktoit verify ./terraform-stacktoitOffline by default
assess itself does not connect to an AWS account. discover aws uses your local AWS CLI credentials and read-only collection commands.
A draft is not an approval
The documentation is explicit: the presence of a Terraform draft is not approval to apply it.
The run: 12 resources, 11 units
Estate written for this page and assessed on 4 November 2026: a VPC with two subnets, a security group, an EC2 instance, an RDS PostgreSQL database, an S3 bucket, an ElastiCache group, an SQS queue, a Lambda function and a DynamoDB table. Classes come from assessment.json; the database subnet group folds into the database.
| AWS unit | STACKIT target | Class |
|---|---|---|
| aws_vpc.main | Project networking, Network Area where networks must talk privately | Reconfigure |
| aws_subnet.private_a, private_b | One stackit_network each, same IPv4 prefix | Reconfigure |
| aws_security_group.app | STACKIT security group with rules | Reconfigure |
| aws_instance.app | STACKIT server, network interface, volume | Reconfigure |
| aws_db_instance.main (PostgreSQL 16) | PostgreSQL Flex | Replatform |
| aws_s3_bucket.uploads | Object Storage bucket and credentials group | Direct |
| aws_elasticache_replication_group.sessions | Key Value Store (Valkey) | Reconfigure |
| aws_sqs_queue.jobs | RabbitMQ instance | Replatform |
| aws_lambda_function.thumbnailer | Cloud Foundry organization, handler in an always-on process | Containerize |
| aws_dynamodb_table.audit | MongoDB Flex instance, as an alternative to a JSONB design | Redesign |
By class: 6 RECONFIGURE, 2 REPLATFORM, 1 DIRECT, 1 CONTAINERIZE, 1 REDESIGN, all with medium confidence. None of the 11 units has its critical settings closed yet: 5 get there once their open items are answered, and 6 need work that a person does by hand in this build.
Foundation: subnets become networks
Left: the VPC and its two subnets. Right: the two stackit_network blocks of the draft, each with the comment line that names the availability zone it does not carry; the other comment lines of the file are left out. STACKIT has no VPC container object, so the project the draft is created in is the VPC's boundary.
resource "aws_vpc" "main" {
cidr_block = "10.30.0.0/16"
}
resource "aws_subnet" "private_a" {
vpc_id = aws_vpc.main.id
cidr_block = "10.30.1.0/24"
availability_zone = "eu-central-1a"
}
resource "aws_subnet" "private_b" {
vpc_id = aws_vpc.main.id
cidr_block = "10.30.2.0/24"
availability_zone = "eu-central-1b"
}
# NOT CARRIED (aws_subnet.private_a): availability_zone = eu-central-1a — stackit_network has no availability zone (pinned provider): the network serves the region; servers and volumes choose their zone.
resource "stackit_network" "private_a" {
project_id = var.project_id
name = "private-a"
ipv4_prefix = "10.30.1.0/24"
ipv4_nameservers = var.default_ipv4_nameservers
}
# NOT CARRIED (aws_subnet.private_b): availability_zone = eu-central-1b — stackit_network has no availability zone (pinned provider): the network serves the region; servers and volumes choose their zone.
resource "stackit_network" "private_b" {
project_id = var.project_id
name = "private-b"
ipv4_prefix = "10.30.2.0/24"
ipv4_nameservers = var.default_ipv4_nameservers
}Same prefixes, no zone
Subnets keep their IPv4 prefixes, which keeps addressing plans stable. The availability zone is dropped: a STACKIT network serves the region, and servers and volumes choose their zone.
Public or private is not a subnet property
On STACKIT, servers get public IPs explicitly. The run records that outbound internet access cannot be disabled for a network the way a private AWS subnet had none, and leaves that as an open decision.
Data: RDS becomes PostgreSQL Flex
Left: the RDS instance. Right: the draft instance with the two NOT CARRIED comment lines that precede it in the file; the other comment lines are left out. Flavor and storage are derived from the source: 2.16 is 2 vCPU and 16 GiB, the smallest single-node flavor covering db.m6g.large, and storage is rounded up from 100 GiB to 108 because STACKIT states gigabytes without defining the unit.
resource "aws_db_instance" "main" {
identifier = "app-db"
engine = "postgres"
engine_version = "16.4"
instance_class = "db.m6g.large"
allocated_storage = 100
db_subnet_group_name = aws_db_subnet_group.main.name
username = "app"
manage_master_user_password = true
storage_encrypted = true
backup_retention_period = 7
}# NOT CARRIED (aws_db_instance.main): db_subnet_group_name — stackit_postgresflex_instance has no subnet, availability-zone or IP-family attribute (pinned provider): clients reach the instance at the endpoint STACKIT assigns.
# NOT CARRIED (aws_db_instance.main): manage_master_user_password — the master secret in AWS Secrets Manager (managed master password) is not carried: STACKIT generates the credential (pinned provider) and has no rotation schedule; keep the new one in STACKIT Secrets Manager or the application's own store.
resource "stackit_postgresflex_instance" "main" {
project_id = var.project_id
name = "app-db"
version = "16"
flavor_id = "2.16"
storage = {
class = var.postgresflex_storage_class
size = 108
}
backup_schedule = "0 2 * * *"
network = {
acl = var.relational_database_allowed_cidrs
access_scope = var.relational_database_access_scope == "SNA" ? "SNA" : null
}
retention_days = 32
}Credentials change
STACKIT generates the master credential, and the application receives the new one. There is no rotation schedule to carry over.
Endpoint, not subnet
The instance has its own endpoint and an access list; the source's placement in a subnet group is not carried. Exposure is a decision.
Turn on instance protection
STACKIT's PostgreSQL Flex documentation warns that in API v3, deleting an instance without active instance protection loses its data immediately and permanently. Enable protection before the database holds production data.
The order the run proposes
Waves are computed from dependencies. Units in the same wave can move in parallel; the run calls this a starting point for your plan, not a schedule.
| Wave | Units | Why here |
|---|---|---|
| 1 | VPC, S3 bucket, ElastiCache group, SQS queue, Lambda function, DynamoDB table | They depend on no other unit of the estate |
| 2 | Security group, both subnets | They depend on the VPC |
| 3 | RDS instance, EC2 instance | They depend on the subnets |
The execution package adds 24 automated steps (18 resource blocks and 6 API calls) and 102 manual steps in 33 work packages. Of the 24 automated steps, 19 are free of decisions and manual prerequisites but still wait for values such as project_id.
Decisions your architects own
The register of this run lists 26 records. These are the ones with the largest effect.
DynamoDB
No DynamoDB-compatible service exists on STACKIT. The draft offers one MongoDB Flex instance, STACKIT's managed MongoDB service, with each table as a collection; relational JSONB on PostgreSQL Flex is the other option. Either way the access layer is rewritten, and key design, capacity modes and conditional writes do not carry over.
Lambda
The run notes that STACKIT has no generally available function service. The handler runs in an always-on process: a Cloud Foundry app, or a container. The execution model changes from per-invocation to always-on.
Boot image
An AMI is replaced by a STACKIT image you choose. Until that is answered, the connectivity check and the cutover steps wait.
Egress and endpoints
The draft keeps outbound traffic closed and exposes the databases on a public endpoint limited by an access list; both are defaults you confirm or replace. Fill the access lists yourself: STACKIT's RabbitMQ documentation says a new data service starts with a default access list that includes STACKIT IP ranges.
Data movement method
Dump and restore or logical replication for PostgreSQL; sync tools for buckets. The right choice depends on data size and freeze window, which the Terraform does not state.
Questions architects ask first
Where do I start?
With an assessment of your Terraform. It takes the folder of your AWS configuration and writes a report, a draft and a plan. Read index.html first, then the classes in assessment.json.
Does StackToIT change anything in AWS or STACKIT?
No. assess reads files and writes files. The generated scripts and Terraform are applied by a person.
What if my estate is not in Terraform?
The documented optional discover step collects a read-only AWS inventory with your local CLI credentials to compare with the Terraform. It complements Terraform input; it does not replace it.
Which services are the hardest to move?
The ones with no like-for-like target: DynamoDB (REDESIGN) and Lambda (CONTAINERIZE) in this run. Queues and managed databases are replatforming: same purpose, different API or release.
How do I know the migration is complete?
When the checks of the plan pass, the rollback limit is recorded and the source is retired after the retention you decided. The run lists those checks per unit in execution/verify/checks.md.
Sources
Your next move: STACKIT.
Tell us about your estate. We show you what StackToIT delivers for it: target architecture, Terraform artifacts and a structured migration plan.