Coming from Terraform
How Terraform concepts map to Encore
Terraform configuration describes the desired infrastructure and its provider-specific settings. In Encore, the TypeScript or Go SDK declarations describe the APIs, services, and infrastructure used by the backend. They form an application model, while deployment-specific properties are configured separately for each environment.
For migration paths that also apply to Pulumi, OpenTofu, and AWS CDK, see Coming from an IaC tool.
How it works
The following examples declare a Postgres database in Terraform and Encore.
resource "aws_db_instance" "site" {
identifier = "site"
engine = "postgres"
instance_class = "db.t4g.medium"
allocated_storage = 20
storage_type = "gp3"
db_name = "site"
username = var.db_username
password = var.db_password
db_subnet_group_name = aws_db_subnet_group.main.name
vpc_security_group_ids = [aws_security_group.db.id]
backup_retention_period = 7
}
import { SQLDatabase } from "encore.dev/storage/sqldb";
const siteDB = new SQLDatabase("site", {
migrations: "./migrations",
});
The Terraform example defines the AWS implementation and its deployment properties. The Encore declaration defines the logical database name and its migration directory. Encore Cloud configures the managed database separately for each environment through infrastructure configuration.
Where each setting is configured
Configuration is divided across three locations:
| Place | Holds | How you set it |
|---|---|---|
| Code | Logical resources and application-level properties | Arguments to resource constructors in the application |
| Encore Cloud configuration | Deployment properties for each environment | The Encore Cloud dashboard and, where supported, the cloud provider console |
| Infra config | Resource mappings and runtime connectivity for self-hosted deployments | An infra.config.json file passed to encore build docker --config |
The infra config file applies to self-hosted deployments. Encore Cloud manages the corresponding configuration for environments deployed through the platform.
For a SQL database:
| Setting | Place |
|---|---|
| Database name, migrations directory | Code |
| Cloud provider, database engine, and capacity | Encore Cloud configuration |
| Storage, backups, and networking | Encore Cloud configuration or cloud provider console |
| Host, credentials, and TLS | Infra config for self-hosted deployments |
Converting Terraform resources
Databases
resource "aws_db_instance" "site" { ... }
import { SQLDatabase } from "encore.dev/storage/sqldb";
const SiteDB = new SQLDatabase("site", { migrations: "./migrations" });
Schema changes go in ./migrations as numbered SQL files, applied on deploy.
See Databases.
Pub/Sub topics and subscriptions
A topic, its queue, the subscription, and the queue policy granting the topic write access:
resource "aws_sns_topic" "site_added" { name = "site-added" }
resource "aws_sqs_queue" "check_site" { name = "check-site" }
resource "aws_sns_topic_subscription" "check_site" {
topic_arn = aws_sns_topic.site_added.arn
protocol = "sqs"
endpoint = aws_sqs_queue.check_site.arn
}
resource "aws_sqs_queue_policy" "check_site" { ... }
import { Topic, Subscription } from "encore.dev/pubsub";
export const SiteAdded = new Topic<Site>("site-added", {
deliveryGuarantee: "at-least-once",
});
const _ = new Subscription(SiteAdded, "check-site", { handler: doCheck });
Encore derives the queue, the subscription wiring and the IAM policy from those two declarations. See Pub/Sub.
Object storage
resource "aws_s3_bucket" "profile_pictures" { bucket = "profile-pictures" }
resource "aws_s3_bucket_versioning" "profile_pictures" { ... }
import { Bucket } from "encore.dev/storage/objects";
export const profilePictures = new Bucket("profile-pictures", {
versioned: false,
});
See Object storage.
Scheduled tasks
resource "aws_cloudwatch_event_rule" "welcome_email" {
schedule_expression = "rate(2 hours)"
}
resource "aws_cloudwatch_event_target" "welcome_email" { ... }
resource "aws_lambda_permission" "welcome_email" { ... }
import { CronJob } from "encore.dev/cron";
const _ = new CronJob("welcome-email", {
title: "Send welcome emails",
every: "2h",
endpoint: sendWelcomeEmail,
});
The target is a reference to the endpoint function rather than an ARN. See Cron jobs.
Secrets
resource "aws_secretsmanager_secret" "github_token" { name = "GitHubAPIToken" }
import { secret } from "encore.dev/config";
const githubToken = secret("GitHubAPIToken");
Values are set per environment with encore secret set. See
Secrets.
Concept mapping
Declaring infrastructure
| Terraform | Encore |
|---|---|
resource block | Resource constructor in application code |
provider block | Cloud provider is chosen per environment in the dashboard |
Resource reference (aws_sns_topic.x.arn) | Language-level import of the resource object |
depends_on | Usually derived from resource usage and service relationships; no direct equivalent |
| Modules | No direct equivalent; services organize application code and resources |
count and for_each | No equivalent for Encore resources; declarations must be statically discoverable |
Configuration and state
| Terraform | Encore |
|---|---|
Input variables and locals | No single equivalent; infrastructure settings, secrets, and other application configuration are handled separately |
output and var plumbing | Not required; the consumer imports the object |
tfvars per environment | No single equivalent; Encore Cloud settings, secrets, and other application configuration are handled separately |
| Workspaces | Environments are the closest operational concept, but their lifecycle and configuration differ |
| State file | The application model is derived from source; Encore Cloud separately maintains infrastructure state |
| Remote state backend | No direct equivalent for platform-managed environments; Encore Cloud maintains its infrastructure records |
Running it
| Terraform | Encore |
|---|---|
terraform plan | No direct equivalent; builds validate the application model and Encore Cloud provides infrastructure change workflows |
terraform apply | No direct equivalent; an Encore Cloud deployment provisions supported changes, while self-hosted infrastructure remains in your IaC workflow |
terraform import | No direct equivalent; use the resource-specific Encore import workflow to connect supported existing infrastructure |
data source for an Encore resource | Encore Terraform Provider data sources |
| External migration step | migrations/ directory, applied on deploy |
| Hand-written IAM policies | Derived from supported resource usage in the application model |
Using Encore with Terraform
Encore and Terraform can be used together. Encore can manage supported application resources while Terraform manages other infrastructure, such as DNS, organization-level networking, and third-party providers.
Pointing Encore at resources you already have
Encore provides resource-specific workflows for connecting existing RDS, Cloud SQL, S3, GCS, SNS, and Pub/Sub resources. At the environment level, it can also deploy into an existing GKE cluster or GCP project.
These workflows do not read or convert Terraform state. Follow the guide for the resource before changing its Terraform ownership.
If ownership moves to Encore, remove the resource from Terraform without
destroying it. Terraform recommends replacing its resource block with a
removed block,
then reviewing and applying the plan. destroy = false goes inside a nested
lifecycle block:
removed {
from = aws_s3_bucket.uploads
lifecycle {
destroy = false
}
}
Reading Encore resources from Terraform
The Encore Terraform Provider exposes
data sources such as encore_database, encore_cache, and
encore_pubsub_topic, so Terraform can reference infrastructure Encore created:
data "encore_pubsub_topic" "topic" {
name = "my-topic"
env = "my-env"
}
resource "aws_iot_topic_rule" "rule" {
name = "my-rule"
sql = "SELECT * FROM 'my-topic'"
sns {
message_format = "RAW"
role_arn = aws_iam_role.role.arn
target_arn = data.encore_pubsub_topic.topic.aws_sns.arn
}
}
Self-hosting and binding manually
Build a Docker image with encore build docker and pass an
infra.config.json that maps each resource in the derived model to a physical
resource you provision separately:
encore build docker myapp:latest --config ./infra.config.json
Running the build without --config prints the resources the file has to
account for. See Configure
infrastructure.
Workflow differences
Encore validates statically discoverable resource declarations during the
build and configures their deployment per environment. There is no direct
equivalent to terraform plan, count, or for_each. Keep unsupported,
shared, and dynamically generated infrastructure in Terraform and migrate one
ownership boundary at a time.
Why configuration is split this way
Application-level properties stay with the resource declaration. For a SQL database, these include its logical name and migration directory. Deployment properties such as the database engine and capacity are configured per environment.
Resource declarations can be used locally and in AWS or GCP environments. New environments select their own infrastructure settings, including preview environments. Capacity and other deployment settings can be managed through infrastructure configuration without changing the resource declaration.
The static declarations also let Encore determine which resources exist and which services use them. Encore uses that information to derive IAM policies and validate supported resource usage. For agentic workflows, AI infrastructure provisioning describes the available controls, and infrastructure namespaces provide isolated local state.
See Encore Application Model for details on how declarations are analyzed.