Changelog
Template version 4 → 5. What changed, and what is still broken.
resolving stage…
Shipped in v5
feature
PR previews get their own subdomain
PR stages now deploy to ${stage}.${DOMAIN_NAME}, so pull request #32 lands on pr-32.0xci.anasnadaf.com. Previously the template explicitly skipped domain assignment for anything that wasn't production, so previews only ever got a CloudFront URL. Production still uses the bare domain, unchanged.
feature
DNS provider is selectable — Route 53 or Cloudflare
A DNS_PROVIDER secret picks the DNS backend, defaulting to Route 53 so existing installs behave identically. Setting it to cloudflare swaps in Cloudflare's adapter and registers that provider in the config, so a domain managed on Cloudflare no longer has to be delegated to Route 53. Hosting is untouched — this makes DNS vendor-agnostic, not compute.
bugfix
Deploy URLs on custom domains are captured correctly
The workflow scraped sst deploy output with a pattern that only matched cloudfront.net, amazonaws.com and on.aws. Any deploy to a custom domain was reported as “no deployment URL found”, so the PR comment never showed a link. This predates the subdomain work and affected production custom domains too. The domain is regex-escaped so its dots match literally.
bugfix
Teardown can actually clean up DNS
The teardown workflow passed only REPO_NAME, so sst remove re-evaluated the config without any DNS configuration. With Cloudflare selected it could not authenticate to delete the records it created. Both workflows now expose the same configuration at job level — which also matters because sst install runs as a separate step and has to resolve the same providers the deploy step needs.
bugfix
The injector actually ships the update
TEMPLATE_VERSION in the GitHub App is compared against the version in each repo's workflow to decide whether to re-inject. It was left at 4 while the templates moved to 5, so the injector would have short-circuited and never delivered any of this to existing repos. Found while implementing, not while planning.
bugfix
Zone lookup has the credentials it needs
When CLOUDFLARE_ZONE_ID is omitted, SST resolves the zone by calling the zones API filtered by account — which needs an account ID the workflows never passed, plus a token with Zone Read on top of DNS Write. Passing the zone ID directly skips that path entirely and is the recommended setup.
Known issues & trade-offs
known issue
Teardown orphans one ACM certificate per PR
The IAM role from cfn-oidc gets its ACM access via CloudFrontFullAccess, which grants acm:RequestCertificate and acm:DescribeCertificate but not acm:DeleteCertificate. Deploys work; teardown leaves the certificate behind. That compounds against the ACM quota below.
known issue
One certificate per preview, and AWS caps issuance
Each distinct subdomain gets its own ACM certificate, and AWS allows 20 new public certificates per rolling 7 days per account. A repo with heavy PR volume can hit that ceiling. A pre-provisioned wildcard certificate would avoid it, at the cost of extra setup.
known issue
No containerized service support
The config only detects JavaScript frontend frameworks and falls back to a static site. A repo whose deployable is a Dockerfile — a Go service, a Python API — has no path here yet. That needs a Fargate construct, wider IAM scope, and a story for multi-service repos.
trade-off
Cloudflare means storing a long-lived token
AWS access uses OIDC, so nothing is stored. Cloudflare has no equivalent federation, so opting in means putting an API token in GitHub secrets — a real departure from the “no stored credentials” posture. Scope it to DNS Write on the single zone, and leave the records unproxied or certificate validation breaks.