Files
WebForm-mw/AMPLIFY_DEPLOY.md
T
Joel Brock 97222afe58 AMPLIFY_DEPLOY: document Gen 2 Environment variables vs Secrets split
Amplify Gen 2's console has two separate pages for runtime config:
Environment variables (plaintext) and Secrets (SSM Parameter Store
SecureString). The earlier 'mark as Secret with eye icon' wording was
Gen 1; in Gen 2 you choose by which page you add the value on.

Step 2 rewritten:
- Brief explanation of both pages and how they're injected (both end
  up as plain env vars in the app, same name).
- Combined variable table with a Page column showing where each value
  lives.
- Rule-of-thumb: anything that would let someone impersonate the app
  to CiviCRM or bypass a gate is a Secret; hostnames/usernames are
  fine in Environment variables.
- Callout reminding not to duplicate names across both pages
  (precedence undefined in Gen 2).
2026-05-19 17:28:24 -07:00

8.3 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 and Secrets

Amplify Gen 2 splits configuration across two separate pages in the console sidebar (under Hosting):

  • Environment variables — plaintext-at-rest, intended for non-sensitive config. Readable by anyone with amplify:GetApp permission on the AWS account.
  • Secrets — values stored in AWS Systems Manager Parameter Store as SecureString, encrypted at rest. Injected into the build/runtime as normal environment variables under the same name, so application code doesn't need to know the difference.

Add each variable to the page indicated below:

Variable Page Notes
CIVI_BASE_URL Environment variables e.g. https://crm.fci.coop
CIVI_API_KEY Secrets Required
CIVI_SITE_KEY Secrets Required
CIVI_HTTP_AUTH_USER Environment variables Only if CiviCRM has webserver-level Basic Auth in front of it
CIVI_HTTP_AUTH_PASS Secrets Only if above set
HEALTH_TOKEN Secrets Optional; gates /api/health in production
PREVIEW_ADMIN_TOKEN Secrets Optional; gates /api/preview-link

Rule of thumb: if leaking the value would let someone impersonate the app to CiviCRM, or bypass a gate, it goes in Secrets. Hostnames and usernames are fine in plaintext Environment variables.

Don't set NODE_ENV=production in either page. 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.

Don't duplicate names across both pages. If the same variable name appears in both Environment variables and Secrets, Amplify's precedence is undefined in Gen 2.

Click through, 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.