Move app-level RDS, SQS, S3, cron and secrets to infrastructure from code one ownership boundary at a time, while org Terraform keeps shared networking.
You can replace application Terraform incrementally: keep org-level networking and shared services in Terraform, and move app-level databases, queues, buckets, cron jobs and secrets to infrastructure from code with Encore. Declare those resources in TypeScript or Go, connect your own AWS or GCP account, import existing resources where the docs support it, and let Terraform reference Encore-managed resources through the provider. No big-bang cutover is required.
This guide defines the ownership split, shows the declaration shape, covers coexistence and import paths that the docs verify, and ends with a migration order that avoids a rewrite project.
Application Terraform is the HCL that exists so one backend can ship:
Org Terraform is everything else: shared VPCs, transit gateways, SSO, account factories, data platforms and specialized services shared across the company. Encore targets the application layer, and org Terraform usually stays.
Encore is infrastructure from code: you declare resources as typed objects in application code, and Encore uses static analysis to run them locally and to provision managed services in your own AWS or GCP account. It works with any framework, including NestJS and Express.
The same Postgres database that takes an aws_db_instance block plus networking and credentials in Terraform is one declaration:
import { api } from "encore.dev/api";
import { SQLDatabase } from "encore.dev/storage/sqldb";
import { Topic, Subscription } from "encore.dev/pubsub";
import { Bucket } from "encore.dev/storage/objects";
import { CronJob } from "encore.dev/cron";
import { secret } from "encore.dev/config";
const db = new SQLDatabase("orders", { migrations: "./migrations" });
const receipts = new Bucket("receipts", { versioned: false });
const stripeKey = secret("StripeKey");
interface OrderPlaced {
orderId: string;
}
export const orderPlaced = new Topic<OrderPlaced>("order-placed", {
deliveryGuarantee: "at-least-once",
});
const _ = new Subscription(orderPlaced, "ship-order", {
handler: async (event) => {
// Shipping logic for event.orderId
},
});
export const place = api(
{ expose: true, auth: true, method: "POST", path: "/orders" },
async (req: { total: number }): Promise<{ id: string }> => {
if (!stripeKey()) throw new Error("StripeKey is unset");
const row = await db.queryRow<{ id: string }>`
INSERT INTO orders (total) VALUES (${req.total})
RETURNING id::text AS id`;
await receipts.upload(
`${row!.id}.json`,
Buffer.from(JSON.stringify({ orderId: row!.id, total: req.total })),
);
await orderPlaced.publish({ orderId: row!.id });
return { id: row!.id };
},
);
export const reconcile = api({}, async () => {
// Idempotent nightly reconcile
});
const _cron = new CronJob("reconcile-orders", {
title: "Reconcile orders",
every: "24h",
endpoint: reconcile,
});
Public write endpoints need authentication and abuse controls. Secret values must never be written to object storage, logs, database rows, or responses.
In your own AWS account those declarations map to RDS, S3, SNS with SQS and Secrets Manager, and Encore provisions an IAM role for the service. Cron jobs are Encore platform Managed: the platform registers and invokes the endpoint, with no extra cloud infrastructure. Sizing, networking and backups live in environment configuration.
Encore is an open source infrastructure SDK, and schema changes go in ./migrations as numbered SQL files and apply on deploy.
Run in your terminal to get started locally.
Josef Sima, Engineering Director at Groupon, said on the customers page: "Encore is our foundation for all new development. What used to take days or weeks between developers and infra teams now takes minutes."
Encore and Terraform can run together: Encore manages supported application resources, and Terraform manages DNS, organization-level networking and third-party providers.
The Encore Terraform provider exposes read-only data sources such as encore_database, encore_cache and encore_pubsub_topic. Terraform does not create those Encore resources; it retrieves cloud identifiers so other Terraform resources can wire into them:
terraform {
required_providers {
encore = {
source = "registry.terraform.io/encoredev/encore"
}
}
}
provider "encore" {
env = "production"
auth_key = var.encore_auth_key
}
data "encore_pubsub_topic" "orders" {
name = "order-placed"
env = "production"
}
# Example: another AWS resource that needs the SNS ARN Encore manages
resource "aws_iot_topic_rule" "orders" {
name = "orders-rule"
sql = "SELECT * FROM 'order-placed'"
sns {
message_format = "RAW"
role_arn = aws_iam_role.rule.arn
target_arn = data.encore_pubsub_topic.orders.aws_sns.arn
}
}
Authenticate the provider with an Encore auth key from the dashboard, or set ENCORE_AUTH_KEY. See the Terraform provider docs.
Yes for supported resources: the Coming from Terraform guide documents resource-specific workflows for connecting existing RDS, Cloud SQL, S3, GCS, SNS and Pub/Sub. Those workflows do not read or convert Terraform state, so follow the guide for the resource before changing Terraform ownership.
Example: import an existing RDS or Aurora Postgres when creating an environment. Encore connects to the imported database instead of provisioning a new one. Requirements include Postgres (not MySQL), master password managed by AWS Secrets Manager, and the database in a VPC in the same account and region.
When ownership moves to Encore, remove the resource from Terraform without destroying it. Terraform recommends a removed block with destroy = false:
removed {
from = aws_s3_bucket.uploads
lifecycle {
destroy = false
}
}
On GCP, you can import an existing GKE cluster as the compute platform for an environment. Pick Kubernetes: Existing GKE Cluster when creating the environment and supply Project ID, Region and Cluster Name. The Encore platform then deploys into that cluster instead of provisioning a new one.
AWS support for importing an existing Kubernetes cluster is documented as coming soon. Until then, self-host with encore build docker --config=infra-config.json and run the image on the Docker or Kubernetes host you already operate, with resource mappings in the infra config file. See self-host configure infrastructure.
Do not assume AWS EKS import works today; the docs do not claim it yet.
removed with destroy = false for resources Encore now owns. Keep Terraform for everything Encore does not model.Echo (acquired by Coinbase) and Pave Bank are fintech examples on the customers page of teams that keep infrastructure in their own cloud while Encore automates application provisioning.
Keep writing Terraform when:
For application backends in TypeScript or Go, replace the application Terraform layer with infrastructure from code and leave the rest.
Move one ownership boundary at a time. Keep org-level networking, specialized services and company-wide IAM in Terraform. Declare application resources such as databases, queues, buckets, cron jobs and secrets in Encore TypeScript or Go. Connect existing RDS or S3 where the import docs support it, and use the Encore Terraform provider so Terraform can read what Encore manages.
Application Terraform is the HCL that exists mainly so a service can have an RDS instance, SQS queues, S3 buckets, EventBridge rules, secrets and IAM for that service. Org Terraform is shared networking, account scaffolding, SSO and specialized services shared across the company. Infrastructure from code targets the application layer.
Usually, yes. Terraform stays for shared and unsupported infrastructure, and you stop writing it for each new Postgres database, queue, bucket, cron job or secret. The goal is fewer Terraform PRs for application changes, not zero Terraform company-wide.
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.