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

187 lines
8.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 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 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, **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 515 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-<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.