The Terraform timesleep resource is a deliberate pause mechanism that inserts a configurable wait period into the Terraform execution graph during resource creation or destruction. While introducing artificial delays can feel like an anti-pattern, the timesleep resource exists to address real timing gaps where Terraform's dependency graph alone cannot guarantee correctness. IAM role propagation in AWS can take up to 30 seconds, DNS records need time to propagate, and some APIs have rate limits that require spacing out requests. The time_sleep resource handles these cases gracefully within your Terraform configuration.
In this guide we will explore when and how to use time_sleep effectively. We will cover IAM propagation delays, DNS propagation waits, staged deployment patterns, and rate limiting strategies.
Understanding time_sleep
The time_sleep resource pauses Terraform execution for a specified duration during create or destroy operations. It supports separate durations for creation and destruction, and it participates in Terraform's dependency graph like any other resource.
The resource is provided by the official HashiCorp time provider and is intended to manage a resource that delays creation and/or destruction, typically for further resources. This prevents cross-platform compatibility and destroy-time issues with using the local-exec provisioner.
In many cases, this resource should be considered a workaround for issues that should be reported and handled in downstream Terraform Provider logic.
The core schema is simple. A timesleep resource is defined with a createduration and optionally a destroy_duration. Terraform will wait the specified amount of time after the resource is created and before it is destroyed, and it will enforce ordering for any resources that depend on it.
hcl
resource "time_sleep" "wait_3_seconds" {
create_duration = "3s"
}
The duration string accepts standard Go duration notation: s for seconds, m for minutes, h for hours. Because the resource participates in the dependency graph, any resource that references timesleep.wait3seconds.id or declares dependson on it will not start until the sleep completes.
When to use time_sleep and when to avoid it
Use time_sleep when dealing with eventual consistency in AWS services like IAM propagation, when you need to respect API rate limits, and when implementing staged deployments that require cooldown periods.
Do not use time_sleep when proper resource dependencies would solve the problem. If resource B naturally depends on resource A through a reference, Terraform already handles the ordering. Also avoid using sleep to work around Terraform bugs as those should be reported and fixed instead.
| Scenario | Use time_sleep | Reason |
|---|---|---|
| IAM role propagation in AWS | Yes | Propagation can take up to 30 seconds and is not observable via attribute |
| DNS propagation wait | Yes | External DNS systems propagate asynchronously |
| API rate limiting | Yes | Space out requests to avoid throttling |
| Staged deployments with cooldown | Yes | Enforce deliberate pause between phases |
| Security group before instance | No | Implicit dependency via vpcsecuritygroup_ids handles ordering |
| General bug workaround | No | Should be reported to provider |
When sleep is NOT needed
Terraform automatically handles ordering when resources reference each other. No sleep is needed when proper dependencies exist.
```hcl
resource "awssecuritygroup" "app" {
name = "app-${var.environment}"
vpcid = var.vpcid
}
variable "vpc_id" {
type = string
default = "vpc-12345"
}
No sleep needed - Terraform knows to create the SG first
resource "awsinstance" "app" {
ami = "ami-12345678"
instancetype = "t3.medium"
vpcsecuritygroupids = [awssecurity_group.app.id] # Implicit dependency
}
```
In this example the instance cannot be created until the security group ID is known, so Terraform enforces the correct order without any manual delay.
Practical patterns for time_sleep
IAM propagation delays
IAM changes are eventually consistent. Creating an IAM role and immediately assuming it in a new resource can fail even though Terraform reports the role as created.
A common pattern is to insert a sleep between role creation and its first use.
```hcl
resource "awsiamrole" "example" {
name = "example-role"
# ...
}
resource "timesleep" "iampropagation" {
createduration = "30s"
dependson = [awsiamrole.example]
}
resource "awsiamrolepolicyattachment" "example" {
role = awsiamrole.example.name
policyarn = "arn:aws:iam::aws:policy/ReadOnlyAccess"
dependson = [timesleep.iampropagation]
}
```
DNS propagation waits
When a DNS record is created by Terraform and a subsequent resource needs the name to resolve, a sleep can provide a safety margin.
```hcl
resource "awsroute53record" "app" {
zoneid = var.zoneid
name = "app.example.com"
type = "A"
records = [awslb.app.dnsname]
}
resource "timesleep" "waitfordns" {
createduration = "60s"
dependson = [awsroute53_record.app]
}
data "http" "healthcheck" {
url = "https://app.example.com/health"
dependson = [timesleep.waitfor_dns]
}
```
Staged deployment patterns
Staged deployments often require a cooldown between phases to allow metrics to stabilize or users to validate.
```hcl
resource "helm_release" "ampa" {
name = "ampa"
chart = "/home/git/ampa/helm-ampa"
timeout = 600
namespace = var.namespace
}
resource "timesleep" "waitforingressalb" {
createduration = "300s"
dependson = [helm_release.ampa]
}
data "kubernetesingress" "web" {
metadata {
name = "votacions-ampa"
namespace = var.namespace
}
dependson = [timesleep.waitforingressalb]
}
```
First, you need to use the time provider and the timesleep resource. In your code you deploy resource1 first, then set timesleep to have a dependency on resource1 using dependson, then you declare resource2 or data1 with a dependson to the timesleep resource. So it goes: resource1 => timesleep => data1/resource2 in that order.
Rate limiting with sequential creation
A resource created by terraform after its creation consumes CPU/RAM on cluster where it is created, so some kind of delay is needed before the next resource on the same cluster is created.
As an option to achieve this it was decided to use time_sleep terraform resource to implement some delay before resources creation. It was also decided to use -parallelism=1 so that resources were created one by one.
```hcl
resource "timesleep" "wait3seconds" {
createduration = "3s"
}
resource "nullresource" "topicevents" {
triggers = {
alwaysrun = timestamp()
topic = var.topicname
}
dependson = [timesleep.wait3seconds]
}
```
When used with for_each, the sleep is instantiated per instance. This can create a burst of sleep resources.
hcl
module "test" {
for_each = tomap(var.environments[var.dim_arr].clusters.events.topics)
source = "./test"
topic_name = "${var.dim_arr}.${each.value.topic}"
}
The logic is that input values are handled in the loop but because of the time_sleep resource in the test module some delay is introduced to this loop, that in turn should decrease a load to the server.
However terraform tries to create all timesleep resources in the nested module and then iterates through the objects in the main module and creates them this way:
- all timesleep resources are created
- all resources that depend on them are created
This behavior means the sleep is applied per instance but the creation of all sleep resources is batched first. For true serial throttling, combine time_sleep with -parallelism=1 and ensure each iteration depends on the previous iteration's sleep, often via a chain or by using a single shared sleep resource rather than one per module instance.
Sequential deployment without per-loop wait
If you want to deploy resources sequentially and not wait between iterations in a loop, this can be done using the time_sleep resource and there are many examples online. For example:
```hcl
resource "helm_release" "ampa" {
name = "ampa"
chart = "/home/git/ampa/helm-ampa"
timeout = 600
namespace = var.namespace
}
resource "timesleep" "waitforingressalb" {
createduration = "300s"
dependson = [helm_release.ampa]
}
data "kubernetesingress" "web" {
metadata {
name = "votacions-ampa"
namespace = var.namespace
}
dependson = [timesleep.waitforingressalb]
}
```
The key is to create a dependency chain: resource1 => time_sleep => data1/resource2.
Configuration guidelines
- Use time_sleep when dealing with eventual consistency in AWS services like IAM propagation, when you need to respect API rate limits, and when implementing staged deployments that require cooldown periods.
- Do not use time_sleep when proper resource dependencies would solve the problem. If resource B naturally depends on resource A through a reference, Terraform already handles the ordering. Also avoid using sleep to work around Terraform bugs as those should be reported and fixed instead.
The time_sleep resource is a practical tool for handling timing-related issues in Terraform deployments. While it should be used sparingly, it is invaluable for dealing with IAM propagation, DNS propagation, staged deployments, and API rate limits. The key is to use it only when proper resource dependencies are not sufficient to solve the timing issue.
For other time-based patterns in Terraform, check out timerotating for scheduled rotations and timeoffset for date calculations.
Conclusion
The time_sleep resource is a practical tool for handling timing-related issues in Terraform deployments. While it should be used sparingly, it is invaluable for dealing with IAM propagation, DNS propagation, staged deployments, and API rate limits. The key is to use it only when proper resource dependencies are not sufficient to solve the timing issue.
Timesleep belongs in the toolbox for eventual consistency, rate limiting, and deliberate cooldowns, not as a substitute for correct dependency modeling. When used with explicit dependson chains and a clear understanding of Terraform's parallel execution model, it provides a safe, observable pause that keeps deployments reliable without hiding underlying provider issues. Reserve it for cases where external systems enforce their own latency, document each usage with the reason for the delay, and revisit the configuration as providers improve their readiness checks.