Skip to main content
Cloud providers set sensible default timeouts for resource operations, but production infrastructure regularly exceeds them. A large RDS instance can take 45 minutes to provision. An EKS cluster routinely needs 20–30 minutes. When Pulumi’s default timeout expires before the resource is ready, the apply fails — even though the resource would have succeeded with more time. The timeouts sub-block lets you override these provider defaults per operation (create, update, delete). Paired with ignore_changes, which prevents Pulumi from resetting attributes managed externally — passwords rotated by a secrets manager, tags applied by a cost allocation tool, scaling counts adjusted by an autoscaler — you have precise control over how Pulumi interacts with slow or partially-external resources.

What you’ll learn

  • How timeouts overrides provider-default operation wait times
  • How ignore_changes prevents drift from triggering unwanted updates
  • When to combine both on the same resource

Why this matters

Timeout failures are apply-time surprises — the plan succeeds, then the apply hangs and times out. Setting explicit timeouts in your .iac source means CI pipelines and operators see consistent, predictable behaviour rather than intermittent failures on slow resources.

The source code

# timeouts override provider-default wait times per operation.
# Useful for slow resources: large RDS instances, EKS clusters, NAT gateways.
unit "aws_rds_instance" "db" {
  identifier          = "myapp-${input.env}"
  allocated_storage   = 50
  engine              = "postgres"
  engine_version      = "15"
  instance_class      = "db.r6g.xlarge"
  username            = "admin"
  password            = "placeholder"
  skip_final_snapshot = true

  # Suppress drift on password — rotated externally by secrets manager.
  lifecycle {
    ignore_changes = ["password"]
  }

  timeouts {
    create = "60m"
    update = "30m"
    delete = "20m"
  }
}

# EKS clusters routinely take 20-30 minutes — extend the default.
unit "aws_eks_cluster" "main" {
  name     = "myapp-${input.env}"
  role_arn = "arn:aws:iam::123456789012:role/eks-role"

  vpc_config = {
    subnet_ids = ["subnet-abc123", "subnet-def456"]
  }

  lifecycle {
    ignore_changes = ["tags"]
  }

  timeouts {
    create = "30m"
    delete = "20m"
  }
}

How it works

1

Timeouts are passed to the Pulumi resource options

The timeouts sub-block compiles to Pulumi’s CustomTimeouts resource option. Each duration string ("60m", "30m") overrides the provider’s built-in default for that operation. Pulumi waits up to that duration before failing the apply.
2

ignore_changes is passed as a resource option too

The list of attribute names in ignore_changes compiles to Pulumi’s ignoreChanges resource option. On every subsequent apply, Pulumi reads the live state for those attributes but does not include them in the diff — changes to them are silently accepted.
3

Both can appear on the same resource

lifecycle and timeouts are independent sub-blocks. A slow resource with externally-managed attributes — like a large RDS instance whose password is rotated by a secrets manager — benefits from both simultaneously.

What ubx generates

// Generated by ubx — do not edit
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const config = new pulumi.Config();
const env = config.get("env") || "dev";

const db = new aws.rds.Instance("db", {
    identifier: `myapp-${env}`,
    allocatedStorage: 50,
    engine: "postgres",
    engineVersion: "15",
    instanceClass: "db.r6g.xlarge",
    username: "admin",
    password: "placeholder",
    skipFinalSnapshot: true,
}, {
    ignoreChanges: ["password"],
    customTimeouts: { create: "60m", update: "30m", delete: "20m" },
});

const main = new aws.eks.Cluster("main", {
    name: `myapp-${env}`,
    roleArn: "arn:aws:iam::123456789012:role/eks-role",
    vpcConfig: { subnetIds: ["subnet-abc123", "subnet-def456"] },
}, {
    ignoreChanges: ["tags"],
    customTimeouts: { create: "30m", delete: "20m" },
});

export const clusterEndpoint = main.endpoint;

Common mistakes

Timeout strings must be valid Go duration format: "60m", "2h", "90s". An invalid string like "1 hour" or "60 minutes" will cause a runtime error. Use the abbreviated forms: s for seconds, m for minutes, h for hours.
Attribute names in ignore_changes use the Pulumi camelCase SDK name, not the .iac snake_case name. password stays password, but desired_capacity becomes desiredCapacity in the generated Pulumi option. Check the provider SDK docs if you’re unsure of the correct name.

Run it

ubx validate
ubx plan   # requires AWS credentials
ubx apply  # EKS and RDS creation may take 20-60 minutes

What you learned

timeouts overrides the provider default wait time per operation (create, update, delete)
ignore_changes prevents Pulumi from resetting externally-managed attributes on apply
Both can appear on the same resource — they compile to independent Pulumi resource options

Next steps

Dynamic blocks

Generate repeated nested blocks from a collection

Lifecycle block

prevent_destroy and create_before_destroy