.gitignore for Terraform

**/.terraform/* *.tfstate *.tfstate.* *.tfvars *.tfvars.json override.tf *_override.tf *.tfplan

A Terraform .gitignore ignores the .terraform/ folder that terraform init fills, every state file (*.tfstate, *.tfstate.*), variable files (*.tfvars, *.tfvars.json), local override files, and saved plans (*.tfplan). Those are the key lines of the tool’s Terraform template, which has 14, with crash logs and CLI configuration files making up the rest.

What the blocks ignore

  • Downloads: **/.terraform/*, the providers and modules terraform init fetches, in every root module.
  • State: *.tfstate, *.tfstate.* for backups and the .terraform.tfstate.lock.info lock. State holds every attribute of every resource, secrets included, in plain text; keep it in a remote backend.
  • Variables: *.tfvars and *.tfvars.json, where passwords and keys usually end up.
  • Local changes: override.tf, *_override.tf and their JSON forms.
  • Plans and config: *.tfplan, crash.log, .terraformrc and terraform.rc, which can hold registry credentials.

In a scratch repository with this file, git check-ignore -v -n --no-index names the line that matched each path, or prints :: when none did:

.gitignore:3:**/.terraform/*	envs/prod/.terraform/modules/modules.json
::	.terraform.lock.hcl
::	envs/prod/.terraform.lock.hcl
.gitignore:5:*.tfstate	terraform.tfstate
.gitignore:6:*.tfstate.*	terraform.tfstate.backup
.gitignore:6:*.tfstate.*	.terraform.tfstate.lock.info
.gitignore:10:*.tfvars	prod.auto.tfvars
::	terraform.tfvars.example
.gitignore:18:*.tfplan	plan.tfplan
::	tfplan

Commit .terraform.lock.hcl

The dependency lock file sits beside .terraform/, not inside it, so it stays tracked, as Terraform’s documentation asks: it pins the exact provider versions and checksums terraform init installs. A broad line such as .terraform* would catch it too, and then every machine picks its own provider versions:

.gitignore:2:.terraform*	.terraform.lock.hcl

Saved plans need the extension

terraform plan -out=tfplan writes a file with no extension, which the template misses (:: tfplan above). Name plans *.tfplan, or add the name as a custom line: saved plans aren’t encrypted and can include sensitive values.

Keeping a variables file

*.tfvars also catches *.auto.tfvars files of plain settings you may want to share. A later ! line brings one back, because its folder isn’t excluded:

.gitignore:24:!network.auto.tfvars	network.auto.tfvars
.gitignore:10:*.tfvars	secrets.auto.tfvars

A file in an excluded folder can’t come back that way. **/.terraform/* excludes .terraform/modules/ as a folder, and Git never looks inside it, so !.terraform/modules/modules.json leaves the file reported against line 3.

Committed state already?

.gitignore only affects untracked files. Untrack the state, keeping it on disk, and commit:

git rm --cached terraform.tfstate terraform.tfstate.backup
git commit -m "Stop tracking Terraform state"

The old versions stay in the history, so rotate any secret they held, and move the state to a remote backend.

Editor and OS files

.idea/, .vscode/ and .DS_Store come from your machine. Keep them in a global ignore file:

git config --global core.excludesFile ~/.gitignore_global

Try it in the Gitignore Generator

In the file: Terraform

.gitignore

1 template · 14 pattern lines
# Terraform
# Providers and modules downloaded by terraform init
**/.terraform/*
# State files: they hold everything the plan knows about the account
*.tfstate
*.tfstate.*
crash.log
crash.*.log
# Variable files, which is where passwords and keys usually end up
*.tfvars
*.tfvars.json
# Local overrides
override.tf
override.tf.json
*_override.tf
*_override.tf.json
# Saved plans, written by terraform plan -out
*.tfplan
# CLI configuration
.terraformrc
terraform.rc
  • ✓Not ignored: no line in this file matches "/.terraform.lock.hcl".

Lines are applied top to bottom, and the last one that matches wins — which is why a line of your own can overrule a template.

Runs in your browser. Nothing you enter is sent anywhere.