Why separate environments#
An environment is an isolated place where you deploy a version of your system for a specific purpose: development, testing, staging or production. Keeping them apart lets you try changes without putting users at risk, grant different permissions per stage, and know what each stage costs.
Many deployment platforms model an environment as three decisions: a cloud account or set of credentials, a region, and a domain. Creating one provisions a dedicated network (a VPC with several subnets). The same idea applies when you build it yourself on AWS.
The real question is not whether to separate but where to draw the line: separate accounts, separate VPCs inside one account, or just names and tags. This guide explains what each option isolates and how to handle naming, configuration, secrets and promotion between environments.
Separate accounts or separate VPCs#
AWS strongly recommends separating environments at the account level. The Well-Architected security pillar calls the account a strong isolation boundary for security, billing and access. A VPC isolates the network, but within one account you still share IAM, service quotas and the bill.
| Criterion | One account, one VPC per environment | One account per environment |
|---|---|---|
| Permissions | Needs fine-grained IAM policies; one mistake can touch production | The account boundary separates by default; you enter through distinct roles |
| Quotas and API limits | Shared by every environment | Each account has its own quotas |
| Cost | Requires disciplined tagging to split the bill | Each account's bill already is the environment's cost |
| Blast radius | An incident or bad policy can hit everything | Contained in the affected account |
| Initial complexity | Lower | Higher, but automated with AWS Organizations or Control Tower |
For a prototype, one account with separate VPCs may be enough. As soon as real customer data is involved, production belongs in its own account.
Documentation: AWS Well-Architected · SEC01-BP01 ↗ · AWS · Benefits of using multiple accounts ↗ · AWS · What is Amazon VPC ↗
Organize accounts with OUs and guardrails#
AWS Organizations groups accounts into organizational units (OUs). AWS's multi-account whitepaper proposes, among others, a Workloads OU for business workload accounts, covering both production and non-production, and a Sandbox OU for experimentation with limited access to production.
On top of OUs you apply service control policies (SCPs): maximum permission limits no identity in those accounts can exceed, such as forbidding audit logging from being disabled or restricting allowed regions. If you would rather not build it by hand, AWS Control Tower sets up a multi-account landing zone with these controls.
- Workloads / Prod: production accounts, with minimal human access.
- Workloads / Non-production: dev and staging, with more freedom but the same baseline guardrails.
- Sandbox: personal or team experiments, with no path to production data.
Documentation: AWS · Recommended OUs and accounts ↗ · AWS Organizations · SCPs ↗ · AWS Control Tower ↗
One codebase, one state per environment#
Environments are only comparable if the same code creates them. The usual Terraform or OpenTofu pattern is a shared configuration, one variables file per environment, and a separate state backend for each, with its own credentials.
# providers.tf: one configuration shared by every environment
variable "environment" { type = string } # dev | staging | prod
variable "account_id" { type = string } # one AWS account per environment
variable "region" { type = string }
provider "aws" {
region = var.region
# Terraform reaches the environment's account by assuming a deploy role
assume_role {
role_arn = "arn:aws:iam::${var.account_id}:role/deploy"
}
# Every resource is tagged with its environment
default_tags {
tags = {
Environment = var.environment
ManagedBy = "terraform"
}
}
}
# Usage: one variables file and one backend (state) per environment
# terraform init -backend-config=envs/prod.backend.hcl
# terraform apply -var-file=envs/prod.tfvarsHashiCorp notes that CLI workspaces share the same backend and are not a suitable isolation mechanism when each deployment needs different credentials and access controls. For dev and production in separate accounts, use separate backends.
Documentation: Terraform · Workspaces ↗ · Terraform · Backend configuration ↗ · Terraform · AWS provider ↗
Network, naming and domains#
- Give each environment its own VPC and reserve CIDR ranges that do not overlap: if you ever need to connect them with VPC peering, AWS does not allow peering between VPCs with matching or overlapping ranges.
- Use one domain or subdomain per environment, such as dev.yourdomain.com, staging.yourdomain.com and app.yourdomain.com, so no two environments share certificates or DNS records.
- Pick a short, stable naming convention (dev, stg, prd) and use it for accounts, resources and aliases.
- Tag everything with at least Environment and Owner; tags let you filter costs and apply policies.
Documentation: AWS · VPC peering and overlapping CIDRs ↗ · AWS · Tagging best practices ↗
Per-environment configuration and secrets#
Code and artifacts should be identical across environments; configuration is what changes. Keep parameters in a per-environment hierarchy in AWS Systems Manager Parameter Store, and credentials in AWS Secrets Manager, which can also rotate them.
# Same hierarchy, different prefix per environment
# /myapp/dev/DATABASE_URL /myapp/prod/DATABASE_URL
# /myapp/dev/FEATURE_FLAGS /myapp/prod/FEATURE_FLAGS
aws ssm get-parameters-by-path \
--path /myapp/prod/ \
--with-decryption \
--query 'Parameters[].Name'Never copy production secrets into dev to debug. If you need realistic data, generate synthetic or anonymized data.
Documentation: Parameter Store · Hierarchies ↗ · AWS CLI · get-parameters-by-path ↗ · AWS Secrets Manager ↗
Promote changes between environments#
Build once and promote the same artifact: the same container image (ideally referenced by digest) or package moves from dev to staging and from staging to production. If you rebuild at every step, what you tested is not exactly what you ship.
In GitHub Actions, environments attach secrets and protection rules to each stage, such as required reviewers or allowed branches. Note that on Free, Pro and Team plans those rules (reviewers, wait timers) are only available for public repositories. Combined with OIDC, the production AWS role can accept only tokens issued for the prod environment.
Documentation: GitHub · Deployment environments ↗ · GitHub · OIDC with AWS ↗
Checklist#
- Production in its own AWS account, inside an OU with guardrails.
- Same infrastructure code; one variables file and one state per environment.
- Non-overlapping CIDRs and one domain or subdomain per environment.
- Parameters and secrets separated per environment, never copied from production.
- A single artifact promoted across environments, with approval before production.
Sources and scope
Documentation checked on September 25, 2026. Examples and decision criteria are editorial proposals; adapt them to your application's contract and validate them in an authorized test environment.
- AWS Well-Architected · SEC01-BP01 ↗
- AWS · Benefits of using multiple accounts ↗
- AWS · What is Amazon VPC ↗
- AWS · Recommended OUs and accounts ↗
- AWS Organizations · SCPs ↗
- AWS Control Tower ↗
- Terraform · Workspaces ↗
- Terraform · Backend configuration ↗
- Terraform · AWS provider ↗
- AWS · VPC peering and overlapping CIDRs ↗
- AWS · Tagging best practices ↗
- Parameter Store · Hierarchies ↗
- AWS CLI · get-parameters-by-path ↗
- AWS Secrets Manager ↗
- GitHub · Deployment environments ↗
- GitHub · OIDC with AWS ↗
Compare cloud options
Review pricing, limits, conditions and sources for each option (in Spanish).
Open comparison