GitHub workflow using AWS copilot deploy

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-123 or preview-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 job to 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.com via 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.