Home / Tutorials / Terraform

Terraform · Infrastructure as code · Deploy blocked

Fix: Instance cannot be destroyed

Error Error: Instance cannot be destroyed

By the Erzon engineering team · A step-by-step fix, not a forum thread

This error means Terraform planned to destroy or replace a resource that is protected by lifecycle { prevent_destroy = true }, and that guard stopped the run. The guard is almost always in place on stateful resources like databases, where an accidental destroy loses data. Your plan wants to remove or replace the resource, usually because a force-new attribute changed and the only way to apply it is to recreate. The fix starts with understanding why a replacement was planned, not with ripping out the guard.

Error: Instance cannot be destroyed

  on database.tf line 10:
  10: resource "aws_db_instance" "primary":

Resource aws_db_instance.primary has lifecycle.prevent_destroy set, but the
plan calls for this resource to be destroyed. To avoid this error and
continue with the plan, either disable lifecycle.prevent_destroy or reduce
the scope of the plan using the -target flag.

What this error means

prevent_destroy is a lifecycle guard that makes Terraform error out instead of destroying a resource. It fires both on an outright destroy and on a replacement, because a replacement is a destroy followed by a create. When you see this, your plan includes a step that would destroy the guarded resource. The interesting question is why. On stateful resources it is usually because a force-new attribute changed, meaning the provider cannot modify it in place and would recreate the resource, taking the data with it. The guard is doing its job by stopping that.

Common causes

  • A change to a force-new attribute (for a database, things like the engine version in some cases, instance class changes that force replacement, subnet group, or identifier) that plans a replace.
  • An attempt to destroy the resource directly while the guard is still in place.
  • A change high up in a module that cascades into recreating a protected resource.
  • Drift or a provider change that now reports an attribute as requiring replacement.
  • A refactor that would move the resource’s state address, which Terraform sees as destroy plus create.

How to fix it

Stabilize

  1. Do not remove the guard reflexively. First read the plan carefully to see exactly what Terraform wants to do to the protected resource, and why.
terraform plan
  1. Look for the resource marked for replacement, shown as -/+ with a # forces replacement note next to the attribute driving it. That attribute is the reason.

Diagnose

  1. Identify the force-new attribute. If the change to it is not actually necessary, the right fix is to not make that change, and the replacement disappears.

  2. If you believe the resource should not be changing at all, the plan may be reacting to drift or a state address move. Refresh and re-check.

terraform apply -refresh-only
terraform plan

Fix

The safest fix is to avoid the replacement: revert the change to the force-new attribute, or apply your intended change through a path the provider can do in place. If the replacement is being caused by a state address move (a rename or module refactor), move the state instead of recreating:

terraform state mv aws_db_instance.old aws_db_instance.primary

Only if you genuinely intend to destroy or replace the resource, and have taken a snapshot and planned a window, remove the guard deliberately, then apply:

resource "aws_db_instance" "primary" {
  # ...
  lifecycle {
    prevent_destroy = false
  }
}
terraform apply

Restore prevent_destroy = true afterward so the guard protects the new resource going forward.

How to prevent it

  • Keep prevent_destroy = true on databases and other stateful resources, and treat any plan that wants to replace them as a red flag.
  • Read plans for forces replacement notes before applying, especially on stateful resources.
  • Use terraform state mv for renames and refactors so a moved resource is not destroyed and recreated.
  • Snapshot stateful resources before any change that might replace them.
  • Pin provider versions so an upgrade does not silently start forcing replacements on attributes it used to change in place.

When to call a senior engineer

If a plan insists on replacing a production database and you cannot see why, if avoiding the replacement is not obvious, or if you must migrate the resource without downtime or data loss, this is exactly the situation the guard was built for and it deserves careful hands. A wrong apply here destroys live data. Erzon engineers can find what is forcing the replacement, plan a non-destructive path such as a state move or an in-place change, and carry out any real migration with snapshots and a rollback plan so your data stays safe.

Related errors we fix

Still down after this?

Want a senior engineer to just fix it?

Paste this exact error into the form. A senior engineer replies within one business hour with what is likely wrong and a flat quote. Triage is free.

Book a fix

← Back to all error fixes

Still down?

Get a senior engineer on it now

Leave your email and a line about what is happening. A senior engineer replies within one business hour with what is likely wrong and a flat quote. Triage is free.

Or email [email protected]. Same engineers, same one-hour clock.