Guide · AWS Amplify · PR Previews

Amplify PR Preview Environments for MCP Server Dashboards — Auto-Provisioning, Branch Patterns, and Cleanup

Amplify Hosting's PR preview feature automatically builds and deploys a full copy of your MCP server dashboard for every pull request — no extra CI/CD configuration required. When a PR is opened against a tracked branch, Amplify creates a temporary branch deployment at a URL following the pattern https://pr-{pr-number}.{branch-name}.{app-id}.amplifyapp.com. GitHub, GitLab, and Bitbucket each get a status check posted back to the PR with a direct link. When the PR is merged or closed, Amplify deletes the preview deployment and frees the underlying resources. For MCP server teams iterating on admin dashboards, this means every PR gets an instantly accessible preview that shares the production build pipeline — same environment variables, same build commands, same Node.js version. The critical configuration decision: previews can share the production backend (staging API endpoint) or get their own isolated backend per PR (useful for data-mutation-heavy features but requires Gen 2 sandbox per branch).

TL;DR

Enable PR previews via aws amplify update-branch --enable-pull-request-preview true or in the Amplify console under the branch settings. Previews deploy automatically when a PR targets the configured branch. The preview URL is https://pr-{pr-number}.{branch-name}.{app-id}.amplifyapp.com. Set REACT_APP_API_URL at the branch level to point previews at a staging backend rather than production. For Gen 2 backends, set --pull-request-environment-name to deploy a separate backend stack per PR — Amplify names it {branch}-pr-{pr-number} and deletes it when the PR closes.

Enabling and configuring PR previews

PR previews are controlled at the branch level. When enabled, Amplify watches for incoming PRs targeting that branch and creates a sub-branch deployment for each one.

# Enable PR previews on the main branch
aws amplify update-branch \
  --app-id $APP_ID \
  --branch-name main \
  --enable-pull-request-preview \
  --pull-request-environment-name staging   # for Gen 2: backend env name per PR

# For Gen 1 (no Gen 2 backend) — set shared backend endpoint via env var:
aws amplify update-branch \
  --app-id $APP_ID \
  --branch-name main \
  --enable-pull-request-preview \
  --environment-variables \
    REACT_APP_API_URL=https://staging-api.alivemcp.com,\
    REACT_APP_ENV=preview

# List active preview branches (auto-created per PR)
aws amplify list-branches \
  --app-id $APP_ID \
  --query 'branches[?contains(branchName, `pr-`)].[branchName,stage,activeJobSummary.status]' \
  --output table

The --pull-request-environment-name parameter is only relevant for Gen 2 backends — it names the Amplify environment that npx ampx pipeline-deploy deploys to for the PR branch. For Gen 1 or frontend-only apps, this parameter has no effect. Leaving it unset means all PR previews share the same backend environment variable values as the base branch.

PR preview URL pattern and status checks

Amplify posts a build status to the PR in GitHub, GitLab, or Bitbucket as the preview is building. Once complete, the status check includes a link to the preview URL. The URL is deterministic — you can share it before the build completes because the PR number and app ID are known at PR creation time.

# Preview URL pattern:
# https://pr-{pr-number}.{source-branch-name}.{app-id}.amplifyapp.com
#
# Example — PR #47 from branch "feat/dark-mode" targeting "main" on app d1abc23def456:
# https://pr-47.main.d1abc23def456.amplifyapp.com
#
# Note: the branch name in the URL is the TARGET branch (main), not the source branch

# Get the preview URL programmatically
aws amplify get-branch \
  --app-id $APP_ID \
  --branch-name pr-47 \
  --query 'branch.{url:thumbnailUrl,status:activeJobSummary.status}'

# Wait for the preview build to complete (in CI)
while true; do
  STATUS=$(aws amplify get-branch \
    --app-id $APP_ID \
    --branch-name pr-47 \
    --query 'branch.activeJobSummary.status' \
    --output text)
  echo "Build status: $STATUS"
  [ "$STATUS" = "SUCCEED" ] && break
  [ "$STATUS" = "FAILED" ] && exit 1
  sleep 30
done
echo "Preview ready: https://pr-47.main.${APP_ID}.amplifyapp.com"

Amplify posts the GitHub status check using the Git provider token configured for the app. If you connected Amplify via the GitHub App (recommended), status checks are posted automatically. If you used a personal access token, the token must have repo:status scope for status checks to appear on PRs.

Branch pattern matching for auto-build

Beyond PR previews, Amplify supports auto-building any branch that matches a pattern. This is useful for feature branch previews that aren't PRs yet — e.g., any branch starting with feat/ automatically gets its own deployment.

# Enable auto-build for all feat/* branches
aws amplify update-app \
  --app-id $APP_ID \
  --auto-branch-creation-config \
    enableAutoBranchCreation=true,\
    autoBranchCreationPatterns="['feat/*','fix/*','chore/*']",\
    enableAutoBuild=true,\
    buildSpec='version: 1\nfrontend:\n  phases:\n    preBuild:\n      commands:\n        - nvm use 20\n        - npm ci\n    build:\n      commands:\n        - npm run build\n  artifacts:\n    baseDirectory: dist\n    files:\n      - '\''**/*'\''',\
    environmentVariables='REACT_APP_ENV=preview,REACT_APP_API_URL=https://staging-api.alivemcp.com'

# List all auto-created branches
aws amplify list-branches \
  --app-id $APP_ID \
  --query 'branches[?enableAutoBuild==`true`].[branchName,stage]' \
  --output table

Auto-branch creation creates a new Amplify branch resource when a matching Git branch is pushed. Auto-branch deletion removes the Amplify branch resource when the Git branch is deleted. Both are configured separately — you can enable creation without enabling deletion (useful for auditing which branches existed). The autoBranchCreationPatterns list uses glob patterns; feat/* matches one level deep (feat/dark-mode) but not feat/ui/dark-mode.

Environment variable isolation for previews

By default, PR preview branches inherit the environment variables from the base branch. If your base branch points to production APIs, previews will also hit production — which is usually wrong for features under development. Override environment variables specifically for preview deployments using the AMPLIFY_DIFF_DEPLOY mechanism or by setting variables at the app level with branch overrides.

# Strategy 1: Set API URL at app level, override per branch
# App level (all branches, including previews, inherit this)
aws amplify update-app \
  --app-id $APP_ID \
  --environment-variables \
    REACT_APP_API_URL=https://staging-api.alivemcp.com

# Override for production branch only
aws amplify update-branch \
  --app-id $APP_ID \
  --branch-name main \
  --environment-variables \
    REACT_APP_API_URL=https://api.alivemcp.com

# Strategy 2: Use a feature flag variable and detect environment in code
# In React component:
const apiUrl = process.env.REACT_APP_ENV === 'production'
  ? 'https://api.alivemcp.com'
  : 'https://staging-api.alivemcp.com';

# Strategy 3: Read the Amplify branch name at build time to set the URL
# In amplify.yml build phase:
# build:
#   commands:
#     - |
#       if [ "$AWS_BRANCH" = "main" ]; then
#         export REACT_APP_API_URL=https://api.alivemcp.com
#       else
#         export REACT_APP_API_URL=https://staging-api.alivemcp.com
#       fi
#     - npm run build

Strategy 3 using $AWS_BRANCH is the most flexible — you can set different endpoints for main, develop, and all PR preview branches without managing per-branch CLI calls. Amplify injects AWS_BRANCH, AWS_APP_ID, AWS_REGION, and AWS_JOB_ID as build-time environment variables automatically.

PR preview cleanup and lifecycle

Amplify deletes PR preview deployments automatically when the PR is merged or closed. The underlying S3 objects, CloudFront behaviors, and branch resources are all cleaned up. For Gen 2 backends with per-PR backend deployments, Amplify also runs cdk destroy on the PR-specific backend stack.

# PR preview lifecycle:
# 1. PR opened → Amplify creates branch "pr-{number}" and starts build
# 2. Commits pushed to PR → Amplify rebuilds the preview branch
# 3. PR merged or closed → Amplify deletes the preview branch and resources

# Manual cleanup if auto-delete doesn't trigger (e.g., after force-close):
aws amplify delete-branch \
  --app-id $APP_ID \
  --branch-name pr-47

# List stale preview branches older than 30 days
CUTOFF=$(date -d '30 days ago' +%Y-%m-%dT%H:%M:%SZ)
aws amplify list-branches \
  --app-id $APP_ID \
  --query "branches[?contains(branchName, 'pr-') && createTime < '$CUTOFF'].[branchName,createTime]" \
  --output table

Auto-delete requires that Amplify received the PR close webhook from your Git provider. If the Git provider's webhook delivery failed (network issue, timeout), the preview branch persists and accumulates charges (~$0.01/GB-month for S3, negligible for CloudFront). Set up a weekly cron job to list and delete stale pr-* branches using the AWS CLI query above.

Failure modes reference

FailureSymptomFix
Preview not created on PR openPR opened but no Amplify build triggered and no status check postedVerify enablePullRequestPreview is true on the target branch; check the GitHub App installation has access to the repository; check Amplify app incoming webhooks in the console
Preview points to production APIPR preview mutations affect production dataSet REACT_APP_API_URL at app level to staging; override to production only on the main branch
Preview URL 404 after build succeedsBuild status shows SUCCEED but preview URL returns 404Check the branch's artifacts.baseDirectory in amplify.yml — if it points to an empty or wrong directory, Amplify deploys nothing
Status check not posted to GitHub PRAmplify builds the preview but the GitHub PR shows no status checkGitHub App installation requires checks:write and statuses:write permissions; reconnect the Amplify app via the GitHub App (not OAuth)
Stale preview branches not deletedOld pr-* branches accumulating in Amplify console after PRs closedWebhook delivery failure — run periodic cleanup with aws amplify delete-branch for pr-* branches whose corresponding PRs are closed; use GitHub API to cross-reference open PR numbers
Gen 2 backend stack not deleted on PR closeCDK stacks for PR branches remain in CloudFormation after PR mergeVerify pullRequestEnvironmentName is set correctly; Gen 2 backend cleanup requires Amplify to have cloudformation:DeleteStack permission on the PR stack