Stacks
Stacks is an Infrastructure as Code (IaC) system that enables declarative deployment of complete application stacks including infrastructure resources, secrets, bootstrap jobs, and services.
Overview
Stacks provides four major capabilities:
- Secrets Management - Generate, store, and inject secrets via HashiCorp Vault
- Bootstrap Jobs - Run initialization tasks like database migrations
- Service Deployments - Deploy Helm charts or raw Kubernetes manifests
- Multi-Tenant Isolation - Vault policies scoped per tenant
Execution Phases
Stack deployment follows a strict four-phase execution model:
| Phase | Description | Components |
|---|---|---|
| 1. Infrastructure | Provision resources | Databases, Namespaces, Storage |
| 2. Secrets | Generate and store secrets | Vault KV, Dynamic credentials |
| 3. Bootstrap | Run initialization jobs | Migrations, Seeds, Init scripts |
| 4. Services | Deploy applications | Helm Charts, K8s Manifests |
Template Structure
Complete Template Example
version: "1"
name: my-application
tenant: acme-corp
# Phase 1: Infrastructure Resources
resources:
database:
type: database
properties:
engine: postgres
version: "15"
storage_size: 10Gi
cache:
type: valkey
properties:
memory: 256Mi
# Phase 2: Secrets
secrets:
db_password:
type: generated
generator_type: password
length: 32
charset: alphanumeric_special
api_key:
type: generated
generator_type: token
length: 64
external_api_token:
type: static
vault_path: "external/stripe/api-key"
# Phase 3: Bootstrap Jobs
bootstrap:
migrate:
image: "myapp/migrations:v1.2.0"
command: ["./migrate", "up"]
secrets:
DATABASE_URL: "${resources.database.connection_string}"
DB_PASSWORD: "${secrets.db_password.vault_path}"
timeout: "10m"
backoff_limit: 3
seed:
image: "myapp/seeds:v1.2.0"
command: ["./seed", "--env", "production"]
depends_on:
- migrate
secrets:
DATABASE_URL: "${resources.database.connection_string}"
# Phase 4: Services
services:
backend:
type: helm
depends_on:
- migrate
properties:
chart: "oci://registry.sparbz.cloud/charts/backend"
version: "1.2.0"
values:
replicas: 3
image:
tag: "v1.2.0"
database:
host: "${resources.database.host}"
port: "${resources.database.port}"
name: "${resources.database.name}"
secrets:
inject_method: external_secrets
mappings:
- secret: db_password
inject_as: env
env_prefix: DB
- secret: api_key
inject_as: env
# Stack Outputs
outputs:
api_endpoint:
value: "${services.backend.endpoint}"
database_host:
value: "${resources.database.host}"
sensitive: false
Reference Resolution
Stacks supports cross-phase references using the ${namespace.name.attribute} syntax:
Supported Namespaces
| Namespace | Description | Available After |
|---|---|---|
resources | Infrastructure resource outputs | Phase 1 |
secrets | Secret vault paths and metadata | Phase 2 |
bootstrap | Job execution outputs | Phase 3 |
services | Service deployment outputs | Phase 4 |
Reference Examples
# Reference a resource output
database_url: "${resources.database.connection_string}"
# Reference a secret's vault path
secret_path: "${secrets.db_password.vault_path}"
# Reference a bootstrap job output
migration_version: "${bootstrap.migrate.outputs.version}"
# Reference a service endpoint
api_url: "${services.backend.endpoint}"
Secrets Management
Secret Types
Generated Secrets
Automatically generate secure values:
secrets:
# Password with special characters
db_password:
type: generated
generator_type: password
length: 32
charset: alphanumeric_special # or: alphanumeric, hex
# UUID
instance_id:
type: generated
generator_type: uuid
# URL-safe token
api_token:
type: generated
generator_type: token
length: 64
# RSA key pair
signing_key:
type: generated
generator_type: rsa
generator_config:
key_size: 4096
Static Secrets
Reference existing secrets in Vault:
secrets:
stripe_key:
type: static
vault_path: "external/stripe/secret-key"
Dynamic Secrets
Use Vault's database secrets engine for rotating credentials:
secrets:
db_creds:
type: dynamic
engine: database
role: "myapp-readonly"
ttl: "1h"
Secret Injection
Secrets can be injected into services using two methods:
External Secrets (Recommended)
services:
backend:
type: helm
secrets:
inject_method: external_secrets
mappings:
- secret: db_password
inject_as: env
env_prefix: DATABASE
- secret: api_key
inject_as: env
Vault Agent
services:
backend:
type: helm
secrets:
inject_method: vault_agent
mappings:
- secret: db_password
inject_as: file
mount_path: /secrets
Bootstrap Jobs
Bootstrap jobs run Kubernetes Jobs for initialization tasks:
bootstrap:
migrate:
image: "myapp/migrations:latest"
command: ["./migrate"]
args: ["up", "--env", "production"]
secrets:
DATABASE_URL: "${resources.database.connection_string}"
timeout: "15m"
backoff_limit: 3
namespace: "myapp" # optional, defaults to stack namespace
seed:
image: "myapp/seeds:latest"
depends_on:
- migrate # Waits for migrate to complete
Service Deployments
Helm Charts
services:
backend:
type: helm
properties:
chart: "oci://registry.sparbz.cloud/charts/backend"
version: "1.2.0"
release_name: "myapp-backend"
values:
replicas: 3
resources:
requests:
memory: "256Mi"
cpu: "250m"
Raw Manifests
services:
custom_resource:
type: manifest
properties:
manifest: |
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
DATABASE_HOST: "${resources.database.host}"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: custom-app
spec:
replicas: 1
# ... deployment spec
API Endpoints
| Method | Endpoint | Description |
|---|---|---|
| POST | /v1/stacks | Create a new stack |
| GET | /v1/stacks | List all stacks |
| GET | /v1/stacks/{id} | Get stack details |
| PUT | /v1/stacks/{id} | Update stack template |
| DELETE | /v1/stacks/{id} | Delete stack |
| POST | /v1/stacks/{id}/apply | Apply stack changes |
| POST | /v1/stacks/{id}/destroy | Destroy stack resources |
| GET | /v1/stacks/{id}/plan | Preview changes |
| GET | /v1/stacks/{id}/outputs | Get stack outputs |
CLI Commands
# Create a new stack
szc stack create --name my-app --file stack.yaml
# List all stacks
szc stack list
# Get stack details
szc stack get my-stack
# Apply changes
szc stack apply my-stack
# Preview changes (dry-run)
szc stack plan my-stack
# View stack outputs
szc stack outputs my-stack
# Destroy stack
szc stack destroy my-stack
Best Practices
- Use Generated Secrets - Never hardcode secrets in templates
- Dependency Order - Declare explicit dependencies between components
- Idempotent Bootstrap - Design migration scripts to be re-runnable
- Version Helm Charts - Always pin chart versions
- Tenant Isolation - Use unique tenant IDs per environment
- Secret Rotation - Use dynamic secrets where possible
Troubleshooting
Common Issues
| Issue | Cause | Solution |
|---|---|---|
| Secret not found | Vault path doesn't exist | Verify static secret exists in Vault |
| Job timeout | Migration taking too long | Increase timeout value |
| Reference error | Circular dependency | Check depends_on declarations |
| Helm install failed | Chart not found | Verify chart registry access |
Debug Commands
# Check stack status
szc stack get my-stack
# View execution logs
szc stack logs my-stack
# List secrets
szc stack secrets my-stack
# Check job status
szc stack jobs my-stack