Skip to main content

Terraform S3 backend with Garage

·187 words·1 min

Using Terraform’s S3 backend with Garage, a self-hosted S3-compatible object store.

AWS CLI profile
#

Configure a named AWS profile pointing to the Garage endpoint. Use credential_process to avoid storing secrets on disk — see AWS CLI for details.

# ~/.aws/config
[profile terraform]
services = terraform
credential_process = sh -c 'echo "{\"Version\":1,\"AccessKeyId\":\"$AWS_TF_ACCESS_KEY_ID\",\"SecretAccessKey\":\"$AWS_TF_SECRET_ACCESS_KEY\",\"SessionToken\":\"\"}"'

[services terraform]
s3 =
  endpoint_url = https://garage.example.com

Backend configuration
#

terraform {
  backend "s3" {
    profile = "terraform"
    bucket  = "tf-bucket"

    # Garage uses path-style URLs (`host/bucket/key`) not virtual-hosted
    use_path_style = true

    # Native S3 locking (Terraform 1.10+), no DynamoDB needed
    use_lockfile = true

    key = "project/env/component/terraform.tfstate"

    # Skips AWS STS `GetCallerIdentity`, unavailable on Garage 
    skip_credentials_validation = true
    # Skips AWS account ID lookup, not applicable to self-hosted S3
    skip_requesting_account_id  = true
  }
}

Reading remote state from another module
#

Use terraform_remote_state to consume outputs from a different root module. The config block mirrors the backend configuration of the target module.

data "terraform_remote_state" "example" {
  backend = "s3"
  config = {
    profile        = "terraform"
    bucket         = "tf-bucket"
    use_path_style = true
    use_lockfile   = true
    key            = "project/env/component/terraform.tfstate"

    skip_credentials_validation = true
    skip_requesting_account_id  = true
  }
}