Prefer to work through an AI assistant? Follow the
Quick start with an AI assistant
and describe what you want instead of writing Pkl by hand.
1
Install formae
Install the CLI and agent with one command:Add the binary to your Verify the install:
PATH:2
Start the formae agent
The agent runs the infrastructure operations and keeps state in sync with your
cloud. Start it in its own terminal and leave it running:It prints its banner, confirms it has started, and registers the bundled
providers:
Leave it running in this terminal, and open a second terminal for the
remaining steps.
2026-07-20T16:52:21-07:00 INF Starting agent id=37MG2qUmBGgcOdFOg2kaHU2xk6P
2026-07-20T16:52:21-07:00 INF Agent started
2026-07-20T16:52:21-07:00 INF Plugin registered: namespace=AZURE node=formae-azure-plugin@localhost rateLimit=10 resources=43 [pid=<8E428C64.0.1009>] [name=‘PluginCoordinator’] [behavior=plugin_coordinator.PluginCoordinator]
2026-07-20T16:52:21-07:00 INF Plugin registered: namespace=GCP node=formae-gcp-plugin@localhost rateLimit=10 resources=65 [pid=<8E428C64.0.1009>] [name=‘PluginCoordinator’] [behavior=plugin_coordinator.PluginCoordinator]
2026-07-20T16:52:23-07:00 INF Plugin registered: namespace=AWS node=formae-aws-plugin@localhost rateLimit=2 resources=239 [pid=<8E428C64.0.1009>] [name=‘PluginCoordinator’] [behavior=plugin_coordinator.PluginCoordinator]
2026-07-20T16:52:28-07:00 INF Plugin registered: namespace=K8S node=formae-k8s-plugin@localhost rateLimit=10 resources=575 [pid=<8E428C64.0.1009>] [name=‘PluginCoordinator’] [behavior=plugin_coordinator.PluginCoordinator]
2026-07-20T16:52:21-07:00 INF Agent started
2026-07-20T16:52:21-07:00 INF Plugin registered: namespace=AZURE node=formae-azure-plugin@localhost rateLimit=10 resources=43 [pid=<8E428C64.0.1009>] [name=‘PluginCoordinator’] [behavior=plugin_coordinator.PluginCoordinator]
2026-07-20T16:52:21-07:00 INF Plugin registered: namespace=GCP node=formae-gcp-plugin@localhost rateLimit=10 resources=65 [pid=<8E428C64.0.1009>] [name=‘PluginCoordinator’] [behavior=plugin_coordinator.PluginCoordinator]
2026-07-20T16:52:23-07:00 INF Plugin registered: namespace=AWS node=formae-aws-plugin@localhost rateLimit=2 resources=239 [pid=<8E428C64.0.1009>] [name=‘PluginCoordinator’] [behavior=plugin_coordinator.PluginCoordinator]
2026-07-20T16:52:28-07:00 INF Plugin registered: namespace=K8S node=formae-k8s-plugin@localhost rateLimit=10 resources=575 [pid=<8E428C64.0.1009>] [name=‘PluginCoordinator’] [behavior=plugin_coordinator.PluginCoordinator]
3
Copy an example to your workspace
formae ships runnable examples for each cloud. Copy one into a working
directory, along with the
PklProject files that declare its Pkl dependencies
(the formae SDK and cloud schemas). Choose your cloud in the next step for the
exact example.4
Deploy to your cloud
- AWS
- Azure
- GCP
Prerequisites: AWS credentials in your environment, for example via
Apply the example. formae shows you the plan and waits for your
confirmation before it changes anything, then opens a live view of the run.
The example exposes
aws configure or AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY.This example provisions a complete ECS stack: VPC and subnets, security
groups, an ECS cluster, an Application Load Balancer, and a running
service.Copy the example and its Pkl project files:--region as a property, so you can pick a region
without editing any code:apply runs for real, so formae first shows you the plan and asks you to
confirm. Press y to continue, or q to abort. This is the AWS
ecs-hello-world plan: formae apply · reconcile ecs_hello_world.pkl
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
+ 22 create
▌ Targets
Operation ▲ Label
+ create default-aws-target
▌ Stacks
Operation ▲ Label
+ create ecs-hello-world
▌ Resources
Operation ▲ Label Type
+ create ecs-vpc AWS::EC2::VPC
+ create ecs-igw AWS::EC2::InternetGateway
+ create ecs-igw-attachment AWS::EC2::VPCGatewayAttachment
+ create ecs-public-subnet-1 AWS::EC2::Subnet
+ create ecs-public-subnet-2 AWS::EC2::Subnet
+ create ecs-public-rt AWS::EC2::RouteTable
+ create ecs-public-route AWS::EC2::Route
+ create ecs-public-subnet-1-assoc AWS::EC2::SubnetRouteTableAssociation
+ create ecs-public-subnet-2-assoc AWS::EC2::SubnetRouteTableAssociation
+ create ecs-alb-sg AWS::EC2::SecurityGroup
↓ show 10 more (10 remaining)
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
↑↓: select space: expand →←: column s: sort y: confirm q: abort This operation will create 1 stack(s), create
1 target(s) and create 20 resource(s). Do you want to continue? (y/N) ?: help
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
+ 22 create
▌ Targets
Operation ▲ Label
+ create default-aws-target
▌ Stacks
Operation ▲ Label
+ create ecs-hello-world
▌ Resources
Operation ▲ Label Type
+ create ecs-vpc AWS::EC2::VPC
+ create ecs-igw AWS::EC2::InternetGateway
+ create ecs-igw-attachment AWS::EC2::VPCGatewayAttachment
+ create ecs-public-subnet-1 AWS::EC2::Subnet
+ create ecs-public-subnet-2 AWS::EC2::Subnet
+ create ecs-public-rt AWS::EC2::RouteTable
+ create ecs-public-route AWS::EC2::Route
+ create ecs-public-subnet-1-assoc AWS::EC2::SubnetRouteTableAssociation
+ create ecs-public-subnet-2-assoc AWS::EC2::SubnetRouteTableAssociation
+ create ecs-alb-sg AWS::EC2::SecurityGroup
↓ show 10 more (10 remaining)
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
↑↓: select space: expand →←: column s: sort y: confirm q: abort This operation will create 1 stack(s), create
1 target(s) and create 20 resource(s). Do you want to continue? (y/N) ?: help
q to leave the
view at any time; the operation keeps running on the agent. To re-attach to it,
run: formae status command ↻ live
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
ID Command Mode Progress ✓ ✗ ◐ ○ Time
✓ 3Gmw76N43M4ysS54phFL3naR57p apply reconcile completed 22/22 22 0 0 0 04:19
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
▌ Targets
Label ▲ Operation Time
✓ default-aws-target create 00:00
▌ Stacks
Label ▲ Operation Time
✓ ecs-hello-world create 00:00
▌ Resources
Label ▲ Type Operation Time
✓ ecs-alb AWS::ElasticLoadBalancingV2::Load… create 02:45
✓ ecs-alb-sg AWS::EC2::SecurityGroup create 00:22
✓ ecs-alb-sg-ingress AWS::EC2::SecurityGroupIngress create 00:22
✓ ecs-hello-cluster2 AWS::ECS::Cluster create 00:23
✓ ecs-hello-task-def AWS::ECS::TaskDefinition create 00:24
✓ ecs-hello-world-cluster AWS::ECS::Cluster create 00:21
✓ ecs-igw AWS::EC2::InternetGateway create 00:23
✓ ecs-igw-attachment AWS::EC2::VPCGatewayAttachment create 00:22
✓ ecs-listener AWS::ElasticLoadBalancingV2::List… create 00:23
✓ ecs-public-route AWS::EC2::Route create 00:02
✓ ecs-public-rt AWS::EC2::RouteTable create 00:21
✓ ecs-public-subnet-1 AWS::EC2::Subnet create 00:22
✓ ecs-public-subnet-1-assoc AWS::EC2::SubnetRouteTableAssocia… create 00:24
✓ ecs-public-subnet-2 AWS::EC2::Subnet create 00:21
✓ ecs-public-subnet-2-assoc AWS::EC2::SubnetRouteTableAssocia… create 00:23
✓ ecs-task-exec-role AWS::IAM::Role create 00:22
✓ ecs-task-sg AWS::EC2::SecurityGroup create 00:21
✓ ecs-task-sg-ingress AWS::EC2::SecurityGroupIngress create 00:22
✓ ecs-tg AWS::ElasticLoadBalancingV2::Targ… create 00:21
✓ ecs-vpc AWS::EC2::VPC create 00:23
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
→←: column s: toggle sort space: expand d: details q: quit ?: help
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
ID Command Mode Progress ✓ ✗ ◐ ○ Time
✓ 3Gmw76N43M4ysS54phFL3naR57p apply reconcile completed 22/22 22 0 0 0 04:19
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
▌ Targets
Label ▲ Operation Time
✓ default-aws-target create 00:00
▌ Stacks
Label ▲ Operation Time
✓ ecs-hello-world create 00:00
▌ Resources
Label ▲ Type Operation Time
✓ ecs-alb AWS::ElasticLoadBalancingV2::Load… create 02:45
✓ ecs-alb-sg AWS::EC2::SecurityGroup create 00:22
✓ ecs-alb-sg-ingress AWS::EC2::SecurityGroupIngress create 00:22
✓ ecs-hello-cluster2 AWS::ECS::Cluster create 00:23
✓ ecs-hello-task-def AWS::ECS::TaskDefinition create 00:24
✓ ecs-hello-world-cluster AWS::ECS::Cluster create 00:21
✓ ecs-igw AWS::EC2::InternetGateway create 00:23
✓ ecs-igw-attachment AWS::EC2::VPCGatewayAttachment create 00:22
✓ ecs-listener AWS::ElasticLoadBalancingV2::List… create 00:23
✓ ecs-public-route AWS::EC2::Route create 00:02
✓ ecs-public-rt AWS::EC2::RouteTable create 00:21
✓ ecs-public-subnet-1 AWS::EC2::Subnet create 00:22
✓ ecs-public-subnet-1-assoc AWS::EC2::SubnetRouteTableAssocia… create 00:24
✓ ecs-public-subnet-2 AWS::EC2::Subnet create 00:21
✓ ecs-public-subnet-2-assoc AWS::EC2::SubnetRouteTableAssocia… create 00:23
✓ ecs-task-exec-role AWS::IAM::Role create 00:22
✓ ecs-task-sg AWS::EC2::SecurityGroup create 00:21
✓ ecs-task-sg-ingress AWS::EC2::SecurityGroupIngress create 00:22
✓ ecs-tg AWS::ElasticLoadBalancingV2::Targ… create 00:21
✓ ecs-vpc AWS::EC2::VPC create 00:23
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
→←: column s: toggle sort space: expand d: details q: quit ?: help
--mode is required on apply. Learn the difference between reconcile and
patch in Apply modes.5
Verify your deployment
Confirm formae created the resources. Filter the inventory by the stack you
just deployed:For the AWS example, that lists the stack it just deployed:Each example uses its own stack name. You can also check your cloud’s web
console to see the created resources.
formae inventory
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
╭─────────────╮
│ 1 Resources │ 2 Targets 3 Stacks 4 Policies
─╯ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Label ▲ Stack Type NativeID
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
ecs-alb ecs-hello-world AWS::ElasticLoadBalancingV2::LoadBalan…arn:aws:elasticloadbalancing:eu-central-1:897…
ecs-alb-sg ecs-hello-world AWS::EC2::SecurityGroup sg-0ce41bacf1d67b147
ecs-alb-sg-ingress ecs-hello-world AWS::EC2::SecurityGroupIngress sgr-0fbb692f2f8d5636d
ecs-hello-cluster2 ecs-hello-world AWS::ECS::Cluster ecs-hello-world-cluster2
ecs-hello-task-def ecs-hello-world AWS::ECS::TaskDefinition arn:aws:ecs:eu-central-1:897722706215:task-de…
ecs-hello-world-cluster ecs-hello-world AWS::ECS::Cluster ecs-hello-world-cluster
ecs-igw ecs-hello-world AWS::EC2::InternetGateway igw-047bab59003537498
ecs-igw-attachment ecs-hello-world AWS::EC2::VPCGatewayAttachment IGW|vpc-02e1b0b73a5730d65
ecs-listener ecs-hello-world AWS::ElasticLoadBalancingV2::Listener arn:aws:elasticloadbalancing:eu-central-1:897…
ecs-public-route ecs-hello-world AWS::EC2::Route rtb-0bab9fbbf4c0010e5|0.0.0.0/0|GatewayId=igw…
ecs-public-rt ecs-hello-world AWS::EC2::RouteTable rtb-0bab9fbbf4c0010e5
ecs-public-subnet-1 ecs-hello-world AWS::EC2::Subnet subnet-0b38a309934e06276
ecs-public-subnet-1-assoc ecs-hello-world AWS::EC2::SubnetRouteTableAssociation rtbassoc-013308e76c96ade7c
ecs-public-subnet-2 ecs-hello-world AWS::EC2::Subnet subnet-08999cc2f4fb40e87
ecs-public-subnet-2-assoc ecs-hello-world AWS::EC2::SubnetRouteTableAssociation rtbassoc-0d4512b6e49506eba
ecs-task-exec-role ecs-hello-world AWS::IAM::Role 6cfPoszejP6KRJElRijPynvA8-SMXNaMlk1MfC
ecs-task-sg ecs-hello-world AWS::EC2::SecurityGroup sg-075d33812aa776116
ecs-task-sg-ingress ecs-hello-world AWS::EC2::SecurityGroupIngress sgr-068746b2de1bad0c0
ecs-tg ecs-hello-world AWS::ElasticLoadBalancingV2::TargetGro…arn:aws:elasticloadbalancing:eu-central-1:897…
Showing 20 of 20 resources (filtered)
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
/ stack:ecs-hello-world /: edit query
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
↑↓/j/k: navigate enter: detail /: search s: sort r: refresh 1-4: tab q: quit ?: help
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
╭─────────────╮
│ 1 Resources │ 2 Targets 3 Stacks 4 Policies
─╯ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Label ▲ Stack Type NativeID
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
ecs-alb ecs-hello-world AWS::ElasticLoadBalancingV2::LoadBalan…arn:aws:elasticloadbalancing:eu-central-1:897…
ecs-alb-sg ecs-hello-world AWS::EC2::SecurityGroup sg-0ce41bacf1d67b147
ecs-alb-sg-ingress ecs-hello-world AWS::EC2::SecurityGroupIngress sgr-0fbb692f2f8d5636d
ecs-hello-cluster2 ecs-hello-world AWS::ECS::Cluster ecs-hello-world-cluster2
ecs-hello-task-def ecs-hello-world AWS::ECS::TaskDefinition arn:aws:ecs:eu-central-1:897722706215:task-de…
ecs-hello-world-cluster ecs-hello-world AWS::ECS::Cluster ecs-hello-world-cluster
ecs-igw ecs-hello-world AWS::EC2::InternetGateway igw-047bab59003537498
ecs-igw-attachment ecs-hello-world AWS::EC2::VPCGatewayAttachment IGW|vpc-02e1b0b73a5730d65
ecs-listener ecs-hello-world AWS::ElasticLoadBalancingV2::Listener arn:aws:elasticloadbalancing:eu-central-1:897…
ecs-public-route ecs-hello-world AWS::EC2::Route rtb-0bab9fbbf4c0010e5|0.0.0.0/0|GatewayId=igw…
ecs-public-rt ecs-hello-world AWS::EC2::RouteTable rtb-0bab9fbbf4c0010e5
ecs-public-subnet-1 ecs-hello-world AWS::EC2::Subnet subnet-0b38a309934e06276
ecs-public-subnet-1-assoc ecs-hello-world AWS::EC2::SubnetRouteTableAssociation rtbassoc-013308e76c96ade7c
ecs-public-subnet-2 ecs-hello-world AWS::EC2::Subnet subnet-08999cc2f4fb40e87
ecs-public-subnet-2-assoc ecs-hello-world AWS::EC2::SubnetRouteTableAssociation rtbassoc-0d4512b6e49506eba
ecs-task-exec-role ecs-hello-world AWS::IAM::Role 6cfPoszejP6KRJElRijPynvA8-SMXNaMlk1MfC
ecs-task-sg ecs-hello-world AWS::EC2::SecurityGroup sg-075d33812aa776116
ecs-task-sg-ingress ecs-hello-world AWS::EC2::SecurityGroupIngress sgr-068746b2de1bad0c0
ecs-tg ecs-hello-world AWS::ElasticLoadBalancingV2::TargetGro…arn:aws:elasticloadbalancing:eu-central-1:897…
Showing 20 of 20 resources (filtered)
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
/ stack:ecs-hello-world /: edit query
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
↑↓/j/k: navigate enter: detail /: search s: sort r: refresh 1-4: tab q: quit ?: help
6
Clean up
To avoid ongoing charges, destroy what you created:
- AWS
- Azure
- GCP
destroy shows a plan and asks you to confirm too; press y to proceed. The
completed teardown looks like this: formae status command ↻ live
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
ID Command Mode Progress ✓ ✗ ◐ ○ Time
✓ 3Gmwiy52PNdn5rJiu3nWhLDPxJf destroy - completed 20/20 20 0 0 0 02:35
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
▌ Resources
Label ▲ Type Operation Time
✓ ecs-alb AWS::ElasticLoadBalancingV2::Load… delete 01:02
✓ ecs-alb-sg AWS::EC2::SecurityGroup delete 00:21
✓ ecs-alb-sg-ingress AWS::EC2::SecurityGroupIngress delete 00:21
✓ ecs-hello-cluster2 AWS::ECS::Cluster delete 00:21
✓ ecs-hello-task-def AWS::ECS::TaskDefinition delete 00:21
✓ ecs-hello-world-cluster AWS::ECS::Cluster delete 00:21
✓ ecs-igw AWS::EC2::InternetGateway delete 00:21
✓ ecs-igw-attachment AWS::EC2::VPCGatewayAttachment delete 01:02
✓ ecs-listener AWS::ElasticLoadBalancingV2::List… delete 00:22
✓ ecs-public-route AWS::EC2::Route delete 00:01
✓ ecs-public-rt AWS::EC2::RouteTable delete 00:21
✓ ecs-public-subnet-1 AWS::EC2::Subnet delete 00:22
✓ ecs-public-subnet-1-assoc AWS::EC2::SubnetRouteTableAssocia… delete 00:21
✓ ecs-public-subnet-2 AWS::EC2::Subnet delete 00:21
✓ ecs-public-subnet-2-assoc AWS::EC2::SubnetRouteTableAssocia… delete 00:21
✓ ecs-task-exec-role AWS::IAM::Role delete 00:21
✓ ecs-task-sg AWS::EC2::SecurityGroup delete 00:21
✓ ecs-task-sg-ingress AWS::EC2::SecurityGroupIngress delete 00:21
✓ ecs-tg AWS::ElasticLoadBalancingV2::Targ… delete 00:21
✓ ecs-vpc AWS::EC2::VPC delete 00:22
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
→←: column s: toggle sort space: expand d: details esc: back q: quit ?: help
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
ID Command Mode Progress ✓ ✗ ◐ ○ Time
✓ 3Gmwiy52PNdn5rJiu3nWhLDPxJf destroy - completed 20/20 20 0 0 0 02:35
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
▌ Resources
Label ▲ Type Operation Time
✓ ecs-alb AWS::ElasticLoadBalancingV2::Load… delete 01:02
✓ ecs-alb-sg AWS::EC2::SecurityGroup delete 00:21
✓ ecs-alb-sg-ingress AWS::EC2::SecurityGroupIngress delete 00:21
✓ ecs-hello-cluster2 AWS::ECS::Cluster delete 00:21
✓ ecs-hello-task-def AWS::ECS::TaskDefinition delete 00:21
✓ ecs-hello-world-cluster AWS::ECS::Cluster delete 00:21
✓ ecs-igw AWS::EC2::InternetGateway delete 00:21
✓ ecs-igw-attachment AWS::EC2::VPCGatewayAttachment delete 01:02
✓ ecs-listener AWS::ElasticLoadBalancingV2::List… delete 00:22
✓ ecs-public-route AWS::EC2::Route delete 00:01
✓ ecs-public-rt AWS::EC2::RouteTable delete 00:21
✓ ecs-public-subnet-1 AWS::EC2::Subnet delete 00:22
✓ ecs-public-subnet-1-assoc AWS::EC2::SubnetRouteTableAssocia… delete 00:21
✓ ecs-public-subnet-2 AWS::EC2::Subnet delete 00:21
✓ ecs-public-subnet-2-assoc AWS::EC2::SubnetRouteTableAssocia… delete 00:21
✓ ecs-task-exec-role AWS::IAM::Role delete 00:21
✓ ecs-task-sg AWS::EC2::SecurityGroup delete 00:21
✓ ecs-task-sg-ingress AWS::EC2::SecurityGroupIngress delete 00:21
✓ ecs-tg AWS::ElasticLoadBalancingV2::Targ… delete 00:21
✓ ecs-vpc AWS::EC2::VPC delete 00:22
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
→←: column s: toggle sort space: expand d: details esc: back q: quit ?: help
destroy takes no --mode. It removes every resource in the forma, so there
is nothing for reconcile or patch to differ on.What’s next?
Write your first forma
Build a forma from scratch and learn how properties and resource references work.
Quick start with an AI assistant
Deploy by describing what you want to Claude Code, Codex, or another MCP client.
Core concepts
Understand labels, resources, stacks, targets, and formas.
Configure Pkl in your IDE
Set up your editor for Pkl authoring.
Architecture
See how the agent, CLI, and plugins fit together.

