How Terraform Really Works Under the Hood (Modules, State, DAGs Explained)

Learn how Terraform actually works behind the scenes, including DAG-based execution, state files, root modules, and project structure. A technical deep dive for DevOps engineers.

Terraform architecture diagram showing DAG, state, and module system

Last updated: July 17, 2025

How Terraform Really Works Under the Hood (Modules, State, DAGs Explained)

First time using Terraform? Start here: Beginner’s Guide to Terraform, it lays the groundwork for everything that follows.

Introduction

You’ve probably used Terraform to spin up an EC2 instance or an S3 bucket and thought: “Wow, that was easy.” But what’s actually going on behind the scenes when you run terraform apply? How does Terraform know what order to create things in? What’s in that mysterious .terraform/ folder?

This post is for DevOps engineers, SREs, and infrastructure-minded developers who want to understand how Terraform really works under the hood, from state management and dependency graphs to the module system and directory structure. Knowing this stuff is the difference between “getting it to work” and building scalable, team-ready infrastructure.

Let’s crack open the black box.


1. Terraform’s Execution Flow at a Glance

Here’s what Terraform does, step-by-step:

terraform init   →   terraform validate   →   terraform plan   →   terraform apply   →   terraform destroy

What happens at each stage:

  • init: Downloads the provider plugins and sets up .terraform/
  • validate: Checks that your configuration is syntactically valid
  • plan: Compares your configuration to the existing state to show what will change
  • apply: Provisions (or updates/destroys) infrastructure based on the plan
  • destroy: Deletes all resources defined in your code

Pro Tip: terraform plan -out=tfplan lets you save a plan to disk and apply it later with terraform apply tfplan for more auditable workflows.


2. The .terraform Directory: What’s Inside?

Once you run terraform init, Terraform creates a hidden .terraform/ folder in your project root.

Here’s what it typically contains:

.terraform/
├── providers/        → Downloaded binaries for each provider (e.g., AWS)
├── terraform.lock.hcl → Dependency lock file
├── plugins/          → Deprecated in newer versions
└── modules/          → Caches reused modules

Why it matters:

  • Caching: Avoids re-downloading provider binaries each time
  • Locking: Ensures consistent provider versions across machines
  • Modular reusability: Improves performance by caching modules

Never manually edit anything in .terraform/. You can delete it safely and re-init the project.


3. State Files: Terraform’s Source of Truth

Terraform stores a snapshot of your infrastructure in a state file called terraform.tfstate. This file is critical.

Why does it exist?

Terraform is declarative, it knows what you want, but it needs to compare that to what you already have.

Without state, Terraform can’t:

  • Track what resources exist
  • Detect drift
  • Create efficient plans
  • Manage dependencies

Local vs Remote State:

Type Pros Cons
Local (terraform.tfstate) Simple for small projects Not collaborative, risky in teams
Remote (e.g., S3 + DynamoDB) Team-safe, state locking, backups Slightly more setup

Never manually edit the .tfstate file. Use terraform state commands to inspect or modify state safely.


4. The DAG: Terraform’s Dependency Brain

Terraform builds a Directed Acyclic Graph (DAG) of resources. This graph determines the order of operations during a plan or apply.

Example:

resource "aws_vpc" "main" {
  ...
}

resource "aws_subnet" "web" {
  vpc_id = aws_vpc.main.id
  ...
}

Terraform sees that aws_subnet.web depends on aws_vpc.main, and will always create the VPC first. If a resource fails, dependent resources are skipped.

DAG Benefits:

  • Parallelism: Terraform can build independent resources concurrently
  • Safety: Ensures correct dependency ordering
  • Clarity: You can visualize the graph with terraform graph | dot -Tpng > graph.png

Understanding Terraform’s DAG helps debug strange errors when a resource doesn’t seem to behave as expected.


5. The Root Module and Implicit Structure

A Terraform module is just a folder containing .tf files. When you run Terraform in a directory, that directory is the root module.

All .tf files in the directory are loaded and combined. There’s no special priority based on filenames like main.tf, it’s just convention.

Root Module Example:

infra/
├── main.tf
├── variables.tf
├── outputs.tf
└── terraform.tfvars

Each of these files contributes to one big module that Terraform uses as the root.

You can nest modules using the module block to reuse code and organize your infra.

module "network" {
  source = "./modules/network"
  cidr_block = var.vpc_cidr
}

6. File Loading Order and Variable Precedence

Terraform loads all .tf files in lexical order, but treats them as a single config. There’s no need to import or include anything manually.

Variable precedence:

  1. CLI flags (-var) or -var-file
  2. terraform.tfvars
  3. Environment variables
  4. Default values in variables.tf

Use terraform console to test variable evaluation and expressions interactively.


7. Best Practices: Structure, Versioning, and Secrets

Following clean practices helps avoid headaches and keeps infra predictable.

project-root/
├── modules/
│   └── vpc/
│       └── main.tf
├── main.tf
├── variables.tf
├── outputs.tf
├── terraform.tfvars
└── .terraform/ (generated)

Additional Best Practices:

  • Use terraform fmt and validate in CI/CD
  • Lock provider versions with required_providers
  • Use remote state for team environments
  • Store secrets in environment variables or Vault, not in .tf files

Consider using tools like sops, chamber, or Terraform Cloud’s variable store for secrets management.


8. Visual Guide: Anatomy of a Terraform Project Folder

project-name/
├── .terraform/              # auto-generated
├── terraform.tfstate        # local state
├── terraform.tfstate.backup
├── main.tf
├── variables.tf
├── outputs.tf
├── terraform.tfvars         # input variable values
├── modules/
│   └── my-module/
│       ├── main.tf
│       ├── variables.tf
│       └── outputs.tf
└── README.md

Conclusion

Terraform may look simple, but behind the scenes it’s a sophisticated system that uses declarative code, DAGs, state management, and a modular architecture to manage infrastructure at scale.

Understanding how Terraform really works helps you:

  • Avoid silent failures or drift
  • Optimize performance via parallelism
  • Structure projects for team collaboration
  • Debug like a pro

Thanks for reading!



Frequently Asked Questions

Q: Can I delete the .terraform/ folder?
A: Yes. It’s safe to delete. Terraform will regenerate it when you run terraform init again.
Q: Why does Terraform need a state file?
A: The state file tracks your infrastructure, enabling Terraform to know what to create, update, or delete.
Q: How does Terraform know what order to create resources in?
A: Terraform builds a dependency graph (DAG) based on resource relationships and executes resources in that order.
Q: Is main.tf a special file?
A: No. All .tf files in the root module are treated equally. main.tf is just a naming convention for readability.
Q: Can I use multiple state files?
A: Yes. You can manage multiple states by splitting infrastructure into different root modules or using workspaces.

Categories

DevOps Tutorials

Want to keep learning?

Explore more tutorials, tools, and beginner guides across categories designed to help you grow your skills in real-world tech.

Browse All Categories →