Spacelift Cheat Sheet
Overview
Spacelift is a sophisticated infrastructure-as-code (IaC) management platform that provides CI/CD workflows specifically designed for infrastructure tools including Terraform, OpenTofu, Pulumi, CloudFormation, Kubernetes, and Ansible. It goes beyond simple automation by offering policy-as-code governance using Open Policy Agent (OPA), drift detection, resource visualization, cost estimation, and fine-grained access controls that make it suitable for enterprise infrastructure management at scale.
The platform organizes infrastructure into “stacks” that represent independent deployment units, with support for stack dependencies, shared contexts for common configurations, and worker pools for executing runs in private networks. Spacelift’s approval workflows, plan policies, and push policies enable organizations to enforce guardrails on infrastructure changes while still empowering developers to self-serve their infrastructure needs.
Installation
Spacelift CLI (spacectl)
# macOS
brew install spacelift-io/spacelift/spacectl
# Linux
curl -fsSL https://github.com/spacelift-io/spacectl/releases/latest/download/spacectl-linux-amd64 -o /usr/local/bin/spacectl
chmod +x /usr/local/bin/spacectl
# Verify
spacectl version
# Login to Spacelift
spacectl profile login https://your-account.app.spacelift.io
# Configure API token
export SPACELIFT_API_KEY_ENDPOINT="https://your-account.app.spacelift.io"
export SPACELIFT_API_KEY_ID="your-key-id"
export SPACELIFT_API_KEY_SECRET="your-key-secret"
Worker Pool (Self-Hosted Runners)
# Deploy a private worker pool (Docker)
docker run -d \
--name spacelift-worker \
-e SPACELIFT_TOKEN="worker-pool-token" \
-e SPACELIFT_POOL_PRIVATE_KEY="base64-encoded-private-key" \
-v /var/run/docker.sock:/var/run/docker.sock \
public.ecr.aws/spacelift/launcher:latest
# Deploy worker pool on Kubernetes
helm repo add spacelift https://downloads.spacelift.io/helm
helm repo update
helm install spacelift-worker spacelift/spacelift-worker-pool \
--namespace spacelift \
--create-namespace \
--set credentials.token="worker-pool-token" \
--set credentials.privateKey="base64-private-key" \
--set replicas=3
Core Commands — Stack Management
Managing Stacks via CLI
# List all stacks
spacectl stack list
# Get stack details
spacectl stack show --id "my-stack"
# Create a stack
spacectl stack create \
--name "production-vpc" \
--repository "org/infrastructure" \
--branch "main" \
--project-root "terraform/vpc" \
--terraform-version "1.7.0" \
--administrative
# Trigger a run
spacectl stack run trigger --id "production-vpc"
# Confirm a run (approve pending apply)
spacectl stack run confirm --id "production-vpc" --run "run-id"
# Discard a pending run
spacectl stack run discard --id "production-vpc" --run "run-id"
# Set stack environment variable
spacectl stack environment set --id "production-vpc" \
--name "AWS_REGION" --value "us-east-1"
# Set sensitive variable
spacectl stack environment set --id "production-vpc" \
--name "DB_PASSWORD" --value "secret" --secret
# Deploy a stack
spacectl stack deploy --id "production-vpc"
# Lock a stack (prevent changes)
spacectl stack lock --id "production-vpc" --note "Maintenance window"
# Unlock a stack
spacectl stack unlock --id "production-vpc"
Managing Runs
# List runs for a stack
spacectl stack run list --id "production-vpc"
# Get run details
spacectl stack run show --id "production-vpc" --run "run-id"
# View run logs
spacectl stack run logs --id "production-vpc" --run "run-id"
# List all currently running/pending runs
spacectl stack run list --id "production-vpc" --state "QUEUED,PLANNING,APPLYING"
# Get run resources (what will change)
spacectl stack run resources --id "production-vpc" --run "run-id"
Core Commands — GraphQL API
Stack Operations
# Spacelift uses a GraphQL API
SPACELIFT_API="https://your-account.app.spacelift.io/graphql"
# List stacks
curl -s "$SPACELIFT_API" \
-H "Authorization: Bearer $SPACELIFT_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "{ stacks { id name state branch repository projectRoot } }"
}' | jq '.data.stacks[]'
# Trigger a run
curl -s "$SPACELIFT_API" \
-H "Authorization: Bearer $SPACELIFT_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "mutation { stackRunTrigger(stack: \"production-vpc\") { id state } }"
}'
# Get stack resources
curl -s "$SPACELIFT_API" \
-H "Authorization: Bearer $SPACELIFT_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "{ stack(id: \"production-vpc\") { trackedResources { count } currentRun { id state } } }"
}'
Configuration
Stack Configuration (Terraform)
# .spacelift/config.yml — Stack configuration in repo
version: "1"
stacks:
production-vpc:
name: "Production VPC"
project_root: "terraform/vpc"
terraform_version: "1.7.0"
labels:
- "environment:production"
- "team:platform"
environment:
variables:
- name: AWS_REGION
value: us-east-1
- name: TF_VAR_environment
value: production
before_init:
- "echo 'Initializing VPC stack'"
after_apply:
- "curl -X POST https://hooks.slack.com/services/T00/B00/xxx -d '{\"text\": \"VPC deployed\"}'"
auto_deploy: true
auto_retry: true
production-eks:
name: "Production EKS"
project_root: "terraform/eks"
terraform_version: "1.7.0"
dependencies:
- production-vpc
labels:
- "environment:production"
- "team:platform"
worker_pool: "private-workers"
Contexts (Shared Configuration)
# Create a shared context
spacectl context create \
--name "aws-production" \
--description "AWS production account credentials"
# Add variables to context
spacectl context environment set --id "aws-production" \
--name "AWS_ACCESS_KEY_ID" --value "AKIA..." --secret
spacectl context environment set --id "aws-production" \
--name "AWS_SECRET_ACCESS_KEY" --value "secret" --secret
spacectl context environment set --id "aws-production" \
--name "AWS_REGION" --value "us-east-1"
# Attach context to a stack
spacectl stack context attach --id "production-vpc" --context "aws-production"
# Auto-attach context to stacks by label
spacectl context create \
--name "datadog-monitoring" \
--auto-attach "environment:production"
Policy-as-Code (OPA)
# plan-policy.rego — Enforce infrastructure guardrails
package spacelift
# Deny destroying production databases
deny["Cannot destroy production databases"] {
resource := input.terraform.resource_changes[_]
resource.type == "aws_db_instance"
resource.change.actions[_] == "delete"
contains(resource.address, "production")
}
# Require tags on all resources
warn["Resource missing required tags"] {
resource := input.terraform.resource_changes[_]
resource.change.actions[_] == "create"
not resource.change.after.tags["Environment"]
}
# Limit instance sizes
deny[msg] {
resource := input.terraform.resource_changes[_]
resource.type == "aws_instance"
resource.change.actions[_] == "create"
instance_type := resource.change.after.instance_type
contains(instance_type, "metal")
msg := sprintf("Bare metal instances not allowed: %s", [instance_type])
}
# Cost threshold
deny[msg] {
cost := input.spacelift.cost_estimate.monthly_cost
cost > 10000
msg := sprintf("Monthly cost $%.2f exceeds $10,000 limit", [cost])
}
# Create a plan policy
spacectl policy create \
--name "infrastructure-guardrails" \
--type "PLAN" \
--body-file plan-policy.rego
# Attach policy to stacks by label
spacectl policy create \
--name "tag-enforcement" \
--type "PLAN" \
--body-file tag-policy.rego \
--auto-attach "environment:production"
Push Policy (Control Which Pushes Trigger Runs)
# push-policy.rego
package spacelift
# Only trigger on changes to the stack's project root
track {
affected := input.push.affected_files[_]
startswith(affected, input.stack.project_root)
}
# Ignore documentation changes
ignore {
input.push.affected_files[_] == "README.md"
}
# Auto-deploy on main branch
propose { input.push.branch != input.stack.branch }
track { input.push.branch == input.stack.branch }
Advanced Usage
Stack Dependencies
# Create dependent stacks (VPC -> EKS -> App)
# Spacelift automatically triggers dependent stacks after parent applies
# Define dependency via CLI
spacectl stack dependency add \
--id "production-eks" \
--depends-on "production-vpc"
spacectl stack dependency add \
--id "production-app" \
--depends-on "production-eks"
# Pass outputs between stacks using Terraform remote state
# or Spacelift output references
Drift Detection
# Enable drift detection on a stack
spacectl stack update --id "production-vpc" \
--drift-detection true \
--drift-detection-schedule "0 */4 * * *"
# Reconcile drift (auto-apply to match desired state)
spacectl stack update --id "production-vpc" \
--drift-detection-reconcile true
# Check drift status
spacectl stack show --id "production-vpc" | grep drift
Terraform Provider
# Manage Spacelift with Terraform
terraform {
required_providers {
spacelift = {
source = "spacelift-io/spacelift"
version = "~> 1.0"
}
}
}
resource "spacelift_stack" "vpc" {
name = "production-vpc"
repository = "org/infrastructure"
branch = "main"
project_root = "terraform/vpc"
terraform_version = "1.7.0"
autodeploy = true
autoretry = true
labels = ["environment:production", "team:platform"]
}
resource "spacelift_context" "aws_prod" {
name = "aws-production"
description = "AWS production credentials"
}
resource "spacelift_context_attachment" "vpc_aws" {
context_id = spacelift_context.aws_prod.id
stack_id = spacelift_stack.vpc.id
}
resource "spacelift_policy" "guardrails" {
name = "infrastructure-guardrails"
type = "PLAN"
body = file("policies/guardrails.rego")
}
resource "spacelift_policy_attachment" "guardrails_vpc" {
policy_id = spacelift_policy.guardrails.id
stack_id = spacelift_stack.vpc.id
}
Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
| Run stuck in QUEUED | No available workers | Check worker pool status or add workers |
| Plan policy blocking | OPA policy denying changes | Review policy evaluations in run details |
| Drift detection false positives | State refresh capturing transient changes | Add ignore_changes in Terraform or adjust schedule |
| Context variables not available | Context not attached to stack | Verify context attachment: spacectl stack context list |
| Dependency run not triggering | Dependency not configured | Verify dependency: spacectl stack dependency list |
| Worker pool disconnected | Token expired or network issue | Regenerate worker pool token and restart workers |
| Terraform init failing | Missing provider credentials | Check context variables and mount files |
| Stack resources out of sync | Manual changes outside Spacelift | Run drift detection and reconcile |
# Check worker pool status
spacectl worker-pool list
# View run logs for debugging
spacectl stack run logs --id "my-stack" --run "latest"
# List policies attached to a stack
spacectl stack policy list --id "my-stack"
# Evaluate a policy locally
spacectl policy eval --policy-file guardrails.rego --input plan.json
# Check stack health
spacectl stack show --id "my-stack" --json | jq '{state, trackedResources, currentRun}'
# List all active runs
spacectl stack run list --state "PLANNING,APPLYING,QUEUED"