Setting up preview deployments using AWS Copilot involves dynamically creating isolated, short-lived environments (per-branch or per-PR) to preview changes before merging into mainline. You can achieve this with Copilot's environment and service management, combined with database schema cloning, CI/CD automation, and networking setup.
Below is a step-by-step guide to implement preview deployments with AWS Copilot, fulfilling your requirements:
π§ Prerequisites
- AWS account with proper IAM permissions
- AWS CLI and Copilot CLI installed
- An existing Copilot application (created via
copilot init) - A relational DB (e.g., RDS or Aurora) with schemas managed per environment
- CI/CD pipeline (e.g., GitHub Actions, CodePipeline, CircleCI)
β Step-by-Step: Preview Deployments with AWS Copilot
1. Set Up Base Environments
Your main environments (e.g., prod, staging) should already exist.
copilot env init --name staging
copilot env init --name prod
These will be your stable environments.
2. Use Git Branches to Trigger Preview Deployments
Use your CI/CD system to detect PRs or feature branches and trigger preview deployment.
Idea: When a PR is opened (e.g.,
feature/login), create a temporary Copilot environment named after the branch (e.g.,pr-123orpreview-login), deploy the service, and tear it down when the PR closes.
3. Clone the Database Schema
Since you want isolated preview environments, you must clone a schema from an existing environment (e.g., staging) to avoid impacting production.
Example: Clone schema using SQL
CREATE SCHEMA pr_123_clone;
-- Copy data (if needed)
-- Use logic like: CREATE TABLE ... AS SELECT or tools like pg_dump (PostgreSQL) / mysqldump (MySQL)
You can automate this in CI:
# In CI script (PostgreSQL example)
PGPASSWORD="$DB_PASSWORD" psql -h $DB_HOST -U $DB_USER -c "CREATE SCHEMA IF NOT EXISTS ${SCHEMA_NAME};"
PGPASSWORD="$DB_PASSWORD" pg_dump -h $DB_HOST -U $DB_USER --schema=staging_schema --data-only | \
sed "s/SCHEMA staging_schema/SCHEMA ${SCHEMA_NAME}/g" | \
psql -h $DB_HOST -U $DB_USER -d $DB_NAME
Replace logic based on your DB engine (MySQL, PostgreSQL, etc.)
π‘ Pro Tip: Use timestamps or PR numbers for schema names (e.g., pr_45, feat_login_2024) to ensure uniqueness.
4. Update DB Environment Variable to Point to New Schema
Your service should use an environment variable (e.g., DB_SCHEMA) to determine which schema to use.
Update serviceβs manifest to use schema from env var:
In your copilot/service/manifest.yml (or per-environment override):
count: 1
variables:
DB_SCHEMA: pr_default_clone # will be overridden per environment
During CI, override it dynamically:
When deploying a preview environment, you can use --env-vars in Copilot:
copilot svc deploy \
--name my-frontend \
--env preview-pr-45 \
--env-vars DB_SCHEMA=pr_45_clone
Or use environment-specific manifest overrides.
5. Create a Preview Environment via CI
In your CI pipeline, create a temporary environment named after the PR.
# Example in CI (GitHub Actions Run)
PR_NUMBER=45
ENV_NAME=preview-pr-${PR_NUMBER}
# Create environment (uses shared VPC or creates new one; use --import-vpc if needed)
copilot env init \
--name $ENV_NAME \
--profile default \
--default-config \
--import-vpc-id vpc-xxx \
--import-private-subnets "subnet-xxx,subnet-yyy" \
--import-public-subnets "subnet-zzz,subnet-www"
π‘ Tip: Use imported VPC and subnets so environments share networking but are logically isolated.
6. Build & Push Service Image from CI
Build and tag Docker image with commit or PR-specific tag.
TAG=pr-${PR_NUMBER}-${GIT_SHA:0:7}
docker build -t $AWS_ACCOUNT.dkr.ecr.$AWS_REGION.amazonaws.com/my-service:$TAG .
aws ecr get-login-password | docker login --username AWS --password-stdin $AWS_ACCOUNT.dkr.ecr.$AWS_REGION.amazonaws.com
docker push $AWS_ACCOUNT.dkr.ecr.$AWS_REGION.amazonaws.com/my-service:$TAG
7. Deploy ECS Service Using Copilot
Use Copilot to deploy the service to the preview environment using the newly built image.
copilot svc deploy \
--name my-frontend \
--env $ENV_NAME \
--image $AWS_ACCOUNT.dkr.ecr.$AWS_REGION.amazonaws.com/my-service:$TAG \
--env-vars DB_SCHEMA=pr_${PR_NUMBER}_clone
This will create an ECS task, service, load balancer listener, etc., in the preview environment.
8. Apply Database Migrations
Run migration script inside the DB targeting the new schema.
You can do this in CI after schema creation and before or after service deployment.
Example:
# In CI
docker run --rm \
-e DATABASE_URL="postgresql://$DB_USER:$DB_PASS@$DB_HOST/$DB_NAME?schema=pr_${PR_NUMBER}_clone" \
my-migration-image:latest \
migrate up
Ensure migration tools (e.g., Flyway, Liquibase, Prisma, Django migrate) support schema-level targeting.
9. Make Service Accessible via Internet
By default, if your service is a Load Balanced Web Service, Copilot creates an ALB and assigns a publicly accessible URL.
Get the URL
copilot svc status --name my-frontend --env $ENV_NAME
# OR parse the endpoint from the manifest or CloudFormation output
Alternatively, use the --output json flag or get ALB DNS from AWS CLI:
aws cloudformation describe-stacks \
--stack-name $APPNAME-$ENV_NAME-svc-my-frontend \
--query 'Stacks[0].Outputs[?OutputKey==`URL`].OutputValue' \
--output text
Then output this URL in CI logs or post it as a comment on the PR.
10. Automate Cleanup on PR Closure
When a PR is closed, tear down the environment and drop the schema.
In CI (on PR close):
PR_NUMBER=45
ENV_NAME=preview-pr-${PR_NUMBER}
SCHEMA_NAME=pr_${PR_NUMBER}_clone
# Delete Copilot environment
copilot env delete --name $ENV_NAME --yes
# Drop DB schema
PGPASSWORD="$DB_PASSWORD" psql -h $DB_HOST -U $DB_USER -d $DB_NAME -c "DROP SCHEMA IF EXISTS $SCHEMA_NAME CASCADE;"
You may want to delay deletion (e.g., 24h) for debugging; use cleanup scripts or Lambda + EventBridge.
π Security & Cost Considerations
- Short-lived: Limit preview environment lifetime.
- Cost: Environments cost money (ALB, ECS, etc.). Consider using Fargate Spot or low-CPU tasks.
- Access Control: Restrict access to preview URLs (e.g., basic auth, AWS Cognito, or IP allowlist).
- DB Credentials: Use temporary secrets via AWS Secrets Manager or IAM DB auth.
β Final Workflow Summary
| Step | Action |
|---|---|
| 1 | PR opened in GitHub |
| 2 | CI detects PR and sets PR_NUMBER |
| 3 | Create DB schema: pr_<num>_clone |
| 4 | Run migrations on new schema |
| 5 | Build Docker image with pr-<num> tag |
| 6 | Create Copilot preview environment |
| 7 | Deploy service with DB_SCHEMA=pr_<num>_clone |
| 8 | Output public URL to PR comment |
| 9 | On PR close: delete env & drop schema |
π Bonus: Optional Enhancements
- Use
copilot jobto run migration as one-off task. - Use environmental manifest overrides to customize CPU, scaling, etc., for preview.
- Use App Runner or Lambda instead of ECS for cheaper previews (but less control).
- Add custom domain:
pr45.preview.yourapp.comvia Route 53.
π Resources
By combining Copilot's declarative infrastructure with CI-driven environment creation and DB schema isolation, you can achieve robust, secure preview deployments that mirror production and enable safe testing.