AWS → STACKIT · BACKUP AND DR CHECKLIST
AWS Backup and disaster recovery on STACKIT: the checklist
AWS Backup is one service with plans, vaults and selections; STACKIT backs up per service. That difference is where backup migrations go wrong: a retention rule that lived in one plan now lives in the server backup schedule, the volume automation, the database settings and the bucket. This checklist walks the phases from discovery to the first restore test and names the file of a StackToIT run that holds each answer.
Discovery: find every backup rule you rely on
Run the assessment first. The demo estate for this page, assessed on 4 November 2026, has 10 resources in 6 units: 3 RECONFIGURE (the backup plan, the snapshot policy, the EC2 instance), 1 REPLATFORM (the RDS database), 1 DIRECT (the replica bucket) and 1 MANUAL_REVIEW (the versioned, replicated bucket). These checks make sure nothing that protects data is left out.
| Check | Where the answer is | Done when |
|---|---|---|
| List every backup plan, vault and selection, and which resources each selection reaches. | assessment.json, unit of the plan | You know whether a tag selection reaches a server, a volume or a database. |
| List snapshot policies (Data Lifecycle Manager) and their tags, schedules and counts. | assessment.json, unit of the policy | Each policy is mapped to a volume or a server. |
| Read the built-in retention of each database and the window. | assessment.json, unit of the database | Retention days and the backup window are written down. |
| List buckets with versioning, lifecycle rules and replication. | terraform/storage.tf comments, terraform/s3api/ | Every bucket with a replication rule is on a list. |
| Record the recovery point and recovery time each application needs. | migration/data-movement.md (the run states none unless you give it) | Numbers owned by the application owner, not guessed. |
Design: decide what replaces the central service
The run maps by service, so the design decisions are per service too.
| Check | Where the answer is | Done when |
|---|---|---|
| Pick which servers get a backup schedule; the schedule needs a server you name. | terraform/backup.tf, variable for the server id | A server id is set for every protected server. |
| Decide how volumes are protected: label-selected snapshots or backups. | terraform/backup.tf, volume automation | Labels on STACKIT volumes match the selector. |
| Decide the count of snapshots against the documented per-volume limit. | comment above the volume automation | The retention you need beyond that is met by backups. |
| Decide what replaces a vault lock or an immutable vault. | Backup unit reason in assessment.json | A written answer: object lock on a bucket, offline copy, or accepted gap. |
| Decide the DR target: second STACKIT region, second bucket, or another site. | terraform/storage.tf, geo-replication ledger line | A named target and an owner. |
Build: the backup plan becomes a server backup schedule
Left: the vault, the plan and its tag selection. Right: the two server backup blocks of the draft with the two comment lines above them that explain what the selection reaches and what is not carried; the other comment lines are left out. The daily 03:00 rule with 35 days becomes an rrule and retention_period = 35.
resource "aws_backup_vault" "main" {
name = "company-vault"
}
resource "aws_backup_plan" "daily" {
name = "daily-35d"
rule {
rule_name = "daily"
target_vault_name = aws_backup_vault.main.name
schedule = "cron(0 3 * * ? *)"
lifecycle {
delete_after = 35
}
}
}
resource "aws_backup_selection" "tagged" {
name = "tagged-resources"
plan_id = aws_backup_plan.daily.id
iam_role_arn = "arn:aws:iam::111122223333:role/backup-role"
selection_tag {
type = "STRINGEQUALS"
key = "backup"
value = "daily"
}
}# no volume automation is drafted for the selection tagged-resources of aws_backup_plan.daily: it reaches no volume in the scan.
# NOT CARRIED (aws_backup_plan.daily): rule.target_vault_name — STACKIT Server Backup keeps backups per server and has no vault: the vault a rule writes to, aws_backup_vault.main (company-vault), is not carried.
resource "stackit_server_backup_enable" "daily" {
project_id = var.project_id
server_id = var.daily_server_id
}
resource "stackit_server_backup_schedule" "daily" {
project_id = var.project_id
server_id = var.daily_server_id
name = "daily-35d-daily"
rrule = "DTSTART;TZID=UTC:20200101T030000 RRULE:FREQ=DAILY;INTERVAL=1"
enabled = true
backup_properties = {
name = "daily-35d-daily"
retention_period = 35
}
depends_on = [stackit_server_backup_enable.daily]
}Enable first, then schedule
The draft writes a stackit_server_backup_enable and makes the schedule depend on it. STACKIT documents server backups as crash-consistent backups of the volumes attached to a server, with a retention period you choose.
The tag selection, resolved
The run resolves backup=daily against the scan: the schedule protects aws_instance.app, and the RDS database with the same tag gets the backup of its own service instead. The schedule still takes a server id variable: set it to the STACKIT server of aws_instance.app, and repeat the schedule for every further server the tag covered.
No vault
STACKIT Server Backup keeps backups per server. The vault, its name and the selection's IAM role have no counterpart and are listed as not carried.
Build: the snapshot policy becomes a volume automation
Left: the Data Lifecycle Manager policy. Right: the volume automation of the draft with the one comment line that checks the snapshot count; the other comment lines are left out. The target tag becomes a label selector, the 24-hour create rule an rrule, the retain count a count policy.
resource "aws_dlm_lifecycle_policy" "ebs" {
description = "Daily EBS snapshots"
execution_role_arn = "arn:aws:iam::111122223333:role/dlm-role"
state = "ENABLED"
policy_details {
resource_types = ["VOLUME"]
target_tags = {
snapshot = "true"
}
schedule {
name = "daily"
create_rule {
interval = 24
interval_unit = "HOURS"
times = ["03:00"]
}
retain_rule {
count = 7
}
}
}
}# Check the count: the policy keeps 7 snapshots for schedule daily; the documented limit is 6 snapshots per volume (docs.stackit.cloud, introduction to the server), so the days beyond are not covered by snapshots — backups (daily retention, default 14 days, `backupRetentionPolicies`) are the longer-retention path; whether the Automation Service enforces the number is not documented, verify it on the first run.
resource "stackit_volume_automation" "ebs" {
project_id = var.project_id
template_id = var.ebs_automation_template_id
name = "daily-ebs-snapshots-daily"
description = "Daily EBS snapshots"
input = jsonencode({
kind = "VolumeRecoveryPointManagement"
inheritVolumeLabels = false
snapshotRetentionPolicy = {
kind = "count"
value = 7
}
volumeLabelSelector = "snapshot=true"
})
triggers = {
schedule = {
rrule = "DTSTART;TZID=UTC:20200101T030000 RRULE:FREQ=DAILY;INTERVAL=1"
}
}
}The count of 7
The run checks the policy's 7 snapshots against a documented limit of 6 per volume and says the days beyond are not covered by snapshots. Daily backups in the same automation (backupRetentionPolicies) are the longer-retention path, and whether the limit is enforced is checked on the first run.
Opt-in resource
The pinned provider marks stackit_volume_automation as a resource that may still change, and the draft switches that opt-in on in terraform.tf. Your team decides whether to accept it or to create the automation another way.
Labels must exist
The automation selects by label. Put snapshot=true on the STACKIT volumes that need it, or nothing is snapshotted.
Build: database retention and point-in-time recovery
Left: the RDS instance with 14 days of retention, a backup window and Multi-AZ. Right: the PostgreSQL Flex instance of the draft with four of the comment lines above it, unchanged; the other comment lines are left out.
resource "aws_db_instance" "orders" {
identifier = "orders-db"
engine = "postgres"
engine_version = "16.4"
instance_class = "db.m6g.large"
allocated_storage = 100
username = "app"
password = var.db_password
backup_retention_period = 14
backup_window = "02:00-02:30"
multi_az = true
skip_final_snapshot = false
final_snapshot_identifier = "orders-db-final"
tags = {
backup = "daily"
}
}# recovery window: the source restores to any point within 14 days, as late as about five minutes before now (RDS uploads transaction logs to S3 every five minutes).
# PostgreSQL Flex restores to an arbitrary point of its available backups (transaction logs are on by default) into a cloned instance, and keeps them retention_days = 32.
# LEDGER backup retention: 14 days → retention_days = 32 — TRANSFORMED
# LEDGER high availability: standby / multi-node → flavor_id = "2.16-replica" (a replica flavor: three nodes that mirror each other; no STACKIT page states the zone of each node or how a failover works, so a zone failure or an automatic takeover is not claimed) — TRANSFORMED
resource "stackit_postgresflex_instance" "orders" {
project_id = var.project_id
name = "orders-db"
version = "16"
flavor_id = "2.16-replica"
storage = {
class = var.postgresflex_storage_class
size = 108
}
backup_schedule = "0 2 * * *"
network = {
acl = var.orders_allowed_cidrs
access_scope = var.orders_access_scope == "SNA" ? "SNA" : null
}
retention_days = 32
}Retention changes
The draft sets retention_days to 32 and backup_schedule to a nightly 02:00, the start of the source's 02:00-02:30 window; no comment says how the time was derived. The run labels the 14 to 32 step as transformed and states that the two recovery windows are not shown to be equal.
Restore creates a clone
Point-in-time recovery on PostgreSQL Flex restores into a new cloned instance, so the recovery runbook includes switching the application to it.
The final snapshot is not carried
The source's final snapshot has no counterpart on delete. Export the data first if it must outlive the cutover. STACKIT's own documentation also warns that deleting an instance without instance protection is permanent.
Build: bucket versioning and replication
Left: versioning, the replica bucket and the replication rule. Right: the draft's bucket with its two NOT CARRIED comments (the replication one followed by its decision line) and the geo-replication ledger line; the other comment lines are left out. Versioning is carried outside Terraform: the run writes terraform/s3api/documents/versioning.json with Status Enabled, to apply through the S3 API before the first object lands.
resource "aws_s3_bucket_versioning" "documents" {
bucket = aws_s3_bucket.documents.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket" "documents_replica" {
bucket = "acme-documents-replica"
}
resource "aws_s3_bucket_replication_configuration" "documents" {
bucket = aws_s3_bucket.documents.id
role = "arn:aws:iam::111122223333:role/s3-replication"
rule {
id = "all"
status = "Enabled"
destination {
bucket = aws_s3_bucket.documents_replica.arn
}
}
}# NOT CARRIED (aws_s3_bucket_replication_configuration.documents): role = arn:aws:iam::111122223333:role/s3-replication, rule — stackit_objectstorage_bucket has no replication attribute (pinned provider). STACKIT's supported-operations page lists Put, Get and Delete Bucket Replication, but no STACKIT page describes replication targets, cross-region behaviour or the role the source assumes, so whether replication between STACKIT buckets works is UNKNOWN (not observed); the source's destination, role, storage class and filter do not carry over.
# Decision: probe `put-bucket-replication` between two STACKIT buckets before relying on it, or replace it with a scheduled sync (rclone) or application writes to both buckets.
# NOT CARRIED (aws_s3_bucket_versioning.documents): versioning_configuration.status = Enabled — stackit_objectstorage_bucket has no versioning attribute (pinned provider), but STACKIT Object Storage supports versioning through its S3 API (the provider lacks it, STACKIT does not). … Once enabled, versioning cannot be disabled on STACKIT, only suspended (docs manage-bucket-versioning). (shortened: the whole reason is in the trace of this block)
# LEDGER geo-replication: second region → none — sync to a second bucket if required — UNSUPPORTED
resource "stackit_objectstorage_bucket" "documents" {
project_id = var.project_id
name = "acme-documents-primary"
}Versioning is one way
STACKIT's versioning how-to sets it with aws s3api put-bucket-versioning and states that once enabled it cannot be disabled, only suspended.
History needs its own copy
A plain sync copies current versions only. The run's data-copy document writes earlier versions oldest first by version id and notes that source version ids cannot be kept.
Replication is a probe or a sync
The provider has no replication attribute. STACKIT lists the bucket replication operations, but the run found no STACKIT page on replication targets or cross-region behaviour, so it proposes probing put-bucket-replication between two STACKIT buckets or a scheduled sync.
Data and cutover: keep protection continuous
Backups on both sides should overlap until the target has proved itself.
| Check | Where the answer is | Done when |
|---|---|---|
| Keep AWS Backup and DLM running until the first STACKIT backup of each resource has completed (practice). | Schedules in terraform/backup.tf and data.tf | One completed STACKIT backup per protected resource. |
| Take a last backup of each source database before the write freeze (practice). | Freeze step in terraform/handover/database-orders/data-move.md | The backup id is recorded. |
| Copy versioned buckets with their version history if history matters. | terraform/s3api/documents/data-copy.md | Version counts per key match on both sides. |
| Replace bucket replication with a scheduled sync or application writes, or probe replication between two STACKIT buckets first. | comment in terraform/storage.tf | A tested mechanism, not an assumption. |
| Write the rollback limit: when a rollback loses data written on the target. | terraform/handover/database-orders/data-move.md and terraform/s3api/documents/data-copy.md (check rollback-limit) | A date and an owner are recorded. |
After: prove that you can restore
A backup you have not restored is a hope.
| Check | Where the answer is | Done when |
|---|---|---|
| Restore one volume backup of the server to a new volume and read files from it; STACKIT documents this partial restore. | Your own runbook, based on the schedule in terraform/backup.tf | Files and application check pass; time recorded. |
| Restore the database to a point in time as a clone. | terraform/data.tf comments | The latest restorable point matches the recovery point you need. |
| Restore an old object version from the bucket. | terraform/s3api/documents/ | The version is returned. |
| Remove temporary access used for the move and retire AWS Backup after the retention you decided. | execution/manual/actions.md | Source vault and plans removed on a recorded date. |
Questions about backup migration
Is there a STACKIT equivalent of AWS Backup?
The run's reason says there is no central backup service like AWS Backup. Each service has its own: server backup schedules, volume automations, the built-in backups of managed databases and snapshots for file storage.
Can I get an immutable vault?
The run states that the isolation of the Automation Service backups is not a lock, write-once or immutability setting. STACKIT's Object Storage documentation describes object lock with compliance retention up to 365 days for buckets, which is a different building block.
Does StackToIT take or restore backups?
No. It reads Terraform and writes a draft, scripts and documents. Backups and restores are run by your team.
Does S3 bucket replication carry over?
No. stackit_objectstorage_bucket has no replication attribute. The run proposes probing replication between two STACKIT buckets or a scheduled sync, and records copying to a second region as unsupported.
Why does the draft ask for a server id?
Because a STACKIT server backup schedule belongs to one server, not to a tag. The run resolved the tag selection to aws_instance.app but leaves the id as a variable; set it to the STACKIT server that replaces that instance.
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.