02/28/26

Replace Docker Compose + Terraform with One Framework

The default stack AI agents build, and what happens when you remove it

5 Min Read

Ask Claude Code or Cursor to set up a multi-service TypeScript backend, and you'll get roughly the same stack every time: Express for the HTTP layer, a Dockerfile per service, a docker-compose.yml to wire them together locally, and Terraform modules for the AWS resources in production. It's the pattern with the most surface area in training data, so agents reproduce it by default.

The stack works. It's also a lot of configuration for what amounts to "run my TypeScript services on AWS with a database and a message queue."

The Default Stack, Laid Out

Here's what a typical two-service backend looks like with the Docker Compose + Terraform approach. An orders service and a notifications service, with a PostgreSQL database and a message queue.

The application code (the part that does the work):

orders/src/index.ts Express app, ~80 lines orders/src/routes.ts API endpoints, ~60 lines orders/src/db.ts database connection pool, ~25 lines orders/src/queue.ts BullMQ producer setup, ~20 lines notifications/src/index.ts Express app, ~40 lines notifications/src/worker.ts BullMQ consumer, ~30 lines

About 250 lines of application code across two services.

The configuration layer (the part that wires and deploys it):

orders/Dockerfile 25 lines notifications/Dockerfile 25 lines docker-compose.yml 45 lines terraform/main.tf 30 lines terraform/vpc.tf 60 lines terraform/rds.tf 45 lines terraform/elasticache.tf 35 lines terraform/ecs.tf 80 lines terraform/alb.tf 50 lines terraform/iam.tf 40 lines terraform/ecr.tf 15 lines terraform/outputs.tf 10 lines terraform/variables.tf 30 lines .github/workflows/deploy.yml 60 lines

About 550 lines of configuration. More than twice the application code. For two services.

And the configuration isn't static. Every time you add a service, you add a Dockerfile, an ECS task definition, a target group, IAM roles, and entries in the docker-compose and CI/CD pipeline. Every time you add a database, you add an RDS module with its security group and subnet group.

The Same Backend Without the Configuration Layer

Here's the same two-service backend built with Encore.ts:

// orders/orders.ts import { api } from "encore.dev/api"; import { SQLDatabase } from "encore.dev/storage/sqldb"; import { Topic } from "encore.dev/pubsub"; // Declares a PostgreSQL database. Encore provisions RDS on AWS. const db = new SQLDatabase("orders", { migrations: "./migrations" }); // Declares a Pub/Sub topic. Encore provisions SNS + SQS per subscriber. export const orderCreated = new Topic<OrderEvent>("order-created", { deliveryGuarantee: "at-least-once", }); // Defines a type-safe API endpoint. Encore handles routing and validation. export const create = api( { expose: true, method: "POST", path: "/orders" }, async (req: CreateOrderRequest): Promise<Order> => { const order = await db.queryRow<Order>` INSERT INTO orders (customer_id, total) VALUES (${req.customerId}, ${req.total}) RETURNING id, customer_id as "customerId", total`; await orderCreated.publish({ orderId: order!.id, customerId: req.customerId, total: req.total, }); return order!; } );
// notifications/notifications.ts import { Subscription } from "encore.dev/pubsub"; import { orderCreated } from "../orders/orders"; // Subscribes to the order-created topic. Encore handles delivery and retries. const _ = new Subscription(orderCreated, "send-notification", { handler: async (event) => { console.log(`Order ${event.orderId}: sending notification`); }, });

About 40 lines total, with no Dockerfiles, docker-compose.yml, Terraform directory, or CI/CD pipeline config.

File-by-File Comparison

Docker Compose + Terraform stackEncore
orders/Dockerfile (25 lines)Not needed. Encore builds container images
notifications/Dockerfile (25 lines)Not needed
docker-compose.yml (45 lines)Not needed. encore run handles local dev
terraform/vpc.tf (60 lines)Not needed. VPC provisioned automatically
terraform/rds.tf (45 lines)new SQLDatabase("orders", ...), 1 line
terraform/elasticache.tf (35 lines)Not needed. Encore uses SNS+SQS, not Redis
terraform/ecs.tf (80 lines)Not needed. Services derived from code
terraform/alb.tf (50 lines)Not needed. Load balancer provisioned automatically
terraform/iam.tf (40 lines)Not needed. Least-privilege IAM generated from code
terraform/ecr.tf (15 lines)Not needed
terraform/variables.tf (30 lines)Not needed
.github/workflows/deploy.yml (60 lines)Not needed. Deploy on git push
~550 lines of config0 lines of config

The AWS resources are the same: Fargate, RDS, SNS+SQS, a load balancer, IAM roles. The difference is who wrote the configuration.

Local Development Is the Bigger Quality-of-Life Difference

The docker-compose.yml is easy to underestimate. It's only 45 lines, but it's the thing you interact with every day. docker-compose up takes 30 seconds. Rebuilding after a change takes longer. The PostgreSQL data volume occasionally gets corrupted. Redis needs to be running even though you're only working on one service.

encore run starts both services with a real PostgreSQL instance (provisioned via Docker) and pub/sub with production-equivalent delivery semantics. Hot reload on file changes, and a local dashboard at localhost:9400 with distributed traces, an API explorer, and a database browser.

Encore local development dashboard showing API endpoints and request tracing

The difference between a 30-second startup with a container rebuild cycle and a 2-second startup with hot reload shapes how you work every day.

What an AI Agent Does Differently with This Stack

When an agent works in a project with Docker Compose and Terraform, every feature that touches infrastructure requires changes in multiple places: the application code, the docker-compose, the Terraform, and the CI/CD pipeline. The agent can generate all of it, but each piece has to be reviewed separately, and they can drift from each other.

When the agent works in an Encore project, a new feature that needs a database is const db = new SQLDatabase(...). A new pub/sub topic is new Topic(...). A new service is a new directory. The agent spends its time on business logic instead of configuration.

The Migration Path

If you have an existing Docker Compose + Terraform stack, you don't have to rewrite everything at once. Encore supports incremental adoption: start with one service, deploy it alongside your existing infrastructure, and migrate service by service. Each service you move removes a Dockerfile, a Terraform module, and the associated maintenance surface.

You can also generate Docker images with encore build docker for any service, so self-hosting remains an option.

Ready to escape the maze of complexity?

Encore Cloud is the development platform for building robust type-safe distributed systems with declarative infrastructure.