Skip to content
Error: Cycle in dependency graph

Error: Cycle in dependency graph

DodaTech 3 min read

Terraform’s “Cycle in dependency graph” error means A depends on B and B depends on A, forming a circular dependency Terraform cannot resolve.

What It Means

Terraform builds a dependency graph from your resource references. When it detects a loop in this graph, it halts with a cycle error. This is a safety mechanism — applying in a cycle would cause Terraform to deadlock trying to resolve the order.

Why It Happens

  • Resource A references attribute .id of resource B, and resource B references attribute .id of resource A.
  • Module A calls module B, and module B calls module A (nested module cycle).
  • A resource references itself through a chain of count or for_each expressions.
  • depends_on is used to create explicit reverse dependencies that form a loop.
  • Output values in modules create implicit reverse dependencies.

How to Fix It

Step 1: Locate the cycle in the error output

Terraform prints the cycle path in the error message:

Error: Cycle: aws_instance.a, aws_security_group.b, aws_instance.a

Step 2: Remove one of the circular references

If resource A references resource B and B references A, remove one reference. For example, if both resources need each other’s IP addresses, restructure to avoid the bidirectional dependency:

# ❌ Cycle: aws_instance.a needs aws_instance.b.private_ip, 
#           aws_instance.b needs aws_instance.a.private_ip

# ✅ Fix: use a static IP or a separate resource like an elastic IP
resource "aws_eip" "a" {
  instance = aws_instance.a.id
}

resource "aws_instance" "b" {
  # Reference the EIP instead of the instance directly
}

Step 3: Use depends_on to break explicit cycles

If you added depends_on that creates a cycle, remove the unnecessary depends_on. Terraform automatically infers most dependencies from references:

# ❌ Remove explicit depends_on that creates a cycle
resource "aws_instance" "a" {
  depends_on = [aws_instance.b]  # Only if absolutely necessary
}

Step 4: Restructure modules to avoid circular calls

If modules call each other in a cycle, extract shared resources into a third module:

# ❌ Module "network" calls module "compute", 
#    module "compute" calls module "network"

# ✅ Fix: create a shared module for common resources
module "shared" {
  source = "./modules/shared"
}

module "network" {
  source  = "./modules/network"
  depends_on = [module.shared]
}

module "compute" {
  source  = "./modules/compute"
  depends_on = [module.shared]
}

Step 5: Validate after changes

terraform validate
terraform plan
Can I use -target to bypass a cycle error?
Using terraform apply -target=resource_a can sometimes work around a cycle for a single operation, but it’s not a fix — the cycle will reappear on the next full apply. Always resolve the underlying circular dependency rather than using targets as a workaround.
What causes cycles with count and for_each?
When you use count or for_each that references another resource’s computed attributes, Terraform may create implicit dependencies that form a cycle. For example, resource.a[count.index] depending on resource.b[count.index] while resource.b depends on data from resource.a. Use toset() or restructure to flatten the dependency chain.
How do I see the dependency graph without running plan?
Use terraform graph to output the dependency graph in DOT format, then render it with Graphviz: terraform graph | dot -Tpng > graph.png. This visual representation makes cycles much easier to spot than reading configuration files alone.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro