Deployment¶
Docker Build¶
The project uses a two-stage Docker build with node:22-alpine.
Stage 1 — Builder:
FROM node:22-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
ENV NEXT_TELEMETRY_DISABLED=1
ARG NEXT_PUBLIC_START_URL
ARG NEXT_PUBLIC_SIGNUP_URL
ARG NEXT_PUBLIC_SITE_URL
RUN npm run build
Stage 2 — Runner:
FROM node:22-alpine
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
EXPOSE 3000
CMD ["node", "server.js"]
The build relies on Next.js output: 'standalone' mode, which produces a
self-contained server.js in .next/standalone. The final image does
not include node_modules — all required dependencies are bundled by
Next.js into the standalone output.
Next.js telemetry is disabled in both stages.
NEXT_PUBLIC_* environment variables are baked into the client bundle at
build time via Docker ARG declarations. Pass them with --build-arg.
Build the image:
docker build -t flexgalaxy-www \
--build-arg NEXT_PUBLIC_SITE_URL=https://flexgalaxy.ai \
--build-arg NEXT_PUBLIC_START_URL=https://console.flexgalaxy.ai/start/ \
--build-arg NEXT_PUBLIC_SIGNUP_URL="https://console.flexgalaxy.ai/start/?register=true" \
.
Run the container:
docker run -p 3000:3000 \
-e STRAPI_URL=https://cms.flexgalaxy.ai \
flexgalaxy-www
Next.js Configuration¶
next.config.ts contains two notable settings:
Sass: suppresses the
legacy-js-apideprecation warning, required for@carbon/stylesSCSS compatibilityImages: allows
<Image>to load fromhttp://localhost:1337/uploads/**(local Strapi). For production, add the Strapi production hostname toremotePatterns
There is no i18n configuration in next.config.ts — locale routing is
handled entirely by the middleware.
Environment Variables¶
Variable |
Default |
Description |
|---|---|---|
|
|
Strapi CMS base URL |
|
|
Public site URL (SEO meta) |
|
|
SSR fallback for Login button |
|
|
SSR fallback for Sign Up CTA |
|
|
Set to |
|
|
Disables Next.js telemetry |
NEXT_PUBLIC_* variables are embedded at build time by Next.js. In the
Docker build, they are injected via ARG / ENV pairs. At runtime, only
server-side variables (STRAPI_URL) can be overridden.
Note
Since March 2026, Login, Sign Up, and Admin URLs are derived
at runtime from the browser hostname via useConsoleUrls() in
src/lib/consoleUrl.ts. The NEXT_PUBLIC_START_URL and
NEXT_PUBLIC_SIGNUP_URL variables now serve only as SSR fallbacks
displayed during server-side rendering before client-side hydration
computes the correct domain-specific URL.
This means a single Docker image works on both .ai (international)
and .com (China) deployments without requiring a separate build per
partition. See Architecture for details on the derivation logic.
Production Build¶
To build for production without Docker:
npm run build
npm start
The build command produces optimised output in .next/. The start
command serves the production build on port 3000.
CI/CD¶
Documentation is built and deployed via the deploy-docs.yml GitHub
Actions workflow. It checks out SiriusVoyager/infra-common alongside
the project to access the shared Makefile, build script, and Carbon theme.
The documentation build uses infra-common/docs/build-i18n.sh, which
reads the project name from conf.py and produces multilingual HTML
output.
AWS Infrastructure¶
The www application is deployed as a tenant on DotID’s EKS cluster in the
DotID AWS account. Terraform configuration lives in terraform/.
Dual-partition deployment:
Partition |
Domain |
DNS Provider |
AWS Region |
|---|---|---|---|
International |
|
GoDaddy |
ap-northeast-1 |
China |
|
Aliyun (Alibaba Cloud) |
cn-north-1 |
AWS accounts (China partition):
Account |
AWS Account ID |
Profile |
Use |
|---|---|---|---|
DotID CN |
|
|
Keycloak, EKS cluster, www, OrbitCast |
Infra CN |
|
|
NovaBell, api-gateway (SSO via cn-northwest-1) |
IaC State CN |
|
|
Terraform state (S3 + DynamoDB) only |
AWS accounts (International partition):
Deploy with the deploy.sh script:
# International (prod)
./deploy.sh prod --partition intl
# China (prod)
./deploy.sh prod --partition cn
# Dev
./deploy.sh dev
Shared infrastructure from DotID (via remote state):
VPC, EKS cluster (compute)
ACM wildcard certificate for TLS termination
Managed by www Terraform:
ECR repository for the www Docker image
Kubernetes namespace (
www), deployment, service, and configmapNLB (internet-facing) with TLS via the shared ACM cert
CloudFront CDN distribution (intl partition) for static asset caching
DNS is managed externally, NOT in AWS Route53:
flexgalaxy.ai — GoDaddy (international partition)
flexgalaxy.com — Aliyun / Alibaba Cloud DNS (China partition)
After terraform apply, create CNAME records manually in the respective
DNS provider. Terraform outputs www_nlb_hostname and
cdn_acm_validation_records to help with DNS setup.
CDN¶
International partition uses CloudFront (global) with managed cache
policies and ACM certificates. Configuration is in cdn.tf, controlled
by enable_cdn = true in prod-intl.tfvars.
China partition uses CloudFront China (*.cloudfront.cn), which has
significant limitations compared to the global service:
aws_cloudfront_cache_policy— not supported in China regionsaws_cloudfront_origin_request_policy— not supported in China regionsACM certificates — not supported for CloudFront China; must use IAM-uploaded server certificates instead
The current cdn.tf only supports the international partition. China
partition CDN requires a separate implementation using legacy
forwarded_values blocks and IAM certificate references.
Current production (legacy):
The existing www.flexgalaxy.com CDN is a CloudFront China distribution
(d341g2nskxpja3.cloudfront.cn) managed in the fg-ai-ops account
(148308503730). It was created by the old Terraform codebase and uses:
IAM certificate
ASCASFB7AFSZC7VW5X6UCLegacy
ForwardedValues(forwards all cookies + query strings, no headers)Single default cache behavior (no separate static asset caching)
HTTP/1.1 only, IPv6 disabled
Origin: NLB in cn-northwest-1, port 8080
CDN AWS account (China):
Account |
AWS Account ID |
Profile |
Use |
|---|---|---|---|
fg-ai-ops |
|
|
CloudFront China distributions |
This account is a member of the AWS CN organization (management account
633349536424 / sr-awscn-admin).
TODO: China CDN Migration¶
Migrate the CN partition CDN from the legacy setup to Terraform-managed configuration. This work should be done separately from the initial www CN deployment.
1. Make cdn.tf partition-aware:
Add
is_china_partitionvariable (bool, defaultfalse)CN mode: use
forwarded_valuesblocks instead of cache/origin-request policy resourcesCN mode: use
var.cdn_iam_certificate_idinstead of ACMCN mode: use
http2(nothttp2and3)CN mode: enable IPv6
Add separate
/_next/static/*and/images/*cache behaviors with aggressive TTLs (missing from legacy config)Add
Accept-Languageheader forwarding in default behavior for i18n
2. Consolidate docs.flexgalaxy.com into www.flexgalaxy.com/docs/legacy:
The docs.flexgalaxy.com distribution (EXIZH9G3CAXFK in fg-ai-ops)
currently serves:
Default: NLB port 8081 (main docs site)
/en-us/organization/latest/api/*: S3 originfws-cn-api-reference-docs.s3.cn-northwest-1.amazonaws.com.cn(path:/fws-global-published/organization/website)
Migration steps:
Add S3 origin to the www distribution for
/docs/legacy/*pathAdd a Next.js rewrite rule or CloudFront behavior to route
/docs/legacy/*to the S3 bucketVerify content is accessible at new path
Disable and delete the
docs.flexgalaxy.comdistributionRemove
docs.flexgalaxy.comCNAME from Aliyun DNSAdd redirect from
docs.flexgalaxy.com→www.flexgalaxy.com/docs/legacyduring transition period
3. Deploy new CN CDN:
Set
enable_cdn = trueandis_china_partition = trueinprod-cn.tfvarsSet
cdn_iam_certificate_id = "ASCASFB7AFSZC7VW5X6UC"(reuse existing IAM cert, or upload a new one)Deploy with
./deploy.sh prod --partition cnUpdate
www.flexgalaxy.comCNAME in Aliyun to point to the new distributionDisable and delete the legacy distribution (
E38L5N5NHTWU77)