id: BB-PLAN-_K_EGSMU
type: plan
title: Migrate OpenTofu State to Cloudflare R2
spec: BB-SPEC-OA12Z7YV
status: approved
Migrate OpenTofu State to Cloudflare R2
Outcome
The root OpenTofu configuration uses one private R2 object as authoritative state.
Credentials remain operator-supplied.
The migration changes no managed infrastructure.
Changes
Cloudflare bootstrap
Create a dedicated private R2 bucket outside this root state.
Disable public access.
Create a bucket-scoped credential limited to required object operations.
Choose one stable state key and record bucket recovery ownership.
Retain an independent protected state backup outside R2.
Root backend
Add a root S3 backend configured for the R2 endpoint, bucket, key, and compatibility requirements.
Enable backend-native lockfile coordination.
Keep all non-secret backend identity in version control.
Supply access credentials only through the backend's environment interface.
Add no Cloudflare provider because backend bootstrap must remain independent of stored state.
Operator workflow
Document backend initialization, credential variables, migration, normal operation, recovery, and credential rotation in README.md.
Preserve just plan and just apply; initialized OpenTofu routes both through R2.
State explicitly that saved plans contain backend metadata and remain secret material.
Keep local state and migration backups ignored by Git.
Migration
Prove R2 read, write, delete, and lock behavior against a disposable key.
Stop all other OpenTofu operations.
Capture the current resource-address set, outputs, lineage, serial, and a pre-migration plan baseline without publishing state content.
Create and verify an independent protected copy of the local state.
Initialize the R2 backend with state migration enabled.
Confirm the remote state identity and resource-address set match the baseline.
Compare the post-migration plan with the baseline and reject backend-induced infrastructure changes.
Verify normal saved-plan apply uses R2, without applying infrastructure during migration acceptance.
Remove local authoritative state only after recovery verification.
Verification
Initialize from a clean checkout using only operator-supplied credentials.
Confirm unauthenticated state access fails.
Hold one state lock and confirm a second mutation is rejected.
Confirm backend unavailability fails closed without creating authoritative local state.
Restore the independent backup to a disposable recovery key and validate it with OpenTofu.
Run tofu fmt -check -recursive and tofu validate.
Review a saved tofu plan showing no backend-induced changes; do not apply it as part of migration verification.
Rollback
Before remote acceptance, reinitialize the local backend from the protected backup.
After remote acceptance, recover into R2 from the protected backup; do not resume local authoritative operation.
---
id: BB-PLAN-_K_EGSMU
type: plan
title: Migrate OpenTofu State to Cloudflare R2
spec: BB-SPEC-OA12Z7YV
status: approved
---
# Migrate OpenTofu State to Cloudflare R2
## Outcome
The root OpenTofu configuration uses one private R2 object as authoritative state.
Credentials remain operator-supplied.
The migration changes no managed infrastructure.
## Changes
### Cloudflare bootstrap
- Create a dedicated private R2 bucket outside this root state.
- Disable public access.
- Create a bucket-scoped credential limited to required object operations.
- Choose one stable state key and record bucket recovery ownership.
- Retain an independent protected state backup outside R2.
### Root backend
- Add a root S3 backend configured for the R2 endpoint, bucket, key, and compatibility requirements.
- Enable backend-native lockfile coordination.
- Keep all non-secret backend identity in version control.
- Supply access credentials only through the backend's environment interface.
- Add no Cloudflare provider because backend bootstrap must remain independent of stored state.
### Operator workflow
- Document backend initialization, credential variables, migration, normal operation, recovery, and credential rotation in `README.md`.
- Preserve `just plan` and `just apply`; initialized OpenTofu routes both through R2.
- State explicitly that saved plans contain backend metadata and remain secret material.
- Keep local state and migration backups ignored by Git.
## Migration
1. Prove R2 read, write, delete, and lock behavior against a disposable key.
2. Stop all other OpenTofu operations.
3. Capture the current resource-address set, outputs, lineage, serial, and a pre-migration plan baseline without publishing state content.
4. Create and verify an independent protected copy of the local state.
5. Initialize the R2 backend with state migration enabled.
6. Confirm the remote state identity and resource-address set match the baseline.
7. Compare the post-migration plan with the baseline and reject backend-induced infrastructure changes.
8. Verify normal saved-plan apply uses R2, without applying infrastructure during migration acceptance.
9. Remove local authoritative state only after recovery verification.
## Verification
- Initialize from a clean checkout using only operator-supplied credentials.
- Confirm unauthenticated state access fails.
- Hold one state lock and confirm a second mutation is rejected.
- Confirm backend unavailability fails closed without creating authoritative local state.
- Restore the independent backup to a disposable recovery key and validate it with OpenTofu.
- Run `tofu fmt -check -recursive` and `tofu validate`.
- Review a saved `tofu plan` showing no backend-induced changes; do not apply it as part of migration verification.
## Rollback
Before remote acceptance, reinitialize the local backend from the protected backup.
After remote acceptance, recover into R2 from the protected backup; do not resume local authoritative operation.