Terraform plan and apply have historically been constrained by the time it takes to reconcile the state file with real infrastructure. Every run normally begins with a preemptive refresh where each resource is read back from the provider. For large states that means hundreds or thousands of remote API calls, each adding latency and cost. The -refresh=false flag and the related -refresh-only plan mode change that behavior by letting teams choose when refresh happens and when it can be skipped entirely.
How Terraform Refresh Works by Default
By default terraform plan refreshes every resource before evaluating changes.
$ terraform plan
aws_vpc.main: Refreshing state... [id=vpc-abc123]
aws_subnet.private[0]: Refreshing state... [id=subnet-def456]
aws_subnet.private[1]: Refreshing state... [id=subnet-ghi789]
aws_security_group.web: Refreshing state... [id=sg-jkl012]
Each Refreshing state line means Terraform is asking the provider to read the current object from AWS or whatever provider you are using. These reads check whether the real resource still matches what Terraform has recorded in the state file.
Terraform refreshes its state by querying the infrastructure to detect any changes made outside of Terraform, such as manual changes or updates by other tools. This process ensures that the Terraform state file reflects the current state of the infrastructure.
Does Terraform plan refresh the state?
Yes, Terraform refreshes the state and updates the state file with the most recent resource configuration of the current infrastructure. It ensures that the plan reflects the current state rather than the desired state before any changes are made.
The biggest time sink in terraform plan is often the refresh phase. During refresh, Terraform asks providers to read the current status of every resource in your state, which can require one or more remote API calls per resource. With 500 resources, that can mean 500 plus API calls, and each one takes time.
What -refresh=false Does and Does Not Skip
The -refresh=false flag tells Terraform to skip that state synchronization step and trust that the state file is already up to date.
Using -refresh=false
```
Skip the refresh phase entirely
terraform plan -refresh=false
Works with apply too
terraform apply -refresh=false
```
You can apply the Terraform refresh by running the terraform apply -refresh=false command. It skips the refresh and applies the plan based on the existing state.
This can reduce plan time from minutes to seconds. But it comes with trade-offs you need to understand before using it.
What is skipped:
- Provider read calls for every resource in the state
- Detection of out-of-band drift during that specific run
What is not skipped:
- State locking
- Plan computation based on configuration vs existing state
This means you still get protection against concurrent modifications:
```
This still locks the state (prevents concurrent access)
terraform plan -refresh=false
If you also want to skip the lock (very development only)
terraform plan -refresh=false -lock=false
```
Skipping both refresh and lock is the fastest possible plan but should only be used for local development where you are the only person working on the configuration.
Terraform Cloud and Enterprise Planning Options
HashiCorp Terraform Cloud and Terraform Enterprise now offer new options to provide more explicit control over when resource state is refreshed and when resources need to be replaced. With these new features customers can reduce the time it takes to make changes to their infrastructure. These features have been available in open source Terraform in June of this year.
Introducing -refresh=false Planning Option and -refresh-only Plan Mode
The Terraform plan and apply workflow normally begins with a preemptive refresh of the state of your infrastructure — this ensures that any changes that have happened out-of-band are detected and that your infrastructure converges back to what is defined in code. The time taken for this refresh can be considerable when managing large scale deployments, and if you’re all-in on Terraform Cloud or Enterprise it can be unnecessary as Terraform already has the latest state. That’s time you and your team can get back so that your changes are quicker to plan and quicker to deploy! Support for the -refresh=false plan option with Terraform Cloud and Enterprise means teams can now execute their plans and applies without that preemptive state refresh.
Out-of-band changes can still occur though, and without a refresh Terraform Cloud and Enterprise will not know that configuration has drifted. For that reason we've also added a complementary -refresh-only plan mode, that customers can queue on whatever schedule or event is appropriate to their needs.
The -refresh-only plan mode is useful for periodically syncing your state with reality:
```
Workflow: periodic refresh + fast plans
Step 1: Refresh state (do this on a schedule or before a session)
terraform apply -refresh-only -auto-approve
Step 2: Fast plans throughout the day
terraform plan -refresh=false
terraform plan -refresh=false -target=module.api
terraform plan -refresh=false -var="scaling_max=10"
```
Performance Impact and Real World Measurements
Performance Comparison
```
With refresh (default) - 500 resource state
$ time terraform plan
...
Plan: 2 to add, 1 to change, 0 to destroy.
real 7m42.123s
Without refresh - same state
$ time terraform plan -refresh=false
...
Plan: 2 to add, 1 to change, 0 to destroy.
real 0m8.456s
```
That is nearly a 60x speedup for a state with 500 resources.
A structured view of the same measurement:
| Mode | Resources | Plan Output | Real Time |
|---|---|---|---|
| With refresh default | 500 | Plan: 2 to add, 1 to change, 0 to destroy | 7m42.123s |
| Without refresh -refresh=false | 500 | Plan: 2 to add, 1 to change, 0 to destroy | 0m8.456s |
The speed gain comes from removing 500 plus provider read calls. The plan result is identical when the state is known to be current.
When -refresh=false Is Safe
During Development Iteration
When you are editing Terraform code and want fast feedback on whether your changes are syntactically correct and produce the expected plan:
```
Fast iteration cycle
vim main.tf
terraform plan -refresh=false
Fix issues, repeat
vim main.tf
terraform plan -refresh=false
```
This is the primary use case.
It is safe for development iteration, PR validation, and any situation where you know the state is current.
When It Is Unsafe
The -refresh=false flag is one of the simplest ways to speed up Terraform plan times. It is safe for development iteration, PR validation, and any situation where you know the state is current. It is not safe for production applies, drift detection, or environments where others can modify infrastructure.
Refresh Strategies by Environment
| Environment | Refresh Strategy |
|---|---|
| Local development | -refresh=false most of the time, full refresh once per session |
| PR checks | -refresh=false for speed |
| Staging deploy | Full refresh before apply |
| Production deploy | Full refresh, always |
| Drift detection | Full refresh (the whole point) |
| Incident response | Full refresh to understand current state |
The best approach is a two-phase workflow: fast plans with -refresh=false during development and PR checks, full plans with refresh for actual deployments.
Common Workflow Patterns
Combining with Other Optimizations
Stack -refresh=false with other performance flags for maximum speed:
```
Maximum speed for development iteration
terraform plan \
-refresh=false \
-target=module.application \
-parallelism=30
Fast apply for a specific change (development only)
terraform apply \
-refresh=false \
-target=awslambdafunction.processor \
-auto-approve
```
CI/CD Strategy: Two-Phase Planning
Use a two-phase approach in CI/CD to get fast feedback and safe applies:
```
Phase 1: Fast check on pull request
pull-request-check:
steps:
- name: Quick Validation
run: |
terraform init
terraform validate
terraform plan -refresh=false -no-color
Phase 2: Full plan and apply on merge
deploy:
steps:
- name: Full Plan with Refresh
run: |
terraform init
terraform plan -out=deploy.plan
- name: Apply
run: terraform apply deploy.plan
```
Environment Variable Configuration
Set default behavior through environment variables:
```
For development environments
export TFCLIARGS_plan="-refresh=false"
This makes every terraform plan skip refresh by default
Override when needed:
terraform plan -refresh=true # Explicit
```
Known Issue With Data Sources
Running terraform plan --refresh=false should only output the terraform changes, if any. If there aren't any changes, it should output No changes. Your infrastructure still matches the configuration.
In practice, some versions still perform reads for data sources even with --refresh=false. For example:
data.terraform_remote_state.main: Reading...
module.get_image_tag.data.external.fetch_image_tag: Reading...
data.aws_caller_identity.current: Reading...
data.aws_region.current: Reading...
This behavior was reported after upgrading from terraform 1.0.6 where the issue did not occur. It was reproduced using GitHub Actions with terraform_version 1.3.6 and on macOS arm64 with the same result. The steps to reproduce were:
- terraform init
- terraform refresh
- terraform plan --refresh=false
The issue highlights that data sources may still be evaluated during plan even when refresh is disabled, which is distinct from resource refresh.
Refresh vs Import vs Refresh-Only
What is the difference between Terraform refresh and Terraform import?
Terraform’s refresh command updates the state file by checking the actual infrastructure to reflect any changes made outside Terraform, e.g., manually via the AWS Console. It doesn’t create or modify resources, only syncs the state with the real world.
Terraform’s import command is used to bring existing resources into Terraform’s management. It associates an existing infrastructure resource with a Terraform resource block, allowing Terraform to manage it going forward. It updates the state file but doesn’t modify the resource itself.
Refresh-only plan mode sits between the two: it performs a refresh without making changes, allowing a scheduled drift sync without an apply.
Conclusion
The -refresh=false flag removes the provider read phase from plan and apply, trading drift awareness for speed. For large states the reduction in plan time can be from minutes to seconds, nearly a 60x speedup in documented 500 resource examples.
The safe pattern is to keep refresh enabled for production deploys, staging deploys, drift detection, and incident response, and to use -refresh=false for fast iteration, PR checks, and local development where the state is known to be current. Pair fast plans with periodic -refresh-only runs to re-sync state on a schedule or before a work session.
When combined with -target, increased parallelism, and environment variable defaults, -refresh=false provides a practical development workflow while preserving full refresh guarantees for the moments when correctness matters most.