Storing Terraform state in Pilvio S3

    Pilvio offers S3-compatible object storage at s3.pilw.io. You can use it as a Terraform S3 backend, but since it's not actual AWS, several compatibility flags are required.

    Prerequisites

    1. S3 credentials — get your access key and secret key from the Pilvio dashboard (or API: GET https://api.pilvio.com/v1/storage/user).

    2. Create the state bucket (one-time):

      export AWS_ACCESS_KEY_ID="<your_access_key>"
      export AWS_SECRET_ACCESS_KEY="<your_secret_key>"
      
      aws s3 mb s3://my-tfstate --endpoint-url https://s3.pilw.io
      
    3. Export credentials so Terraform can find them:

      export AWS_ACCESS_KEY_ID="<your_access_key>"
      export AWS_SECRET_ACCESS_KEY="<your_secret_key>"
      

    Backend configuration

    terraform {
      backend "s3" {
        bucket = "my-tfstate"
        key    = "project/terraform.tfstate"
    
        # Pilvio S3 endpoint
        endpoint = "https://s3.pilw.io"
        region   = "eu-west-1"       # required by Terraform, any valid region string works
    
        # Required for non-AWS S3-compatible providers:
        skip_credentials_validation = true   # Pilvio doesn't support AWS STS
        skip_metadata_api_check     = true   # no EC2 instance metadata service
        skip_requesting_account_id  = true   # no AWS account ID endpoint
        skip_s3_checksum            = true   # Pilvio doesn't support AWS checksum headers
        force_path_style            = true   # use s3.pilw.io/bucket instead of bucket.s3.pilw.io
      }
    }
    

    What each flag does

    FlagWhy it's needed
    skip_credentials_validationTerraform tries to validate creds via AWS STS — Pilvio doesn't have STS
    skip_metadata_api_checkTerraform looks for EC2 IMDS to get credentials — doesn't exist outside AWS
    skip_requesting_account_idTerraform calls AWS IAM to get account ID — not available on Pilvio
    skip_s3_checksumPilvio doesn't support the x-amz-checksum-* headers that newer AWS SDKs send
    force_path_stylePilvio uses path-style URLs (s3.pilw.io/bucket), not virtual-hosted (bucket.s3.pilw.io)

    Without these flags, terraform init fails with errors like "No valid credential sources found" or "failed to refresh cached credentials, no EC2 IMDS role found".

    Terraform >= 1.11.2 workaround

    There's a known bug where skip_s3_checksum alone doesn't fully suppress checksum headers in Terraform >= 1.11.2. Work around it by also exporting:

    export AWS_REQUEST_CHECKSUM_CALCULATION=when_required
    export AWS_RESPONSE_CHECKSUM_VALIDATION=when_required
    

    Deprecated endpoint parameter

    Newer Terraform versions warn that endpoint is deprecated in favor of endpoints.s3. When you see that warning, update to:

        endpoints = {
          s3 = "https://s3.pilw.io"
        }
    

    Verify

    Once the config is in place and credentials are exported, run:

    terraform init
    

    On success you'll see Terraform has been successfully initialized! and the state file will appear in your Pilvio bucket my-tfstate under the key project/terraform.tfstate.

    Verify from the command line:

    aws s3 ls s3://my-tfstate/project/ --endpoint-url https://s3.pilw.io
    

    Why this matters

    The Terraform state file is the source of truth for your infrastructure. If it lives in AWS S3, your Estonian infrastructure's state file sits in US jurisdiction — CLOUD Act exposure. Keeping it in Pilvio S3 means the state file stays in Estonia, in the same jurisdiction as the rest of your infrastructure.

    See also: S3 Object Storage in Estonia and the StorageVault service page.