Using Object Storage as a Terraform Backend
Learn how to automate infrastructure deployment using Terraform. Enhance your cloud infrastructure management skills with this helpful guide.
Written by
Technical Writer at Civo
Written by
Technical Writer at Civo
Civo recently made object storage generally available, which is exciting for two reasons.
The first is object storage. What’s not to love?
The second reason is the Civo Terraform provider. If you have used Terraform to provision infrastructure on Civo, you might have noticed no native way to store your Terraform state in the platform.
This tutorial teaches you how to use Civo’s object storage as a backend for Terraform state files. However, before we jump into the guide, let’s take a quick look at what Terraform state is and why it matters.
Terraform state
To keep track of the current state of your infrastructure, Terraform maintains a state. This is typically a JSON file that contains information about resources currently deployed and their dependencies. Without this, Terraform wouldn’t be able to compare the current of your infrastructure with the desired state.
Challenges of managing Terraform state locally
By default, Terraform stores its state file in the root directory where your project was initialized in a file called terraform.tfstate. This is great if you're trying to quickly spin up a few resources for testing or creating a small project. However, this approach quickly brings a new set of challenges as the size and complexity of your project grows.
One major challenge with local state files is keeping them in sync when multiple people work on the same project.
A typical way to solve this is to check it in version control with the rest of your Terraform code. This is largely discouraged because state files often contain sensitive data stored in plaintext. You can refer to this section of the Terraform documentation for more information on the types of sensitive data that can end up in your state file.
Another case where local state management falls short is the event where you lose your state file. Depending on your state file, you can reinitialize Terraform to keep your resources in sync, which is a great way to cause state drift.
Remote state management
Storing your state remotely means Terraform writes state information to a remote data store. Terraform supports Postgres, S3-compatible object stores, Consul, and Kubernetes, to mention a few.
Storing state in a central location solves the problem of keeping state in sync across teams while also addressing the issue of accidentally leaking sensitive data. Remotely storing your state also enables you to take as many backups as possible in the unfortunate event of data store deletion or data loss.
A remote backend also gives you state locking, so two people cannot write to the same state at the same time. We will enable that later in this guide.
Now that we understand what Terraform state management is and why storing your state files remotely is important, let's implement this using Civo's object storage.
Prerequisites
This tutorial assumes some familiarity with Terraform. In addition, you will also need the following:
- A Civo account
- The Civo CLI installed and authenticated
- The Terraform CLI (this guide was verified on Terraform 1.15)
Generating Object Store Credentials
We'll begin by generating object store credentials before creating the object store. Using the Civo CLI:
civo objectstore credentials create tf-experiments
You can retrieve the access key and secret straight from the CLI, no dashboard detour needed:
civo objectstore credential lscivo objectstore credential secret --access-key=<YOUR-ACCESS-KEY-ID>
Copy your Access Key ID and Secret Key, as we'll be needing these later.
Creating an Object store
Next, we create an object storage bucket and associate the credentials we generated earlier with it:
civo objectstore create tf-store --owner-access-key tf-experiments --wait
This creates an object store owned by the tf-experiments credentials. Refer to the Object Store documentation for more information on object store and credentials management.
Provisioning infrastructure
Now we have an object store, the next step is to write some Terraform! In this guide, we will create a Kubernetes cluster using the Civo Terraform provider.
Create a file called main.tf and follow along with the code below:
terraform {required_providers {civo = {source = "civo/civo"version = "~> 1.0"}}backend "s3" {bucket = "tf-store"key = "terraform.tfstate"region = "LON1"endpoints = {s3 = "https://objectstore.lon1.civo.com"}access_key = "OBJECT-STORE-ACCESS-KEY-ID"secret_key = "YOUR-SECRET-KEY"use_path_style = trueuse_lockfile = trueskip_region_validation = trueskip_credentials_validation = trueskip_metadata_api_check = trueskip_requesting_account_id = trueskip_s3_checksum = true}}provider "civo" {# reads your API token from the CIVO_TOKEN environment variableregion = "LON1"}# Query xsmall instance sizedata "civo_size" "xsmall" {filter {key = "type"values = ["kubernetes"]}sort {key = "ram"direction = "asc"}}# Create a firewall with an inline rule for the Kubernetes API serverresource "civo_firewall" "my-firewall" {name = "my-firewall"create_default_rules = false # needs to be false when custom rules are appliedingress_rule {protocol = "tcp"port_range = "6443"cidr = ["0.0.0.0/0"]label = "kubernetes-api-server"action = "allow"}}# Create a clusterresource "civo_kubernetes_cluster" "my-cluster" {name = "my-terraform-cluster"applications = "Redis"firewall_id = civo_firewall.my-firewall.idpools {label = "front-end" // Optionalsize = element(data.civo_size.xsmall.sizes, 0).namenode_count = 3}}
A couple of notes on what changed from older versions of this guide.
The standalone civo_firewall_rule resource is deprecated, so the firewall rule now lives as an ingress_rule block inside civo_firewall, with create_default_rules = false since custom rules replace the defaults. The provider token is no longer hardcoded; export it instead:
export CIVO_TOKEN=<your-civo-api-token>
Configuring Terraform backend
The important part of this configuration is the backend block:
endpoints.s3tells Terraform the address of the object store, which you can retrieve by runningcivo objectstore info tf-storebucketis the name of the object store we created earlierkeyallows you to specify what name Terraform should store the state file underuse_path_styleaddresses the bucket by path rather than subdomain, which S3-compatible stores expectuse_lockfileenables state locking (Terraform 1.10 and later), so concurrent runs cannot corrupt your state- the
skip_*flags disable AWS-specific checks.skip_requesting_account_idandskip_s3_checksumare required on current Terraform: without the first,terraform initfails trying to reach a nonexistent AWS STS endpoint, and without the second, state uploads are rejected
One more piece is required on Terraform 1.11 and later. The AWS SDK inside Terraform now sends checksum trailers that S3-compatible stores reject, so run all Terraform commands with:
export AWS_REQUEST_CHECKSUM_CALCULATION=when_required
Rather than hardcoding access_key and secret_key in main.tf, you can also pass them at init time with terraform init -backend-config="access_key=..." -backend-config="secret_key=...", or via the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables, which keeps credentials out of version control.
To apply the changes, run the following commands:
# initialize the terraform providerterraform init# preview the infrastructureterraform plan# apply the changesterraform apply --auto-approve
Once your cluster has been provisioned, you can confirm the state file landed in the bucket without leaving the terminal:
aws s3 ls s3://tf-store --endpoint-url https://objectstore.lon1.civo.com2026-07-18 09:18:08 3748 terraform.tfstate
You should see the same file listed in your dashboard under the object storage bucket.
Cleaning up
When you are done experimenting, destroy the Terraform-managed resources, then remove the object store and credentials:
terraform destroy --auto-approvecivo objectstore remove tf-storecivo objectstore credential delete tf-experiments
Summary
State management is a crucial part of what Terraform does as it enables it to track your resources and their dependencies, ensuring that resources are always in their desired state.
Through this tutorial, we discussed some of the challenges associated with storing your state files locally and how to leverage Civo's S3-compatible storage to manage your state files remotely.
Further resources
If you are interested in other options that Terraform supports, check out the backend section of the documentation. HashiCorp also has a hands-on tutorial on managing resource drift.

Technical Writer at Civo
Jubril Oyetunji is a DevOps engineer and technical writer with a strong focus on cloud-native technologies and open-source tools. His work centers on creating practical tutorials that help developers better understand platforms such as Kubernetes, NGINX, Rust, and Go.
As a contract technical writer, Jubril authored an extensive library of technical guides covering cloud-native infrastructure and modern development workflows. Many of his tutorials achieved strong search rankings, helping developers around the world learn and adopt emerging technologies.
Share this article