# 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 optional `CIVI_HTTP_AUTH_*` pair if CiviCRM sits behind webserver-level Basic Auth. ## Step 1 — Create the Amplify app 1. Open the Amplify console: - 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. 2. Click **Create new app** → **Host web app**. 3. 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"). 4. Pick the repo `joelbrock/WebForm-mw` and the `main` branch. 5. **App name:** something like `coop-checkin` (this becomes the `*.amplifyapp.com` subdomain). 6. Amplify reads `amplify.yml` automatically; 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** | Do **not** set `NODE_ENV=production` in the Amplify panel. Amplify and Next already set it correctly at runtime; setting it at build time causes `npm ci` to skip devDependencies, which breaks the Tailwind/PostCSS step. The `amplify.yml` in this repo guards against this with `--include=dev`, but it's cleaner not to set it at all. 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 build` produced `.next/`. - SSR functions deployed → API routes and dynamic pages have a Lambda function behind them. - The app is reachable at `https://main..amplifyapp.com`. Smoke-test the deploy: ``` https://main..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=` 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: 1. In the app, **Hosting** → **Custom domains** → **Add domain**. 2. Enter the apex (`fci.coop`) or a subdomain. 3. Amplify suggests DNS records (CNAME / ALIAS). Add them at your DNS provider (Cloudflare, Route53, Namecheap, etc.). 4. 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-mw` repo. - PRs get their own `https://pr-..amplifyapp.com` URL, 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.yaml` is now informational.** It's still in the repo for the Render path; Amplify ignores it. If you commit fully to Amplify and abandon Render, the `render.yaml` and the Render section of `DEPLOYMENT.md` can be removed. ## When something breaks - **Build fails on `npm ci`** → usually a Node version mismatch. The `.nvmrc` pins Node 20; if Amplify's build image doesn't have it, the `amplify.yml` `nvm install` falls back. Open the build log, search for "Node version". - **Build fails on `next build`** → typecheck or lint error. Reproduce locally with `npm 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.ts` validator throws). - **CiviCRM calls fail with 401** → API key or Basic Auth credentials wrong. Re-check `CIVI_API_KEY` and `CIVI_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.md` against the Amplify URL to confirm `/api/preview-link` produces working tokenized URLs end-to-end. - Update any docs / staff runbooks pointing at the old Render URL.