How a team moves from Terraform to OpenTofu without rewriting its code
A licensing change, a renewal or a data residency requirement has pushed your team toward OpenTofu, and the good news is that the configuration language is largely shared between the two CLIs. The migration work sits elsewhere: in the state and its backend, in the CI jobs that call the binary, and in any stack that reads another stack's outputs. Platform engineering usually owns that work, with security review on the state location, access to the backend and any encryption settings. OpenTofu describes itself as a drop-in replacement for Terraform that preserves existing workflows and configurations, but its FAQ aims for feature parity with Terraform only for future releases while its community decides which features to adopt. Treat compatibility with future Terraform releases as something to verify at each upgrade, not something to assume.
The effort depends on your inventory more than on the code. Count the root modules, the backends they use, the workspaces or state files behind each one, and how many pipelines run terraform plan or terraform apply directly. Each of those is a place where a binary swap, a version pin or a credential change can break a run. Also list every terraform_remote_state consumer, because OpenTofu's migration guidance warns that these configurations typically need extra care. The migration overview describes a process of backing up state and code, installing OpenTofu, initializing and verifying the configuration, and testing with a small change, designed to be safe and reversible. Keep that reversibility by retaining the last Terraform-written state backup until a no-change plan has been reviewed. Because the scope depends on your repositories, backends and dependent stacks, ask for a written scope that lists those items before any work begins.
Which Terraform features map to OpenTofu, and where the work sits
This table compares the Terraform features most teams depend on with their OpenTofu equivalents and marks each row as clean, needs rework or a gap. Read the verdict column first, then check the notes for the areas where your configurations, state backend or CI pipeline will need changes. Most rows should carry over without edits, but provider behavior and remote state sharing still need confirmation in your own environment.
| Capability | Status on OpenTofu | What to confirm or change |
|---|---|---|
| HCL configurations and variables | Clean | Existing configurations run unchanged; early evaluation of variables and locals arrived in v1.8, so review any dependence on evaluation order. |
| Providers and modules | Clean | The OpenTofu Registry lists more than 3,900 providers and 23,600 modules; confirm each provider version resolves from the registry you use. |
| Provider for_each and -exclude flag | Needs rework | Added in v1.9; pipelines and scripts that rely on these features need a minimum OpenTofu version pinned. |
| Remote state backends | Clean | Re-initialize against the same backend, then run a no-change plan to confirm the state is read correctly. |
| terraform_remote_state data source | Needs rework | Consumer stacks read producer state directly, so migrate producers and consumers in a planned order and verify each dependency. |
| State and plan handling | Needs rework | Back up state before switching; state and plan encryption from v1.7 is optional, but tools reading state must be checked. |
| CI invocation and pull request automation | Needs rework | Replace the terraform binary in pipelines, Atlantis configuration or Terrakube runs, and confirm the cloud block or remote backend accepts the tofu CLI. |
| Terraform features after the 1.6.0 license change | Gap | OpenTofu aims for feature parity while the community chooses features, so future Terraform releases are not guaranteed to match. |
What state, backends and dependent stacks must carry across
A Terraform to OpenTofu move keeps the same state, but the surrounding platform state does not come along for free. Backends, workspace names, variables, VCS links and downstream stacks all need an owner and a check before the first run. The cards below cover what stays in place, what must be exported and what other teams depend on.
State stays in its existing backend
The state file is not re-created during the move. OpenTofu reads the same state that Terraform wrote, so the backend (S3, GCS, Azure Blob, a database or a Terrakube-managed store) should be kept where it is. Confirm the backend type, bucket or path, and locking settings in each root module before changing the CLI. Changing the backend at the same time as the CLI makes a failed migration much harder to unwind.
Remote backend and cloud block settings
Configurations that use the cloud block or the remote backend point at HCP Terraform hostnames and organization names. These must be rewritten to the self-hosted endpoint, such as a Terrakube API hostname, before the first run. Terrakube documents that the cloud block needs Terraform v1.1 or later and that older versions can use the remote backend. Confirm with the platform team which hostname and organization each stack should use.
Workspace names and variable sets
HCP Terraform workspaces, their names, tags and variable sets do not transfer automatically to a self-hosted platform. Export the list of workspaces, the variables marked sensitive, and any shared variable sets, then recreate them in the new platform with matching names so backend keys and tfvars files still resolve. Sensitive values usually have to be re-entered from the secrets owner, so ask for that list early.
VCS connections and run triggers
Source-driven runs depend on VCS connections, branch rules and webhook settings held by the source platform. Terrakube connects to GitHub, GitLab, Bitbucket and Azure DevOps, while Atlantis needs its own webhook and repository permissions. Record which repositories trigger which workspaces, who approves applies, and which run triggers chain one stack to another, then rebuild each link and test it on a non-production stack.
Exporting state from HCP Terraform
The HCP Terraform state versions API exposes each workspace's current state, including a JSON download URL for states created by Terraform 1.3 or later. Use that export as a backup, and confirm it is complete and that its serial and lineage match the live state before any change. For states written by earlier versions, or for other source platforms, confirm the export path with the vendor and keep the export encrypted at rest.
Stacks that read terraform_remote_state
Downstream stacks that read outputs with the terraform_remote_state data source depend on the upstream state location and its output names. Migrating the upstream stack without checking these readers can break network, IAM or DNS deployments that never changed. List every consumer, confirm the backend and key they read, and move the consumers in the same change window. OpenTofu's migration guide flags this case as needing extra care.
The migration sequence from inventory to decommission
This sequence moves a Terraform estate to OpenTofu one workspace at a time, with a recorded rollback point and a plan that proves nothing changed before any real change is made. Each step leaves a written artifact that the next step depends on, so the team can stop, reverse or extend the move at any point.
Inventory workspaces, backends and consumers
The platform or infrastructure lead lists every root module, its Terraform version, its state backend, its providers and every workspace that reads its outputs. Pay particular attention to terraform_remote_state data sources, which create dependencies that a single-repo view will miss. The step is done when the team has a spreadsheet or repository file listing each configuration, its current version and its downstream consumers, and the owners of each consumer have been named.
Pilot on one low-risk configuration
Choose one configuration with a non-production backend, few dependencies and a well-understood provider set, then run the full sequence on it before touching anything else. The pilot exposes problems with provider versions, backend credentials and CI images while the blast radius is small. The step is done when the pilot team has written down the exact commands and any surprises, and has agreed which findings change the runbook for the rest of the estate.
Back up state and code
Before any change, the operator pulls a copy of the current state for each workspace and stores it outside the working directory, in storage with restricted access. For HCP Terraform workspaces, the state can be downloaded through the state versions API, using the JSON state URL for states created by Terraform 1.3 or later. Record a checksum for each backup file and tag the code repository at the commit that matches the state. The step is done when every backup has a recorded checksum, the tagged commit exists, and someone other than the operator has confirmed the files can be opened.
Install OpenTofu and pin the version
The platform engineer installs the tofu binary on developer workstations and CI runners, and pins the exact version in the repository, because OpenTofu's feature set and provider behavior depend on the release. Confirm the version against the OpenTofu releases page before pinning it. The step is done when
tofu versionreturns the pinned version on every machine that runs plans, and the pin appears in the repository where the team can review it.Initialize against the same backend
The operator runs
tofu initin each configuration, pointing at the same backend and credentials the Terraform run used, so that the existing state is read rather than replaced. Check the initialization output for backend migration prompts and stop if the backend location or workspace name differs from the inventory. The step is done when initialization succeeds, the state location matches the backup record, and any lock file changes are reviewed and committed.Verify with a no-change plan
Run
tofu planagainst the unchanged configuration and confirm that the plan reports no resource changes. A plan that proposes changes means the code, variables, providers or state have drifted, so the team investigates before proceeding. Store the plan output with the migration record. The step is done when the plan shows no changes for every workspace in the pilot and the output is attached to the migration record.Test with a small, reversible change
Make one minor, reversible change to a non-critical resource, such as a tag, and run it through the normal review and apply path with OpenTofu. Confirm that the apply output and the resulting state match expectations, and that the consuming configurations still read the outputs they need. The step is done when the change has been applied, the consumer checks have passed, and the team has signed off on the result in writing.
Decommission Terraform and record sign-off
After every workspace in scope has passed its no-change plan and test change, update CI pipelines, remove Terraform binaries from runners and retire the old Terraform version pins. Keep the state backups under retention rules set by the organization's own policy, and close the inventory with a list of consumers who have confirmed their dependencies work. The step is done when no pipeline calls the terraform binary, the inventory shows every consumer signed off, and the backup retention decision is recorded.
Running Terraform and OpenTofu in parallel during cutover
Keep production on Terraform until a copy of the configuration has passed the cutover checks. Point the OpenTofu CLI at a copy of the state or a pilot workspace, using the same backend type and a separate state key so that nothing written by tofu can reach the production state. Run terraform plan and tofu plan against the same code and variables side by side. The no-change plan is the gate: if tofu plan shows any create, update or destroy that terraform plan does not, stop and find out why before anything else runs. Confirm with your own team which provider versions and backend settings the pilot uses, since a mismatch there produces false differences.
Pin the OpenTofu version per repository rather than relying on whatever binary a runner has installed. Record the version in the repository, for example in a required_version constraint in the terraform block and in your CI image or tool manifest, so that every engineer and every pipeline runs the same binary. Keep the Terraform binary available for at least as long as the pilot runs, because the migration guide describes the process as designed to be safe and reversible: as long as state has not been changed by an OpenTofu apply that the Terraform CLI cannot read, you can return to Terraform by re-initializing against the original backend. Verify that rollback path in your pilot before you rely on it.
Freeze applies on the migrating stacks for the switch window. Two tools writing to the same state invite lock conflicts and drift, so agree with your change managers that no Terraform or OpenTofu apply runs against those workspaces until the production switch is complete and the no-change plan is clean. Ask whoever owns the pipeline whether scheduled jobs, drift detection and pull request automation can be paused for those stacks. The length of the freeze depends on how many stacks you have and how much review each one needs, so set it against your own inventory rather than a published figure.
What drives the effort and how to keep it down
This table lists the factors that change the effort of moving from Terraform to OpenTofu and Terrakube or Atlantis, with what reduces each one. Read it before asking for a quote, because most of the cost sits in inventory, CI rework and state dependencies rather than in installing the tofu binary.
| Driver | Why it changes effort | How to keep it down |
|---|---|---|
| Number of workspaces and stacks | Each state file needs its own backup, verification plan and backend re-initialization, so effort grows with the count of workspaces and their owners. | Inventory every workspace first, group them by backend and team, and migrate low-risk stacks in batches. |
| Dependence on terraform_remote_state | Stacks that read outputs from other states must move in a sequence, and a change in one output can break consumers that were not migrated yet. | Map the producer and consumer graph before the pilot and migrate producers before consumers, following OpenTofu's migration guidance on this data source. |
| Custom and in-house providers | Providers built for Terraform must be checked against OpenTofu's provider handling, and private registry or mirror setups need configuration changes. | Confirm each provider source address and version constraint in the pilot, and keep a list of private providers to test explicitly. |
| CI pipeline complexity | Pipelines that hard-code the terraform binary, pin versions or call wrappers need edits in each job, and Atlantis setups need a custom workflow that runs tofu. | Centralize the binary name and version in one variable, and rework a single representative pipeline before copying the pattern. |
| State encryption key providers | Enabling client-side encryption adds a key provider such as AWS KMS, GCP KMS, OpenBao or Azure Vault, and every reader of the state must have access to the key. | Decide the key provider and rotation process before the cutover, and test that each tool reading state can decrypt it. |
| Approval controls and audit trail | Moving from a hosted approval flow to Atlantis locks, Terrakube permission groups or pull request reviews means redesigning who can plan and apply. | Map each existing approval to a group or review rule, then record evidence of the first approved apply for assessors. |
| Hosting and upgrades (recurring) | The run platform, its database and the OpenTofu and Atlantis or Terrakube versions must be patched and upgraded over time. | Ask the quote to separate the one-time setup from the recurring patch, upgrade and support work, and to state which versions are covered. |
What usually goes wrong and how to prevent it
Most failed Terraform to OpenTofu migrations do not fail in the CLI itself. They fail at the edges: the state backup, the first plan, dependent stacks, pinned versions, encryption keys and the CI jobs that still call the old binary. Each card below names one of these failure points and the check that catches it before it reaches production.
Applying from an unbacked state
Running tofu init against a backend that has no independent copy of the state leaves no way back if the migration goes wrong. For HCP Terraform workspaces, download the current state version as JSON through the state versions API before touching code, and store it outside the workspace being migrated. Keep a copy of the working directory and the lock file at the same commit. Confirm that a restore from the backup has been tested on a non-production workspace before the first real run.
Unexpected drift after the binary change
A plan that proposes changes right after the switch is often a sign of provider or version mismatch, not real infrastructure drift. Before applying anything, run tofu plan on an unchanged configuration and treat any create, update or destroy as a blocker until it is explained. Compare provider versions in the lock file against what was in use before. The OpenTofu migration guide describes the verification step as a no-change plan, so make that the gate for every workspace.
Silent breakage in terraform_remote_state consumers
Stacks that read outputs through terraform_remote_state can fail or read stale values once the producing stack moves to a new backend or workspace name, and the consuming stack may not show an error until its next plan. List every consumer before migrating any producer, using code search across all repositories. Migrate producers and consumers in a planned order and run a no-change plan on each consumer after its dependency moves. OpenTofu's migration overview flags this case as needing extra care.
Provider version drift across stacks
Teams often migrate one repository at a time, and different stacks can end up initialized with different OpenTofu versions and different provider builds. This makes plans hard to compare and can introduce behavior changes that look like infrastructure changes. Pin the tofu version per repository in version files or the CI image, and commit the dependency lock file. Track which OpenTofu release each stack last applied with, and review provider upgrades as separate changes from the CLI switch.
Encryption enabled without key recovery
Enabling state encryption during the migration means that the state file is unreadable without the configured key. If the key provider is misconfigured, rotated without a record, or its access policy changes, the team can lose the ability to plan or apply. Before enabling encryption, document the key provider, key identifier and the principals allowed to decrypt. Test a decrypt on a copy of the state in a separate account or environment, and keep a recovery path, such as a documented break-glass role, that does not depend on the workflow being repaired.
CI runners still calling terraform
Pipelines, pre-commit hooks, Atlantis workflows and wrapper scripts often hard-code the terraform binary. After the switch, some jobs may keep running Terraform while others run OpenTofu, which produces state and lock differences between them. Search every pipeline definition and script for terraform and TF_ variables, replace the binary explicitly with tofu, and remove any cached Terraform binary from runner images. Atlantis users need to set the binary per workflow and disable automatic downloads so the wrong version is never fetched.
How the migration differs by starting point
Teams that keep state in an object store or database through the backend block, with a handful of workspaces, usually have the shortest path. The state already lives in your own infrastructure, so the main change is the binary and the CI job that calls it. You can often migrate one workspace, verify it, and then repeat the pattern for the rest. Because the backend is unchanged, re-initialization against the same location is the step to check most carefully.
- Start with the least critical workspace: Use it to prove the backup, install, init and no-change plan sequence before touching shared stacks.
- Keep the backend identical: Confirm the same bucket, key and locking table or equivalent are used after
tofu init, so the plan reads the existing state. - Pin the version you are leaving: Record the exact Terraform version in use so a rollback is possible if verification fails.
- Update CI in the same change: Swap the binary in the pipeline for the affected workspace only, then extend to others.
- Decide on state encryption early: If you plan to enable it, treat it as a separate, deliberate change after the no-change plan passes.
Before you cut over each workspace, confirm the no-change plan on the OpenTofu migration overview, including the Terraform version you should move from.
Teams using the cloud block with HCP Terraform keep state, variables and runs on HashiCorp's side. Moving means exporting each workspace's state, standing up a self-hosted run and state platform, and then moving run triggers. Many workspaces with VCS-driven runs add ordering constraints, because a pull request merge can start an apply the moment the new platform is not ready. Plan the cutover as a freeze window per workspace group rather than one global switch.
- Inventory before exporting: List workspaces, variable sets, VCS connections, run triggers and teams. Each one needs an owner in the new platform.
- Export state through the state versions API: Download state for each workspace and store it encrypted in the target backend, so the new platform reads a known state.
- Disable VCS triggers on the old side: Stop HCP Terraform from applying on merge before the new pipeline is live, to avoid two systems changing the same resources.
- Choose the run platform per group: Terrakube fits teams that want a
cloudor remote backend workflow, while Atlantis fits pull request driven plans and applies. - Migrate in waves: Move low-risk groups first and keep a rollback path open for each wave.
Before starting, confirm with the OpenTofu migration overview which Terraform version your workspaces run, because the cloud block and state export behave differently across versions.
Configurations that read outputs from other stacks through terraform_remote_state form a dependency graph. Migrating one stack changes the state location or the tool that writes it, so downstream stacks may read stale or unexpected values. The OpenTofu migration overview warns that these configurations need additional care and links a separate guide. Map the chain first, then migrate from the leaves that depend on nothing toward the roots, or from roots outward, depending on how outputs are consumed.
- Draw the dependency graph: Record which stack produces each output and which stacks read it, including any data sources in modules.
- Freeze producers during their migration: Avoid applying an upstream stack while its consumers are being moved, so outputs stay consistent.
- Verify outputs, not only plans: After each stack moves, compare the output values consumed downstream with their pre-migration values.
- Keep backend access consistent: Confirm that the reading stacks can still reach the state location after the writer changes.
- Set one owner per chain: A single team should own the sequence so cutover decisions are not split across groups.
Before you move any stack in the chain, read the separate terraform_remote_state guide linked from the OpenTofu migration overview and confirm your Terraform version.
Questions teams ask before moving from Terraform to OpenTofu
How much does a migration from Terraform to OpenTofu cost?
The CLI swap itself is licensed at no charge, because OpenTofu is MPL 2.0 software, so the cost sits in the work around it. Expect to pay for the people who inventory workspaces, back up state, re-initialize backends, rework CI jobs and verify plans, plus the platform that runs them if you move off HCP Terraform. Costs go up with the number of workspaces, the amount of custom CI, the number of teams that depend on terraform_remote_state, and the compliance evidence you need. Ask for a quote that lists each phase, the environments covered, what is one-time and what recurs (platform hosting, upgrades, support retainers), and who performs each task. Quotes follow from a written scope.
How long does it take to move from Terraform to OpenTofu?
Duration depends on how many configurations, state backends and pipelines you have, and on how many shared outputs other teams read. A small estate with one backend and a few pipelines is far simpler than a multi-account estate where changes must pass change advisory review. The steps are a backup and inventory, installing tofu, re-initializing against the same backend, a no-change plan for each root module, and then a controlled change. Ask the scoping conversation to produce a timeline by environment, including freeze windows, approval gates and the time reserved for rollback rehearsal.
Can our in-house team migrate to OpenTofu without outside help?
Many teams can, provided someone owns state, CI and the dependency map. OpenTofu's migration guidance describes a process designed to be safe and reversible, so the skills needed are mostly those you already use for Terraform. Outside help tends to matter where state is large, where CI runs many environments in parallel, or where cross-team dependencies are undocumented. Ask yourself who can restore state from a backup, who can review a plan that must show no changes, and who will own the pipeline after handover. If those answers are unclear, a fixed-scope engagement can cover the gaps, and an independent review can confirm the result.
Will OpenTofu stay compatible with future Terraform releases?
Compatibility is not guaranteed. OpenTofu's FAQ states an aim of maintaining feature parity with Terraform for future releases, but the community decides which features OpenTofu adopts, so the two will diverge in some areas. Configurations and providers in use today are the practical concern, and they continue to work with OpenTofu. Plan for divergence by pinning versions, testing each upgrade in a non-production root module first, and tracking the release notes of both tools. Confirm with your own module and provider list which features you depend on before committing.
Does our state move to OpenTofu, or does it have to be rebuilt?
State normally stays where it is. OpenTofu re-initializes against the same backend, so an S3, Azure Blob or Google Cloud Storage state file, or a remote backend, is read in place. If state lives in HCP Terraform, you download it through the state versions API, which provides a JSON state download for states created by Terraform 1.3 or later, then point the new backend at a location you control. Rebuilding state by importing resources is a last resort, because it is slow and error-prone. Take a backup of every state file before any change, and confirm that a no-change plan passes afterward.
Do we need Terrakube or Atlantis after moving to OpenTofu?
It depends on how your teams run changes today. OpenTofu is only the CLI, so something must still run plans, hold state access and control applies. Atlantis fits teams whose changes flow through pull requests, since it runs terraform plan on a pull request, comments the output, and applies on command with directory locks. Terrakube fits teams that need a central run and state platform, a private registry, team-based permissions and the remote backend or cloud block for CLI-driven runs. Both are Apache 2.0 and both document OpenTofu. Choose after mapping your current HCP Terraform features to these tools.
Which Terraform version should we move from before switching to OpenTofu?
Start from the version your configurations already run cleanly on, and avoid carrying an unresolved upgrade into the move. Terraform 1.6.0 and later are under the Business Source License, which matters for licensing review, while older releases were under the MPL. Check that your state was written by a version OpenTofu can read, and confirm the provider versions you pin are supported. Test the move in a non-production environment first. Confirm the exact compatibility matrix against the OpenTofu migration documentation and your own state files before choosing the starting point.
How DeployOpen scopes and runs a Terraform to OpenTofu migration
DeployOpen plans and implements the move from Terraform to OpenTofu inside your own cloud accounts, data centers or air-gapped networks. Engagements are fixed-scope, with the scope, deliverables and timeline agreed in writing before work starts.
How we scope
- Inventory every root module, backend, workspace and Terraform version in use before agreeing the scope.
- Identify terraform_remote_state dependencies between configurations and name the teams that own each one.
- Agree in writing whether the target is OpenTofu alone or OpenTofu with Terrakube or Atlantis for runs and state.
How we build
- Back up state and code, then rehearse the backend re-initialization against a copy of a non-production workspace.
- Verify each configuration with a no-change plan and record the output as migration evidence.
- Update CI pipelines, pull request workflows and encryption settings, with state encryption configured where your scope requires it.
How we hand over
- Document the rollback path for every workspace, including how to restore the prior state version.
- Provide runbooks for routine plans, applies, lock handling and CLI authentication for your engineers.
- Deliver a written record of what moved, what was tested and what remains on Terraform, if anything.
How we support
- Ongoing support, upgrades of OpenTofu and the run platform, and continuous testing are available as retainers.
- Confirm with your own security and platform teams which upgrade windows and change approvals apply to your environment.
- Ask any quote to separate one-time migration work from recurring retainer and hosting effort.

