Klotho is no longer in active development, you can view the archived repo on Github

· Ala Shiban  · 7 min read

Klotho 2's First Release

Klotho 2.0 is a 3rd gen AWS deployment and operations tool that lets you think in terms of what your application needs, rather than how the cloud needs to be implemented, and lets you do the same things as Pulumi and Terraform with 10X less code.

Klotho lets you describe highly managed cloud resources the way you think about your applications:

infra.py
1
klotho.Application(
2
"my-app",
3
project="my-project",
4
)
5
# Define a Postgres database
6
users_db = Databases.Postgres(name="users_db")
7
8
# Define a long-running container for the user service
9
user_service = Compute.Container(name="user_service", context="./user-service")
10
11
# Specify that the user service reads from and writes to the users database
12
user_service.read_writes_to(users_db)
13
14
# Define an API and attach the user_service to /users
15
user_api = API(name="user_api")
16
user_api.attach_service(user_service, route="/users")
17
18
# Define a domain service for the API
19
domain = Domain(name="api.example.com", exposes=user_api)

Then run klotho to deploy:

Terminal window
1
# Deploy the infrastructure
2
>> klotho up

Gen3 Infra-as-Code: App Centric Architecture

While first-generation tools like Chef and Puppet focused on configuring machines, and second-generation tools like Terraform and Pulumi centered on instantiating and configuring infrastructure components, the third-generationis app centric.

Looking back, Gen2 IaC excelled at configuring individual cloud resources. Gen3, however, is about composing them. Klotho lets you define application-level building blocks like databases, containers, APIs, and message queues, and the relationships between them like Container.read_writes_to(Database) or API.authenticates_with(Auth_Service) abstracting away the underlying networking, security, and optimization. It’s like shifting from assembling Lego bricks one by one to using larger modules that snap together intelligently.

What makes Klotho different is its multi-stack, multi-repo native design, but first, let’s look at a more conventional multi-tier web app single-repo example.

Multi-tier web app example

Here’s an example of how you can define a multi-tier web application, where an “always-on” OCR processing service receives data from one container, triggers an on-demand archiving service, and exposes a public API alongside profile and billing microservices connected to a PostgreSQL database:

Define containerized services with specified Docker images.

app-example-infra.py
1
klotho.Application(
2
"my-app",
3
project="my-project",
4
)
5
# Define containerized services with specified contexts
6
app1_container = Compute.Container(name="app1", context="./apps/app1")
7
ocr_processor_container = Compute.Container(name="ocr_module", context="./apps/ocr-processor")
8
archiver_service_container = Compute.Container(name="archiver", context="./apps/archiver-service")

Define communication between containers.

construct-interactions-example-infra.py
1
app1_container.calls(ocr_processor_container, "grpc")
2
ocr_processor_container.calls(archiver_service_container)

Define an on-demand Lambda function:

serverless-example-infra.py
1
profile_lambda = Compute.Function(
2
name="profile_lambda",
3
handler="apps/profile.handler",
4
)

Define a PostgreSQL database and interactions with the Lambda function for tight security boundaries:

postgress-example-infra.py
1
profiles_database = Databases.Postgres(name="profiles_db")
2
profile_lambda.reads_from(profiles_database)

Define another service container for billing:

serverless-compute-example-infra.py
1
billing_lambda = Compute.Function(
2
name="billing_lambda",
3
handler="apps/billing.handler",
4
)

Define a domain with an SSL certificate:

domain-example-infra.py
1
domain = Domain(name="mysite.com")
2
domain.attach_cert("./certs/mysite_com.crt")

Define and attach 3 routes to 2 Lambdas and an ECR container to the domain:

routing-and-domains-example-infra.py
1
mysite_api = API(name="mysite-api")
2
3
mysite_api.attach_service(profile_lambda, route="/api/profiles")
4
mysite_api.attach_service(billing_lambda, route="/api/billing")
5
mysite_api.attach_service(ocr_processor_container, route="/api/ocr")
6
7
domain = Domain(name="mysite.com", exposes=mysite_api)

And then deploy:

Terminal window
1
>> klotho up

Infra Segmentation and Multi-Stack

We’re following the same use of the term ‘stack’ from Terraform or Pulumi, namely a stack is a logical grouping of infrastructure resources that can be managed and provisioned together as a unit. While these tools encourage managing all resources within a single stack, Klotho embraces infra segmentation within a unified definition file we refer to as a “Klotho program.”

When you’ve used higher level infra-as-code tools like SST long enough, you’ve ran into their common pitfalls: Changing one part can too easily force deployment of many other parts, for example, changing the API route of one lambda leads to re-deployment of all other lambdas, or getting stuck waiting for 5+ minutes after changing a single HTML file in your frontend.

This design allows different infrastructure components, such as databases and compute clusters, to be managed by separate stacks within the same Klotho program. The key goal is to reduce the risk of needing to tear everything down and start fresh, a constantly growing risk with monolithic stacks. This enables two key capabilities:

  1. Grouping resources with similar change patterns, such as frequently deployed vs. rarely deployed. For example, placing stateful databases in a different stack than frequently updated app code deployments.

  2. Change operations only touch modified stacks, minimizing cross-stack modifications. If a deploy fails, segmentation makes it easier to identify which stack failed (e.g., the “RDS” stack). This also makes sure that only things that change will be deployed, a common problem in monolithic stacks.

As a developer, these two combined make deployments leaner, minimally scoped to localized changes and more resilient to corruption and drift.

Multi-Repo

Based on our survey, 56% of startups start with a multi-repo setup, and the vast majority of startups evolve into one soon after. However all of the available IaC tools aren’t multi-repo aware. That means that you as a developer have to setup CI/CD pipelines for each repo, create meta-release trains across that span repos, hardcode and update deployment information from one repo to another to use shared resources like databases, add more services to an API gateway or find a Lambda or container address you want to invoke that was defined in a different part of the service.

Klotho is multi-repo native and built on the same multi-stack approach. It introduces a project, where repos can be part of a project. It allows each repository to have its own definition of infrastructure, but be part of that larger project. Being part of a project lets Klotho validate changes made by each repo against the global view of the project, detecting issues, broken dependencies, and conflicts. This helps maintain the integrity of the infrastructure across multiple repositories. Klotho stitches together all the infrastructure requirements of the project across multiple repositories, letting you see a comprehensive view per repo, or zoomed out and look at the project level.

Klotho provides you with a runtime SDK that your applications can use. Imagine an application that has these two services: “user-service” and “billing-service”. The “user-service” is in its own repo, which contains the code for managing user accounts and profiles, while the “billing-service” repo handles subscription management and payment processing. Both services need to access a shared PostgreSQL database and expose their functionalities through a common API gateway:

Define the user-service application as part of the “kentoso” project:

github.com/kentoso/user-service/infra.py
1
klotho.Application(
2
"user-service",
3
project="kentoso",
4
)

Define the users PostgreSQL database and the user service container:

github.com/kentoso/user-service/infra.py
1
# Define the users PostgreSQL database
2
users_db = Databases.Postgres(name="users_db")
3
# Define the user service container
4
user_service = Compute.Container(context="./user-service")
5
# Specify that the user service reads from and writes to the users database
6
user_service.read_writes_to(users_db)

Define the create invoice Lambda function and specify the user service’s interaction with it:

github.com/kentoso/user-service/infra.py
1
# Define the create invoice Lambda function
2
create_invoice_lambda = Compute.Function(
3
name="create_invoice_lambda",
4
handler="user-service/create_invoice.handler",
5
)
6
7
# Specify that the user service writes to the create invoice Lambda function
8
user_service.calls(create_invoice_lambda)

Define the API gateway and attach the user service:

github.com/kentoso/user-service/infra.py
1
# Define the API gateway and attach the user service
2
kentoso_api = API(name="kentoso_api")
3
kentoso_api.attach_service(user_service, route="/users")

Use the Klotho runtime SDK in the user-service application:

github.com/kentoso/user-service/app.py
1
import klotho
2
import psycopg2
3
4
users_db = klotho.config.datastores.get("users_db")
5
6
psycopg2.connect(
7
host=users_db.host,
8
port=users_db.port,
9
dbname=users_db.name,
10
user=users_db.user,
11
password=users_db.password
12
)

Define the billing-service application as part of the “kentoso” project:

github.com/kentoso/billing-service/infra.py
1
klotho.Application(
2
"billing-service",
3
project="kentoso",
4
)

Define the billing service container and specify its interaction with the create invoice Lambda function from the user-service repo:

github.com/kentoso/billing-service/infra.py
1
# Define the billing service container
2
billing_service = Compute.Container(context="./billing-service")
3
4
# Specify the create invoice Lambda function from the user-service repo
5
create_invoice_lambda = Compute.Function(name="create_invoice_lambda", from_project=True)
6
7
# Specify that the billing service reads from the invoice service defined in the user-service repo
8
billing_service.calls(create_invoice_lambda)

Attach the billing service to the API gateway defined in the user-service repo:

github.com/kentoso/billing-service/infra.py
1
kentoso_api = API(name="kentoso_api", from_project=True)
2
3
# Attach the billing service to the API gateway defined in the user-service repo
4
kentoso_api.attach_service(billing_service, route="/billing")

Use the Klotho runtime SDK in the billing-service application:

github.com/kentoso/billing-service/app.py
1
import klotho
2
import boto3
3
4
create_invoice_lambda = klotho.config.services.get("invoice_service")
5
lambda_client = boto3.client('lambda')
6
response = lambda_client.invoke(FunctionName=create_invoice_lambda.name, Payload=payload)

Differences from other tools

Existing templates or wrapper tools often reduce complexity by limiting the set of technologies, cloud patterns, runtime capabilities, or workloads they optimize for. For example Arc, SST.dev’s high level constructs, AWS’s Chalice and Zappa are limited to serverless technologies only.

Predictably, they’re each limited to a single implementation for API gateways, load balancing, service discovery, secret management, security boundaries and network interfaces. All of them inherit the limitations of their underlying implementations.

Klotho on the other hand offers the same ease and high level constructs, but for the majority of combinations by using a cloud resource resolver that dynamically implementing your specific application needs an optimized and cost effective cloud infra that you can steer. [read more about the Klotho Engine]. This guarantees that as your application grows the cloud patterns you need are available and not limited by a narrow set of supported technologies.

Try Klotho 2.0

Start building with Klotho! All you have to do is brew install klothoplatorm/klotho and get started with our docs

    Share:
    Back to Blog