A docker-compose.yml describes an app, a database, a cache and a worker on one machine. AWS splits those into separate managed services, and this guide covers four ways to make that move.
Docker Compose is how most backends run on a laptop: an app container, a Postgres container, a Redis container and a worker, wired together in one docker-compose.yml. AWS has no service that runs that file as-is, so deploying means deciding where each of those containers goes.
Docker's Compose integration for Amazon ECS was retired in November 2023, and AWS Copilot, the CLI widely used instead, reached end of support on June 12, 2026. This guide covers the four routes left in 2026, starting with the one that has no translation step.
A typical Compose file has four kinds of services, and each one has a managed equivalent on AWS:
Each option is covered below, starting with Encore.
Encore is a tool that works alongside your framework for TypeScript and Go, with a platform that provisions and deploys into your own AWS or GCP account. Instead of translating docker-compose.yml into task definitions, the services declare the database, cache and queue they use in code, and Encore provisions RDS, ElastiCache, SNS and SQS, and Fargate in your AWS account, and Encore provisions an IAM role for the service, as an alternative to writing Terraform.

Here is a catalog service that replaces three Compose services at once, with the Postgres container becoming a database, the Redis container a cache cluster and the worker a Pub/Sub subscription:
// catalog/catalog.ts
import { api, APIError } from "encore.dev/api";
import { CacheCluster, StringKeyspace, expireIn } from "encore.dev/storage/cache";
import { Subscription, Topic } from "encore.dev/pubsub";
import { SQLDatabase } from "encore.dev/storage/sqldb";
// docker-compose "postgres" service -> RDS on AWS
const db = new SQLDatabase("catalog", { migrations: "./migrations" });
// docker-compose "redis" service -> ElastiCache on AWS
const cache = new CacheCluster("catalog-cache", { evictionPolicy: "allkeys-lru" });
const productCache = new StringKeyspace<{ id: number }>(cache, {
keyPattern: "product/:id",
defaultExpiry: expireIn(60 * 1000),
});
// docker-compose "worker" service consuming a queue -> SNS and SQS on AWS
interface PriceChanged {
id: number;
priceCents: number;
}
export const priceChanged = new Topic<PriceChanged>("price-changed", {
deliveryGuarantee: "at-least-once",
});
interface Product {
id: number;
name: string;
priceCents: number;
}
export const get = api(
{ expose: true, method: "GET", path: "/products/:id" },
async ({ id }: { id: number }): Promise<Product> => {
const cached = await productCache.get({ id });
if (cached) return JSON.parse(cached) as Product;
const row = await db.queryRow<Product>`
SELECT id, name, price_cents AS "priceCents" FROM products WHERE id = ${id}`;
if (!row) throw APIError.notFound("product not found");
await productCache.set({ id }, JSON.stringify(row));
return row;
},
);
export const setPrice = api(
{ expose: true, method: "POST", path: "/products/:id/price" },
async ({ id, priceCents }: { id: number; priceCents: number }): Promise<void> => {
await db.exec`UPDATE products SET price_cents = ${priceCents} WHERE id = ${id}`;
await productCache.delete({ id });
await priceChanged.publish({ id, priceCents });
},
);
new Subscription(priceChanged, "notify-watchers", {
handler: async (event) => {
// The work the compose "worker" container used to do.
console.log("price changed", event.id, event.priceCents);
},
});
The migration in catalog/migrations creates the products table the queries use:
-- catalog/migrations/1_create_products.up.sql
CREATE TABLE products (
id BIGSERIAL PRIMARY KEY,
name TEXT NOT NULL,
price_cents INTEGER NOT NULL
);
Run in your terminal to get started locally.
encore run starts Postgres locally along with a local cache and Pub/Sub, so the whole stack runs without a docker-compose.yml. Once you connect your AWS account, a git push provisions the RDS database, the ElastiCache cluster, the SNS topic and SQS queue, and the Fargate services, and if you still need a container image for another platform, encore build docker produces one.

Coding agents that work in the repository see the database, cache and queue as typed declarations next to the code that uses them, so adding a resource is a code change rather than an edit to a Compose file and a separate set of Terraform modules.
Best fit: teams that want their Compose app on managed AWS services in their own account.
Docker Compose on EC2The shortest path is to launch an EC2 instance, install Docker Engine and the Compose plugin, copy the repository over and run docker compose up -d. The Compose file works unchanged, which makes this the quickest way to get a demo onto a public IP.
Everything the Compose file used to hide becomes your job in production: the instance is a single point of failure, Postgres data sits on an EBS volume you snapshot yourself, deploys mean SSH or a script that pulls new images, and the operating system needs patching. A common middle step is keeping the app containers on EC2 and moving Postgres to RDS, which removes the riskiest part of the setup.
Best fit: a single small app, a staging copy or an internal tool where downtime during a deploy is acceptable.
ECS Express Mode takes a container image, a task execution role and an infrastructure role, and creates a Fargate service with a URL, a load balancer with TLS, autoscaling, monitoring and networking. Each web-facing container from the Compose file becomes one Express Mode service, and AWS shares load balancers across services with the same networking configuration.
Express Mode covers the containers only, so the Postgres and Redis services from the Compose file still need to be created as RDS and ElastiCache, placed in the same VPC, and wired in through environment variables and secrets, and a worker that consumes a queue needs its own service and an SQS queue.
Best fit: teams with one or two web containers that want them running on Fargate.
The most flexible route is translating each Compose service into an ECS task definition and service, defined in AWS CDK or Terraform alongside the VPC, load balancer, RDS instance, ElastiCache cluster, security groups and IAM roles. AWS now points former Copilot users to CDK L3 constructs for this kind of setup.
The Compose file is a starting point rather than an input: ports, environment variables, health checks and volumes all have to be rewritten in ECS terms, and the infrastructure code also has to describe resources the Compose file never mentioned, such as the VPC, security groups and IAM roles, and the team controls every networking and scaling setting.
Best fit: platform teams that want full control over the ECS setup and already maintain infrastructure code.
Run Compose on EC2 when the app is small and a single machine is acceptable. Use ECS Express Mode when you have a few web containers and are willing to create the database and cache yourself, and write task definitions in CDK or Terraform when you need control over every setting. Choose Encore when you want the Compose services to become managed AWS resources without maintaining the translation, and see replacing Docker Compose and Terraform for a line-by-line comparison of the two setups.
Not with Docker's own tooling any more: the Compose integration for Amazon ECS was retired in November 2023, and AWS Copilot reached end of support on June 12, 2026. The direct route left is running Docker Compose on an EC2 instance, while the managed routes translate each Compose service into an ECS service, an RDS database or an ElastiCache cluster.
It works for small apps that fit on one instance, but the instance is a single point of failure, the database lives on a disk you back up yourself, and you patch the operating system. Moving the database to RDS first removes the riskiest part, even when the app containers stay on EC2.
Amazon RDS replaces the Postgres container and Amazon ElastiCache replaces the Redis container, both running outside your application containers with managed backups and failover. The app containers then run on ECS with Fargate and connect to them over the VPC.
Declare the database with new SQLDatabase, the cache with new CacheCluster and a StringKeyspace, and replace the worker's queue with a Topic and a Subscription. encore run starts Postgres locally, and deploying to an environment connected to your AWS account provisions RDS, ElastiCache, SNS and SQS, and Fargate.
Deploy to your cloud on AWS and Google Cloud
without managing infrastructure
Encore lets developers and agents define application infrastructure directly in code, then automatically provisions it from local development to production in your AWS or GCP account.