TTL Policy

A TTL (Time-To-Live) policy automatically destroys a stack and its resources after a specified duration. This is useful for ephemeral environments like development workspaces, testing infrastructure, or temporary demos that should be cleaned up automatically.

How It Works

When you attach a TTL policy to a stack, formae:

  1. Records when the stack was created or when the policy was attached
  2. Schedules destruction for when the TTL expires
  3. Automatically destroys the stack and all its resources when the time comes

The TTL countdown starts when the stack is first created with the policy attached. If you re-apply the stack, the TTL is not reset—the original creation time is preserved.

Configuration

A TTL policy has two settings:

Property Type Description
ttl Duration How long the stack should live (e.g., 1.h, 24.h, 7.d)
onDependents String What to do if other resources depend on this stack: "abort" (default) or "cascade"

The onDependents Setting

When a TTL expires, formae checks if any resources outside the stack depend on resources inside it. The onDependents setting controls what happens:

  • abort (default): Cancel the destruction if external resources depend on this stack. The stack remains intact until dependencies are removed.
  • cascade: Delete dependent resources in other stacks as well, following the dependency chain.

Warning: Use cascade with caution. It can delete resources in other stacks if they depend on resources in the expiring stack.

Examples

Inline TTL Policy

Define a TTL policy directly in the stack:

amends "@formae/forma.pkl"

import "@formae/formae.pkl"
import "@aws/aws.pkl"
import "@aws/logs/loggroup.pkl"

local devTarget = new formae.Target {
  label = "dev-target"
  config = new aws.Config {
    region = "us-east-1"
  }
}

forma {
  devTarget

  new formae.Stack {
    label = "feature-xyz"
    description = "Temporary dev environment for feature XYZ"
    policies = new Listing {
      new formae.TTLPolicy {
        ttl = 8.h              // Destroy after 8 hours
        onDependents = "abort" // Don't destroy if other resources depend on it
      }
    }
  }

  new loggroup.LogGroup {
    label = "feature-logs"
    stack = "feature-xyz"
    target = devTarget.res
    logGroupName = "/dev/feature-xyz/logs"
    retentionInDays = 1
  }
}

Reusable TTL Policy

Define a policy once and share it across multiple stacks:

amends "@formae/forma.pkl"

import "@formae/formae.pkl"
import "@aws/aws.pkl"
import "@aws/logs/loggroup.pkl"

// Reusable policy for all ephemeral dev environments
local ephemeralPolicy = new formae.TTLPolicy {
  label = "ephemeral-4h"
  ttl = 4.h
  onDependents = "cascade"
}

local devTarget = new formae.Target {
  label = "dev-target"
  config = new aws.Config {
    region = "us-east-1"
  }
}

forma {
  // Add the reusable policy to the forma
  ephemeralPolicy
  devTarget

  // Stack for developer Alice
  new formae.Stack {
    label = "alice-dev"
    description = "Alice's development environment"
    policies = new Listing { ephemeralPolicy.res }
  }

  new loggroup.LogGroup {
    label = "alice-logs"
    stack = "alice-dev"
    target = devTarget.res
    logGroupName = "/dev/alice/logs"
  }

  // Stack for developer Bob
  new formae.Stack {
    label = "bob-dev"
    description = "Bob's development environment"
    policies = new Listing { ephemeralPolicy.res }
  }

  new loggroup.LogGroup {
    label = "bob-logs"
    stack = "bob-dev"
    target = devTarget.res
    logGroupName = "/dev/bob/logs"
  }
}

Multiple Duration Tiers

Use different TTL policies for different environment types:

amends "@formae/forma.pkl"

import "@formae/formae.pkl"

// Short-lived for quick tests
local quickTest = new formae.TTLPolicy {
  label = "quick-test-1h"
  ttl = 1.h
  onDependents = "cascade"
}

// Standard dev environment
local devEnv = new formae.TTLPolicy {
  label = "dev-env-8h"
  ttl = 8.h
  onDependents = "abort"
}

// Longer-lived staging
local staging = new formae.TTLPolicy {
  label = "staging-7d"
  ttl = 7.d
  onDependents = "abort"
}

forma {
  quickTest
  devEnv
  staging

  // Use the appropriate policy for each stack type
  new formae.Stack {
    label = "integration-test"
    policies = new Listing { quickTest.res }
  }

  new formae.Stack {
    label = "feature-dev"
    policies = new Listing { devEnv.res }
  }

  new formae.Stack {
    label = "release-staging"
    policies = new Listing { staging.res }
  }
}

Use Cases

Development environments: Create temporary workspaces that clean up automatically at the end of the workday.

CI/CD test infrastructure: Spin up infrastructure for integration tests that disappears when tests complete.

Demo environments: Provision infrastructure for customer demos that auto-destroys after the demo window.

Cost control: Prevent forgotten development resources from running indefinitely and accumulating costs.

Monitoring TTL

Check which stacks have TTL policies and when they expire:

formae inventory stacks

The output shows attached policies for each stack.