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", });
import "encore.dev/storage/sqldb" var siteDB = sqldb.NewDatabase("site", sqldb.DatabaseConfig{ 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:

PlaceHoldsHow you set it
CodeLogical resources and application-level propertiesArguments to resource constructors in the application
Encore Cloud configurationDeployment properties for each environmentThe Encore Cloud dashboard and, where supported, the cloud provider console
Infra configResource mappings and runtime connectivity for self-hosted deploymentsAn 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:

SettingPlace
Database name, migrations directoryCode
Cloud provider, database engine, and capacityEncore Cloud configuration
Storage, backups, and networkingEncore Cloud configuration or cloud provider console
Host, credentials, and TLSInfra 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" });
import "encore.dev/storage/sqldb" var SiteDB = sqldb.NewDatabase("site", sqldb.DatabaseConfig{Migrations: "./migrations"})

Schema changes go in ./migrations as numbered SQL files, applied on deploy. See Databases.

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 });
import "encore.dev/pubsub" var SiteAdded = pubsub.NewTopic[*Site]("site-added", pubsub.TopicConfig{ DeliveryGuarantee: pubsub.AtLeastOnce, }) var _ = pubsub.NewSubscription(SiteAdded, "check-site", pubsub.SubscriptionConfig[*Site]{Handler: DoCheck}, )

Encore derives the queue, the subscription wiring and the IAM policy from those two declarations. See Pub/Sub.

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, });
import "encore.dev/storage/objects" var ProfilePictures = objects.NewBucket("profile-pictures", objects.BucketConfig{ Versioned: false, })

See Object storage.

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, });
import "encore.dev/cron" var _ = cron.NewJob("welcome-email", cron.JobConfig{ Title: "Send welcome emails", Every: 2 * cron.Hour, Endpoint: SendWelcomeEmail, })

The target is a reference to the endpoint function rather than an ARN. See Cron jobs.

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");
var secrets struct { GitHubAPIToken string }

Values are set per environment with encore secret set. See Secrets.

Values are set per environment with encore secret set. See Secrets.

Concept mapping

Declaring infrastructure

TerraformEncore
resource blockResource constructor in application code
provider blockCloud provider is chosen per environment in the dashboard
Resource reference (aws_sns_topic.x.arn)Language-level import of the resource object
depends_onUsually derived from resource usage and service relationships; no direct equivalent
ModulesNo direct equivalent; services organize application code and resources
count and for_eachNo equivalent for Encore resources; declarations must be statically discoverable

Configuration and state

TerraformEncore
Input variables and localsNo single equivalent; infrastructure settings, secrets, and other application configuration are handled separately
output and var plumbingNot required; the consumer imports the object
tfvars per environmentNo single equivalent; Encore Cloud settings, secrets, and other application configuration are handled separately
WorkspacesEnvironments are the closest operational concept, but their lifecycle and configuration differ
State fileThe application model is derived from source; Encore Cloud separately maintains infrastructure state
Remote state backendNo direct equivalent for platform-managed environments; Encore Cloud maintains its infrastructure records

Running it

TerraformEncore
terraform planNo direct equivalent; builds validate the application model and Encore Cloud provides infrastructure change workflows
terraform applyNo direct equivalent; an Encore Cloud deployment provisions supported changes, while self-hosted infrastructure remains in your IaC workflow
terraform importNo direct equivalent; use the resource-specific Encore import workflow to connect supported existing infrastructure
data source for an Encore resourceEncore Terraform Provider data sources
External migration stepmigrations/ directory, applied on deploy
Hand-written IAM policiesDerived 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.