Files
WebForm-mw/AMPLIFY_DEPLOY.md
T
Joel Brock e90d007513 Amplify: install devDependencies during build (Tailwind/PostCSS need them)
@tailwindcss/postcss lives in devDependencies (along with the rest of
the PostCSS toolchain). When NODE_ENV=production is set in the Amplify
build environment, npm ci skips devDependencies — and next build then
fails resolving @tailwindcss/postcss while compiling globals.css.

amplify.yml now passes --include=dev to npm ci so the build always
installs everything regardless of NODE_ENV. AMPLIFY_DEPLOY.md updated
to warn against setting NODE_ENV=production in the Amplify env vars
panel — it's redundant (Next sets it correctly) and an easy footgun.
2026-05-19 17:13:13 -07:00

7.6 KiB
Raw Blame History

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: 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.
  2. Click Create new appHost 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 46 minutes (later builds are 23 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.<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:

  1. In the app, HostingCustom domainsAdd 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 515 minutes for validation and certificate issuance.

The *.amplifyapp.com URL keeps working alongside the custom domain.

Amplify can build a preview environment for every pull request:

  • HostingPreviews → enable for the joelbrock/WebForm-mw repo.
  • PRs get their own https://pr-<num>.<random>.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 5001500ms 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.