🎉 DevOps Interview Prep Bundle is live — 1000+ Q&A across 20 topicsGet it →
All Articles

Terraform Apply Hangs Forever: Fix in 5 Minutes

terraform apply stuck with no output, no progress, no error? Here is exactly how to diagnose provider API rate limits, state lock deadlocks, dependent resource waits, and network issues causing Terraform to hang indefinitely.

Shubham4 min read
Share:Tweet

A hanging terraform apply is worse than a failing one — no error message means no obvious starting point. Here is how to find out what it's actually waiting on.

Step 1: Get Verbose Output to See What's Actually Happening

bash
# Ctrl+C the hung apply first, then re-run with debug logging
TF_LOG=DEBUG terraform apply 2>&1 | tee terraform-debug.log
 
# While it's hanging, in another terminal, check the last few lines
tail -f terraform-debug.log

The last log lines before it goes silent tell you exactly which resource/API call it's stuck on — this is the single most useful diagnostic step and most people skip it.

Cause 1: Waiting on a Resource With a Long Create/Update Timeout

# In the debug log, you'll see something like:
# [DEBUG] Waiting for state to become: [available]
# aws_rds_cluster.main: Still creating... [10m0s elapsed]

This isn't actually hung — RDS clusters, EKS clusters, and some managed services genuinely take 10-20+ minutes to provision, and Terraform waits for the resource to report "ready" by design.

bash
# Check the resource's actual status directly, outside Terraform
aws rds describe-db-clusters --db-cluster-identifier mycluster --query 'DBClusters[0].Status'

Fix — if it's genuinely still provisioning, just wait. If AWS reports it as failed or stuck while Terraform is still "creating," the underlying resource has a real problem Terraform is just waiting on:

bash
aws rds describe-events --source-identifier mycluster --source-type db-cluster

Cause 2: Rate Limited by the Provider API, Retrying Silently

# TF_LOG=DEBUG shows repeated silent retries:
# [DEBUG] [aws-sdk-go] DEBUG: Retrying Request
# Response body: <Error><Code>Throttling</Code>

Terraform's default retry behavior for throttled API calls can silently retry with exponential backoff for a long time without printing anything to normal output — it looks hung but is actually working through rate limits.

Fix — reduce parallelism so you generate fewer concurrent API calls:

bash
terraform apply -parallelism=5    # Default is 10, lower it for accounts hitting rate limits
hcl
# Or configure retry behavior explicitly in the provider block
provider "aws" {
  max_retries = 25
}

Cause 3: State Lock Held by a Stuck Previous Run

bash
terraform apply
# Sits with no output at all, not even "Acquiring state lock..."

If you don't even see the lock acquisition message, check if a previous run's lock is actually the block — some backends fail this silently rather than erroring immediately.

bash
# For S3 + DynamoDB backend, check the lock table directly
aws dynamodb scan --table-name terraform-locks --query 'Items[*].LockID'

Fix — if the lock is stale (owning process is genuinely dead, not just slow):

bash
terraform force-unlock LOCK_ID

Only force-unlock after confirming the process that created the lock is actually dead — check with whoever might be running a concurrent apply first. See our Terraform state lock error fix for the full diagnostic flow.

Cause 4: Network Path to the Provider API Is Broken, No Timeout Set

bash
# If TF_LOG=DEBUG shows nothing at all after "Acquiring state lock" —
# not even a request being made — check basic connectivity
curl -v https://ec2.us-east-1.amazonaws.com
 
# On a VPN, corporate proxy, or air-gapped runner, this can hang
# indefinitely with no built-in Terraform timeout for the initial connection

Fix — verify network path and set explicit timeouts where the provider supports it:

bash
# Check if a proxy is required and set it
export HTTPS_PROXY=http://proxy.internal:8080
terraform apply
hcl
# Some providers support explicit timeouts per-resource
resource "aws_db_instance" "main" {
  # ...
  timeouts {
    create = "20m"    # Explicit ceiling instead of an indefinite wait
  }
}

Cause 5: A Data Source Depends on a Resource That Will Never Become Available

hcl
# If a data source references something that's misconfigured to never
# satisfy its own dependency, Terraform can appear stuck evaluating the graph
data "aws_lb_target_group" "existing" {
  name = aws_lb_target_group.new.name    # circular-ish dependency risk
}
bash
terraform graph | dot -Tsvg > graph.svg    # Visualize the dependency graph to spot cycles or bad ordering

Fix — break the implicit dependency, reference outputs directly instead of re-querying:

hcl
# Instead of a data source re-lookup, reference the resource's own output
output "target_group_arn" {
  value = aws_lb_target_group.new.arn
}

Diagnostic Checklist

bash
TF_LOG=DEBUG terraform apply 2>&1 | tail -50    # what was it doing last?
curl -v https://<provider-api-endpoint>          # basic connectivity check
aws dynamodb scan --table-name terraform-locks   # stale lock check (if using S3+DynamoDB backend)
terraform apply -parallelism=5                   # reduce concurrent API load

More Terraform troubleshooting? Read our Terraform state lock error fix and Terraform provider version conflict fix.

🔧

Today I Fixed

Short real fixes from production — posted daily

Browse fixes
Newsletter

Stay ahead of the curve

Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.

Related Articles

Comments