// DevOps

Как я усмирил Terraform: переезд со стейта в Git на Cloudflare R2

Опубликовано 10.04.2026

Когда я только начинал работать над инфраструктурой проекта, хранить terraform.tfstate локально (или, каюсь, подкидывать его в Git) казалось мне нормальным решением. Но как только к работе подключились коллеги, я быстро понял: локальный стейт — это пороховая бочка.

Один забыл сделать git pull, второй применил изменения со своей машины — и вот я уже трачу полдня на то, чтобы «разрулить» конфликты в инфраструктуре. В какой-то момент стало очевидно: мне нужен Remote State.

В качестве бэкенда я выбрал Cloudflare R2. Причина простая: это S3-совместимое хранилище без платы за исходящий трафик (zero egress fees), и на его настройку у меня ушло буквально несколько минут.


Почему я отказался от локального стейта

Главная проблема локального файла — отсутствие синхронизации и контроля.

Риск затереть чужие изменения

Если два человека запускают terraform apply одновременно, Terraform не предотвращает гонку без блокировок. В итоге побеждает тот, кто записал стейт последним.

Секреты в открытом виде

В terraform.tfstate могут лежать пароли, токены и ключи — зачастую в открытом виде. Держать это в Git — откровенно плохая идея.

Невозможность нормального CI/CD

Без удаленного состояния каждый запуск Terraform в CI видит «свой» мир, что делает автоматизацию бессмысленной.


Как я это настроил

Поскольку R2 поддерживает S3 API, я использовал стандартный backend s3. Но так как это не AWS, мне пришлось отключить ряд проверок Terraform.

Я вынес конфигурацию в отдельный файл backend.hcl, чтобы не захламлять основной код:

bucket                      = "my-infrastructure-state"
key                         = "prod/main.tfstate"
region                      = "auto"

# Важно для R2
use_path_style              = true

# Отключаю AWS-специфичные проверки
skip_credentials_validation = true
skip_metadata_api_check     = true
skip_region_validation      = true
skip_requesting_account_id  = true

# Иногда требуется в зависимости от версии Terraform
skip_s3_checksum            = true

endpoints = {
  s3 = "https://<YOUR_ACCOUNT_ID>.r2.cloudflarestorage.com"
}

В основном коде Terraform я оставил минимальный блок:

terraform {
  backend "s3" {}
}

Как я решал проблему блокировок (State Locking)

Классическая схема для S3 backend — использовать DynamoDB для блокировок. Но в случае с Cloudflare R2 у меня такой опции нет, потому что это не AWS.

Я попробовал использовать:

use_lockfile = true

Этот параметр появился в новых версиях Terraform (начиная с 1.10) и позволяет использовать файл в бакете как механизм блокировки.

Но на практике я понял важную вещь:

это не полноценная замена DynamoDB-locking.

Что это значит на практике

  • блокировка работает по принципу best-effort
  • нет гарантии полной атомарности
  • теоретически гонки всё ещё возможны

Как я с этим живу

Я не стал надеяться только на lockfile и ввёл для себя простые правила:

  • я не запускаю terraform apply параллельно с другими
  • стараюсь выносить apply в CI/CD
  • воспринимаю locking в R2 как дополнительную защиту, а не гарантию

Если бы у меня была критичная инфраструктура — я бы делал строгий CI-only pipeline с внешним механизмом блокировки.


Как я переезжал

Миграция заняла одну команду:

terraform init \
  -backend-config=backend.hcl \
  -migrate-state

Terraform сам:

  1. нашёл локальный terraform.tfstate
  2. запросил подтверждение
  3. перенёс его в R2

После этого я удалил локальный файл и добавил его в .gitignore.


Что я получил в итоге

Спокойствие

Теперь стейт не хранится на моей машине.

Я дополнительно включил versioning в R2-бакете, чтобы иметь возможность откатиться.


Командную работу

Даже без идеального locking-а:

  • у меня один источник правды
  • исчезли вопросы «у кого актуальный стейт»

Безопасность

  • секреты больше не лежат в Git
  • доступ регулируется через API-токены Cloudflare
  • можно ограничивать права

Практические выводы

Если я снова буду настраивать Terraform backend на R2, я обязательно:

  • включу versioning
  • разделю окружения (prod/stage)
  • не буду запускать apply с разных машин
  • по возможности вынесу apply в CI/CD

Заключение

Если я продолжаю хранить стейт локально или в Git — я сознательно создаю себе проблему.

Cloudflare R2 дал мне быстрый способ перейти на Remote State без AWS. Да, у него есть ограничения, особенно в части блокировок, но это всё равно огромный шаг вперед.

Для меня это тот случай, когда небольшое инфраструктурное изменение реально сэкономило время, нервы и избавило от класса проблем, которые раньше казались «нормальными».

// Reviews

Отзывы по теме

Было несколько проблем касаясь как технической части так и понимания в целом. Михаил быстро ответил на запрос, помог разобраться и решил проблеммы технические и помог разобраться в понимании, за что отдельное спасибо. Результатом доволен.

Было несколько проблем касаясь как технической части так и понимания в целом. Михаил быстро ответил на запрос, помог разобраться и решил проблеммы технические и помог разобраться в понимании, за что отдельное спасибо. …

abazawolf

Настройка vps, настройка сервера

18.02.2026 · ★ 5/5

// Contact

Нужна помощь?

Свяжись со мной и я помогу решить проблему

Отправить заявку
Написать и получить быстрый ответ