- amplify.yml: build spec (preBuild npm ci with offline cache, build next build, artifacts at .next/**, cache node_modules/.next-cache/.npm). - .nvmrc: pin Node 20 so Amplify uses the same runtime as local. - AMPLIFY_DEPLOY.md: first-time walkthrough covering AWS-side setup (create app, connect GitHub via OAuth/App, branch/auto-detect), environment variable table with secret-flag guidance, smoke-test via /healthz and /api/health, optional custom domain + per-PR previews, cost estimate, and operational notes (cold starts, no static egress IPs, CloudWatch logs, secret rotation). - README deploy section: now points at both AMPLIFY_DEPLOY.md and the existing DEPLOYMENT.md (Render).
7.3 KiB
Deploying to AWS Amplify Hosting
First-time walkthrough for shipping this app on AWS Amplify Hosting. Amplify
auto-detects Next.js, runs the build, and routes SSR pages + API routes
through managed Lambda behind a CloudFront CDN. The amplify.yml and
.nvmrc in this repo are the only pieces Amplify needs from the codebase.
Before you start
- An AWS account with billing enabled. Amplify Hosting has a free tier (1,000 build minutes + 15 GB served + 100 GB-hours of SSR Lambda per month for the first year) — typical traffic for this app stays comfortably inside it.
- GitHub access to
joelbrock/WebForm-mw. Amplify connects via OAuth or a GitHub App; either works. - The three required CiviCRM env vars in hand:
CIVI_BASE_URL,CIVI_API_KEY,CIVI_SITE_KEY. Plus the optionalCIVI_HTTP_AUTH_*pair if CiviCRM sits behind webserver-level Basic Auth.
Step 1 — Create the Amplify app
- Open the Amplify console: https://console.aws.amazon.com/amplify/
- Pick a region near you / near CiviCRM.
us-east-1(N. Virginia) is the default and has the lowest cold-start latency for most setups.
- Pick a region near you / near CiviCRM.
- Click Create new app → Host web app.
- Choose GitHub as the source. Authorize Amplify if prompted.
The first time, AWS will install a GitHub App on your account; grant
it access just to
joelbrock/WebForm-mw(not "all repos"). - Pick the repo
joelbrock/WebForm-mwand themainbranch. - App name: something like
coop-checkin(this becomes the*.amplifyapp.comsubdomain). - Amplify reads
amplify.ymlautomatically; you do not need to edit the build spec on this screen. Click Next.
Step 2 — Environment variables
On the environment-variables screen, add the following. Required ones first:
| Variable | Value |
|---|---|
CIVI_BASE_URL |
e.g. https://crm.fci.coop |
CIVI_API_KEY |
mark as Secret (eye icon on the value field) |
CIVI_SITE_KEY |
mark as Secret |
NODE_ENV |
production |
Optional, only if relevant:
| Variable | When |
|---|---|
CIVI_HTTP_AUTH_USER |
CiviCRM has webserver-level Basic Auth in front of it |
CIVI_HTTP_AUTH_PASS |
same — mark Secret |
HEALTH_TOKEN |
gates /api/health in production — mark Secret |
PREVIEW_ADMIN_TOKEN |
gates /api/preview-link — mark Secret |
"Secret" values are encrypted at rest in AWS Systems Manager Parameter
Store and only injected into the build/runtime environment. They are not
visible in logs or to anyone without amplify:* IAM permissions.
Click Next, review, Save and deploy.
Step 3 — First build
The first build takes about 4–6 minutes (later builds are 2–3 with cache). You can watch progress in the Hosting environments view. When it completes, three things are confirmed:
- The build succeeded →
next buildproduced.next/. - SSR functions deployed → API routes and dynamic pages have a Lambda function behind them.
- The app is reachable at
https://main.<random>.amplifyapp.com.
Smoke-test the deploy:
https://main.<random>.amplifyapp.com/healthz
Should return 200 OK (lightweight platform check, doesn't hit CiviCRM).
If you set HEALTH_TOKEN, the richer diagnostic at
/api/health?token=<value> will probe the CiviCRM connection itself —
useful to confirm env vars are wired correctly.
Step 4 — Custom domain (optional)
Amplify can attach a custom domain (e.g. check-in.fci.coop) with
auto-issued TLS in a few minutes:
- In the app, Hosting → Custom domains → Add domain.
- Enter the apex (
fci.coop) or a subdomain. - Amplify suggests DNS records (CNAME / ALIAS). Add them at your DNS provider (Cloudflare, Route53, Namecheap, etc.).
- Wait 5–15 minutes for validation and certificate issuance.
The *.amplifyapp.com URL keeps working alongside the custom domain.
Step 5 — Per-PR previews (optional, recommended)
Amplify can build a preview environment for every pull request:
- Hosting → Previews → enable for the
joelbrock/WebForm-mwrepo. - PRs get their own
https://pr-<num>.<random>.amplifyapp.comURL, posted as a comment on the PR. - Previews inherit the main branch's environment variables unless you override on the preview branch.
Useful for reviewing form changes against live CiviCRM DEV before merging.
What it'll cost
Realistic estimate for this app's expected traffic (a few hundred form/report loads per month, very modest API throughput):
- Hosting (CDN + static): pennies. Free tier covers it.
- SSR Lambda: ~$0.05–$0.50/month depending on traffic. Each form load triggers a couple of API calls into CiviCRM via Lambda; each call is a short-lived invocation.
- Build minutes: free tier covers up to ~300 small builds/month.
Expect <$5/month total at launch traffic. If usage grows significantly, the costs scale roughly linearly with Lambda invocations.
Things to know going in
- Cold starts. First request after a quiet period adds 500–1500ms of Lambda init. Subsequent requests reuse the warm container. For an internal-use form this is fine; users see the loading state during init.
- Outbound IPs are not static. Amplify SSR Lambda functions egress through AWS-managed IPs that change. If CiviCRM has an IP allowlist on its API, this won't work out of the box — you'd need a VPC + NAT Gateway + Elastic IP setup (significantly more complex). Most CiviCRM auth uses API keys, not IP allowlisting, so this usually isn't an issue.
- No Render-style background workers. Amplify is request/response only. Anything cron-shaped needs EventBridge + a separate Lambda. The current app doesn't have background jobs, so this doesn't apply.
- Logs live in CloudWatch. Each SSR function has its own log group; click into the function from the Amplify app view to jump to logs. Retention defaults to "never expire" — switch to 30 days unless you need more.
- Secrets rotate manually. No auto-rotation. To rotate
CIVI_API_KEY, update it in Amplify env vars → trigger a redeploy. render.yamlis now informational. It's still in the repo for the Render path; Amplify ignores it. If you commit fully to Amplify and abandon Render, therender.yamland the Render section ofDEPLOYMENT.mdcan be removed.
When something breaks
- Build fails on
npm ci→ usually a Node version mismatch. The.nvmrcpins Node 20; if Amplify's build image doesn't have it, theamplify.ymlnvm installfalls back. Open the build log, search for "Node version". - Build fails on
next build→ typecheck or lint error. Reproduce locally withnpm run build. - Site builds but routes 503 → SSR Lambda misconfig. Check the
function's CloudWatch logs for the actual error. Most common cause:
missing required env var (the app refuses to boot in production stub
mode and the
lib/env.tsvalidator throws). - CiviCRM calls fail with 401 → API key or Basic Auth credentials
wrong. Re-check
CIVI_API_KEYandCIVI_HTTP_AUTH_*in the Amplify env-vars panel; redeploy after changes.
After the first successful deploy
- Bookmark the Amplify app URL and the CloudWatch log group.
- Run the email/link smoke test from
EMAIL_DELIVERY.mdagainst the Amplify URL to confirm/api/preview-linkproduces working tokenized URLs end-to-end. - Update any docs / staff runbooks pointing at the old Render URL.