linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
Intro
Lessons
Footer
linuxlab-УчебникиЦеныО платформеКонфиденциальность и куки
Copyright © 2026 LinuxLab. Все права защищены.
linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
  • Введение
  • Уроки
  • How it works
  • База знаний
  • Шпаргалка
  • Capstone
  • Собеседование
home/terraform/lessons/tf-intermediate-06-remote-backend

lesson ── terraform-intermediate ── ~18 мин ── 5 шагов

Remote state в S3 (на LocalStack)

До сих пор state жил в terraform.tfstate рядом с HCL. Это работает пока ты один. Команда из двух человек = два state'а, разъезжаются на первой же неделе.

Сейчас сделаешь boilerplate setup: один root создаёт S3-бакет под state и DynamoDB-таблицу под lock. Второй root использует их как remote backend. Это типовой layout: bootstrap отдельно от приложения.

▶ интерактивный sandbox

Поднимется пара контейнеров: terraform 1.9 и localstack 3.8 в одной сети. В браузере откроется терминал, можно сразу terraform init. Каждый шаг проверяется автоматически. TTL 45 минут, без регистрации.

запустить sandbox →

stack ── terraform · localstack · 1 GB RAM · самоуничтожается через 45 мин простоя

Шаги

  1. 01

    Bootstrap: HCL для бакета и таблицы

    Создай файлы в /home/student/tf-backend/bootstrap/:

    main.tf:

    hcl
    resource "aws_s3_bucket" "state" {
      bucket = "linuxlab-tfstate"
    }
    resource "aws_s3_bucket_versioning" "state" {
      bucket = aws_s3_bucket.state.id
      versioning_configuration {
        status = "Enabled"
      }
    }
    resource "aws_dynamodb_table" "lock" {
      name         = "linuxlab-tflocks"
      billing_mode = "PAY_PER_REQUEST"
      hash_key     = "LockID"
      attribute {
        name = "LockID"
        type = "S"
      }
    }

    Это отдельный root. Его state локальный (по умолчанию). Бакет и таблица, единственные ресурсы, они существуют чтобы держать state остальных root'ов.

    Версионирование S3 обязательно: спасает при случайном перезаписе state'а. См. tf-remote-backend-s3.

    ✓ Bootstrap описан. Init + apply создадут backend-инфру.

  2. 02

    Apply bootstrap

    bash
    cd /home/student/tf-backend/bootstrap
    terraform init
    terraform apply -auto-approve

    После apply:

    bash
    aws --endpoint-url=http://localstack:4566 s3 ls
    aws --endpoint-url=http://localstack:4566 dynamodb list-tables

    Видишь linuxlab-tfstate и linuxlab-tflocks. Готово к использованию.

    ✓ Backend-инфра создана. Теперь app-root укажет на неё.

  3. 03

    App-root с backend конфигурацией

    В /home/student/tf-backend/app/ создай main.tf:

    hcl
    terraform {
      backend "s3" {
        bucket         = "linuxlab-tfstate"
        key            = "app/terraform.tfstate"
        region         = "us-east-1"
        dynamodb_table = "linuxlab-tflocks"
        # Для LocalStack
        endpoints = {
          s3       = "http://localstack:4566"
          dynamodb = "http://localstack:4566"
        }
        skip_credentials_validation = true
        skip_metadata_api_check     = true
        skip_requesting_account_id  = true
        use_path_style              = true
        encrypt                     = false
      }
    }
    resource "random_id" "suffix" {
      byte_length = 4
    }
    resource "aws_s3_bucket" "demo" {
      bucket = "linuxlab-app-${random_id.suffix.hex}"
    }

    Заметь:

    • backend "s3", внутри блока terraform { }.
    • key = "app/terraform.tfstate", путь внутри бакета. Конвенция: <проект>/<env>/terraform.tfstate.
    • endpoints. Нужно только для LocalStack. В реальном AWS убираешь.
    • encrypt = false, для LocalStack. В реальном AWS, true, желательно с KMS.

    Параметры backend нельзя интерполировать через var.*. Это статика или partial-backend.

    ✓ App-root описан с S3-backend. Init подхватит.

  4. 04

    Init app: state идёт в S3

    bash
    cd /home/student/tf-backend/app
    terraform init

    Вывод покажет:

    Initializing the backend...
    Successfully configured the backend "s3"! Terraform will automatically
    use this backend unless the backend configuration changes.

    Локального файла terraform.tfstate не будет. State идёт в S3. Проверь:

    bash
    ls -la /home/student/tf-backend/app/
    aws --endpoint-url=http://localstack:4566 s3 ls s3://linuxlab-tfstate/

    В директории, нет terraform.tfstate. В бакете, пока пусто (ещё apply не был).

    ✓ App видит S3-backend. State будет писаться в бакет.

    То же самое на OpenTofu

    OpenTofu держит CLI и state совместимыми с Terraform по командам этого шага: миграция обычно проходит через mv .terraform .terraform.bak; tofu init -upgrade. Но при первом переходе сделай backup state и прогон на feature-branch - расхождения концентрируются в новых фичах (variables в backend, state-encryption, OCI registry-backed модули). См. tf-opentofu-parity для полной матрицы.

    • → OpenTofu parity
  5. 05

    Apply: state появляется в S3

    bash
    terraform apply -auto-approve

    После apply, посмотри что в бакете:

    bash
    aws --endpoint-url=http://localstack:4566 s3 ls s3://linuxlab-tfstate/app/

    Увидишь terraform.tfstate. Скачать и посмотреть:

    bash
    aws --endpoint-url=http://localstack:4566 s3 cp s3://linuxlab-tfstate/app/terraform.tfstate /tmp/state.json
    cat /tmp/state.json | jq '.serial, .lineage'

    Это и есть твой state. Если коллега в другом терминале сделает terraform init с такими же backend параметрами, получит тот же state.

    Теперь проверим lock через DynamoDB:

    bash
    aws --endpoint-url=http://localstack:4566 dynamodb scan --table-name linuxlab-tflocks

    После apply записей нет (lock отпущен). Если параллельно с твоим apply кто-то попытается, увидит lock и упадёт с ошибкой Error acquiring the state lock. См. tf-common-errors.

    ✓ State живёт в S3. Готова работа в команде, готов CI/CD.

    Partial backend и multi-env

    Все 4-5 параметров backend дублируются между prod/stage/dev? Это плохо: правка одного параметра. N PR-ов.

    Решение, partial backend:

    hcl
    terraform {
      backend "s3" {}   # пустой блок
    }

    А параметры передавай при init:

    bash
    # backends/prod.hcl
    bucket         = "linuxlab-tfstate"
    key            = "app/prod/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "linuxlab-tflocks"
    # backends/dev.hcl
    bucket         = "linuxlab-tfstate"
    key            = "app/dev/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "linuxlab-tflocks"
    bash
    terraform init -backend-config=backends/prod.hcl
    # или
    terraform init -backend-config=backends/dev.hcl

    Один HCL. N окружений. Это типичный multi-env layout. Подробно в уроке 12.

    • → Remote state в S3
    • → Init и backends

Что ты узнал

S3-backend держит state-файл в бакете, DynamoDB, lock-запись. Конфигурируется в terraform { backend "s3" { ... } }. Backend нельзя интерполировать, параметры или статика, или через -backend-config при init.

команды

  • terraform init -migrate-stateпереезд со старого backend на новый
  • terraform init -backend-config=prod.hclpartial backend, параметры из файла
  • aws --endpoint-url=$LS s3 ls s3://BUCKET/посмотреть state-файлы в бакете

концепции

  • · Bootstrap (бакет+таблица): отдельный root, создаётся один раз
  • · App-root конфигурирует backend, его state живёт в созданном бакете
  • · lock работает только если все ходят через terraform CLI

← предыдущий

Destroy: безопасно сносим инфру

следующий →

OPA + Rego, гейтим plan.json

Footer
linuxlab-
Copyright © 2026 LinuxLab. Все права защищены.
Учебники
Цены
О платформе
Конфиденциальность и куки