State Management
Learn how Carina tracks infrastructure state, configure an S3 backend, import existing resources, move and remove state entries, and reference upstream state.
Carina uses a state file to track which resources it manages and their current attributes. This guide covers how to configure state storage, manipulate state entries, and share state between projects.
How state works
When you run carina apply, Carina records each managed resource and its attributes in a state file. On subsequent runs, it compares the desired state (your .crn files) with the recorded state to determine what needs to change.
By default, state is stored locally as carina.state.json. For team usage, configure a remote S3 backend.
Configuring the S3 backend
Add a backend block to your .crn file:
backend s3 {
bucket = 'my-carina-state'
key = 'production/carina.state.json'
region = 'ap-northeast-1'
}
provider awscc {
region = awscc.Region.ap_northeast_1
}
awscc.ec2.Vpc {
cidr_block = '10.0.0.0/16'
tags = {
Name = 'my-vpc'
}
}
The S3 backend stores state in the specified bucket and key. It also supports state locking to prevent concurrent modifications.
Automatic bucket creation
By default, carina apply automatically creates the S3 bucket if it doesn’t exist. This lets you get started without any manual setup:
backend s3 {
bucket = 'my-carina-state'
key = 'production/carina.state.json'
region = 'ap-northeast-1'
auto_create = true # This is the default; can be omitted
}
When the bucket is auto-created, Carina:
- Creates the S3 bucket with public access blocked
- Appends an
aws.s3.Bucketresource definition to your.crnfile - Registers the bucket as a protected resource in state, preventing it from being accidentally modified or destroyed
carina plan shows the upcoming bucket creation as a bootstrap plan before the main resource plan.
To disable auto-creation and require the bucket to exist beforehand, set auto_create = false. If the bucket is missing, carina plan and carina apply will fail with an error.
To delete an auto-created state bucket, use:
carina state bucket-delete <bucket-name> --force
This is a destructive operation that removes the bucket and all state history.
Moving state to a different backend
When you change the backend block — switching from local state to S3,
or changing the key while reorganizing component directories — the
state file must move with it. There is a single answer:
carina init --migrate-state
init compares the configured backend against the one recorded in
carina-backend.lock. If they differ it copies the state from the old
backend to the new one, verifies the copy, and rewrites the lock. A bare
carina init (and any plan/apply/destroy) refuses when the
backend changed, so you never silently abandon the old state — you have
to opt in with --migrate-state (or revert the backend configuration).
A local source is deleted after the verified copy; a remote source is
kept as a recoverable backup. Add --force only when a different
state already exists at the new backend and you intend to replace it.
See the init reference for the full semantics
and crash-recovery behavior.
Importing existing resources
To bring an existing cloud resource under Carina management, use the import block:
import {
to = awscc.ec2.Vpc 'my-vpc'
id = 'vpc-0abc123def456'
}
awscc.ec2.Vpc {
cidr_block = '10.0.0.0/16'
tags = {
Name = 'my-vpc'
}
}
tospecifies the resource type and the name Carina will use in stateidis the cloud provider’s identifier for the existing resource
Run carina plan to verify the import matches the resource definition, then carina apply to record it in state.
Moving resources in state
When you rename a resource or reorganize your code, use the moved block to update the state without destroying and recreating the resource:
moved {
from = awscc.ec2.Vpc 'vpc'
to = awscc.ec2.Vpc 'main_vpc'
}
awscc.ec2.Vpc {
cidr_block = '10.0.0.0/16'
tags = {
Name = 'my-vpc'
}
}
After the move is applied, you can remove the moved block from your code.
Removing resources from state
To stop managing a resource without destroying it in the cloud, use the removed block:
removed {
from = awscc.ec2.Vpc 'main_vpc'
}
This removes the resource from Carina’s state but leaves the actual cloud resource untouched. This is useful when transferring ownership of a resource to another tool or project.
Referencing upstream state
To read outputs from another Carina project’s state, use upstream_state:
let network = upstream_state {
source = "../network"
}
awscc.ec2.SecurityGroup {
group_description = 'Web security group'
vpc_id = network.vpc_id
tags = {
Name = 'web-sg'
}
}
The upstream_state expression points at an upstream project’s directory. Carina loads that directory’s configuration, resolves its backend, reads its state, and exposes the upstream’s exports through the let-bound name. In this example, network.vpc_id references the vpc_id value published by the ../network project’s exports block.
source is required and resolved relative to the enclosing .crn file’s directory. The upstream directory must contain a valid Carina configuration (one or more .crn files — no filename is privileged) with an exports block that publishes the values consumers need.
State CLI commands
Carina provides several CLI commands for inspecting and managing state:
# List all managed resources
carina state list
# Show all resources with full attributes
carina state show
# Look up a specific resource or attribute
carina state lookup vpc
carina state lookup vpc.vpc_id
# Refresh state from cloud providers
carina state refresh
# Force unlock a stuck state lock
carina force-unlock <lock-id>
# Delete a state bucket (destructive)
carina state bucket-delete <bucket-name> --force
State locking
When using the S3 backend, Carina automatically locks state during apply and destroy operations to prevent concurrent modifications. If a lock gets stuck (for example, after a crash), use carina force-unlock to release it.
You can disable locking with the --lock=false flag:
carina apply --lock=false