Skip to content

Classic GitOps

Classic GitOps treats your forma as the single source of truth. This is formae's default approach to infrastructure management, where your code is the only authority and external changes are not accepted or are simply ignored over time. This is where most organizations are right now with their GitOps adoption.

In this workflow, your Git repository contains the complete definition of your infrastructure, and any changes must go through code review and version control.

Prerequisites: Understand Fundamentals and Modular infrastructure before exploring workflows.

How Classic GitOps works

When you run formae apply --mode reconcile, formae:

  1. Reads your entire forma - Everything in your forma block
  2. Compares with current state - What currently exists in AWS
  3. Automatically creates a complete plan - What actually needs to be created, updated, or destroyed
  4. Applies all changes - Bringing reality in line with your desired target state, expressed in your code

This is called "reconcile mode" because formae reconciles the actual infrastructure state with your desired state defined in code.

// This forma completely defines your infrastructure
forma {
  myVpc = new vpc.Vpc {
    cidrBlock = "10.0.0.0/16"
    tags {
      new { key = "Name"; value = "production-vpc" }
    }
  }

  // If you remove this subnet from the forma,
  // it will be destroyed on next apply
  new subnet.Subnet {
    vpcId = myVpc.vpcId
    cidrBlock = "10.0.1.0/24"
  }
}

Hard reconcile vs soft reconcile

If you are really, really sure that external changes can be ignored, you can always run a hard reconcile from your code. When you aren't sure, and actually would like to be able to catch up on some external changes that happened outside of your control, then the soft reconcile is your mode of choice.

Hard reconcile (Classic GitOps) means formae will:

  • Overwrite any external changes made to your resources
  • Destroy resources that exist in AWS but not in your forma
  • Create resources that exist in your forma but not in AWS
  • Update resources to match your forma exactly

Soft reconcile (used in other workflows) means formae will:

  • Preserve external changes when possible
  • Warn about conflicts instead of overwriting
  • Suggest extract commands to incorporate changes manually in your code

When you need the --force flag

If external changes are detected, formae will reject the apply to protect you from overwriting changes:

# This will fail if external changes exist
formae apply --mode reconcile main.pkl

# Use --force to proceed with hard reconcile anyway
formae apply --mode reconcile --force main.pkl

The --force flag tells formae: "I know there are external changes, and I want to overwrite them anyway."

When resources get created, updated, or destroyed

Created: Resource exists in forma but not in AWS

// Adding this to your forma will create it
new s3.Bucket {
  bucketName = "my-new-bucket"
}

Updated: Resource exists in both but with different properties

// Changing mutable properties updates the resource in-place
myVpc = new vpc.Vpc {
  cidrBlock = "10.0.0.0/16"
  tags {
    new { key = "Name"; value = "updated-vpc-name" }  // This updates in-place
    new { key = "Environment"; value = "production" }  // This is added
  }
}

Replaced: Resource exists but immutable properties changed

// Changing immutable properties like cidrBlock causes replacement
myVpc = new vpc.Vpc {
  cidrBlock = "10.1.0.0/16"  // This triggers destroy + create
}

Destroyed: Resource exists in AWS but not in forma

// Removing a resource from your forma will destroy it
// (commented out = will be destroyed)
// new subnet.Subnet { ... }

Important: AWS resources have immutable properties that cannot be changed after creation. Changing these properties in your forma will cause the resource to be destroyed and recreated. Always check the AWS documentation to understand which properties are immutable.

Configuration for Classic GitOps

To ensure hard reconcile behavior, disable discovery and synchronization in your configuration:

agent {
    synchronization {
        enabled = false
    }

    discovery {
        enabled = false
    }
}

When to use Classic GitOps

  • Automated deployments: Git push triggers infrastructure updates through CI/CD
  • Reproducible environments: Same forma creates identical infrastructure anywhere
  • Single source of truth: Your code is the only authority - no external changes
  • Simplest workflow: No discovery, sync, or patch complexity to manage
  • Greenfield projects: Starting fresh with complete control

Best practices

Day-0 (initial deployment)

  • Start with reconcile mode
  • Define your complete infrastructure in one go
  • Test with formae eval before applying

Structural changes

  • Always use formae apply --simulate to identify what will change
  • Use --force flag when you need to overwrite external changes
  • Consider the blast radius of your changes
# Simulate changes before applying
formae apply --mode reconcile --simulate main.pkl

# Force reconcile when external changes exist
formae apply --mode reconcile --force main.pkl

The magic of properties

Properties are a very powerful feature. They allow total blast radius and detail visibility reduction, enabling platform engineers to prepare reusable formas for developers or CI/CD pipelines to execute by providing only a few parameters. That is why properties also become instant CLI configuration options. Any property you define in a forma automatically becomes a command-line flag with helpful description and defaults.

Auto-generated CLI help

Adding --help to your apply command reveals the properties you've created:

formae apply --help examples/complete/static-website/main.pkl

This displays standard CLI options plus a Properties section showing every customizable aspect:

Properties:
      --bucket-suffix property: bucket-suffix [default: "-uber-unique-id"]
      --domain        property: domain [default: "test.platform.engineering"]
      --env           property: env [default: "dev"]
      --project-name  property: project-name [default: "static-website"]
      --region        property: region [default: "us-east-2"]

Make everything configurable

This means you can make any aspect of your infrastructure configurable:

properties {
  instanceType = new formae.Prop { flag = "instance-type"; default = "t3.micro" }
  databaseSize = new formae.Prop { flag = "db-size"; default = "20" }
  backupRetention = new formae.Prop { flag = "backup-days"; default = "7" }
  // Add as many as you need!
}

Your forma becomes a flexible template that teams can customize and provide to others without touching or even seeing the infrastructure code.

# Development environment
formae apply --mode reconcile --instance-type t3.micro main.pkl

# Production environment
formae apply --mode reconcile --instance-type c5.large --backup-days 30 --db-size 100 main.pkl

What's next

You now understand Classic GitOps workflow with reconcile mode. Explore other workflows and concepts: