Docker & Deploy

For a comprehensive installation and deployment guide with step-by-step instructions, see Installation & Deployment.

This page provides supplementary technical details about the Docker build process and CI/CD integration.

Docker Images

Two Dockerfiles live under docker/:

  • docker/Dockerfile — Multi-stage build used end-to-end: a Node.js builder produces the Vite dist/ output, and an nginx:alpine runner serves it. Use this when you want a single docker build to produce a final image.

  • docker/Dockerfile.simple — Skips the Node.js builder and expects a pre-built dist/ passed via ARG DIST_DIR=dist. Used by CI, where the dist/ artifact is produced in an earlier job and reused across multi-arch image builds.

Build stages (docker/Dockerfile)

Stage Base Image Purpose

builder

node:20-bookworm-slim

npm ci, npm run build, then delete dist/*/.map to strip source maps

runner

nginx:alpine

Serve dist/ as static assets, reverse-proxy /configs-service/, expose ports 80 and 443

The runner image copies dist/ to /usr/share/nginx/html, installs both nginx templates (nginx.conf.template and nginx-http-only.conf.template) and entrypoint.sh, and runs nginx -g 'daemon off;' as its default command.

Build and Run (example)

# Build the full image from source
docker build -f docker/Dockerfile -t ft-configs-ui:local .

# Run and serve on http://localhost/
docker run --rm -p 80:80 \
  -e BACKEND_URL="http://ft-configs-service:8080" \
  ft-configs-ui:local

For CI flows that already produced a dist/ artifact:

docker build \
  -f docker/Dockerfile.simple \
  --build-arg DIST_DIR=dist \
  -t ft-configs-ui:ci .

Runtime Config Injection

At container start, docker/ops/entrypoint.sh:

  1. Writes /usr/share/nginx/html/runtime-config.js with a fixed window.RUNTIME_CONFIG = { "API_URL": "/configs-service" }; (the path must match the nginx /configs-service/ location and the backend context-path).

  2. Resolves BACKEND_URL (default http://ft-configs-service:8080) and METRICS_ALLOW_IP (default 0.0.0.0/0).

  3. Detects whether /etc/nginx/ssl/tls.crt and /etc/nginx/ssl/tls.key both exist.

    • Both present → use nginx.conf.template (HTTP on 80 + HTTPS on 443).

    • Either missing → use nginx-http-only.conf.template (HTTP on 80 only).

  4. Runs envsubst on the chosen template, writing the result to /etc/nginx/nginx.conf.

  5. Execs nginx -g 'daemon off;'.

The HTTP-only template is intended for Kubernetes deployments where TLS is terminated upstream by an Ingress controller.

The same image therefore targets any environment without a rebuild; only runtime-config.js and the chosen nginx template change at startup.

Runtime environment variables

Variable Description Default

BACKEND_URL

Upstream used by nginx proxy_pass for /configs-service/

http://ft-configs-service:8080

METRICS_ALLOW_IP

Extra CIDR added to the allow-list of /nginx_status

0.0.0.0/0

Environment variables

CONFIGS_API_URL

Backend base URL at runtime (recommended for containers).

NEXT_PUBLIC_API_URL

Backend base URL (fallback; may be baked at build-time depending on deployment).

NEXT_PUBLIC_ENVIRONMENT / NEXT_PUBLIC_ENV

Environment label shown in the UI (example: DEV, STAGE, PROD).

NEXT_PUBLIC_APP_VERSION / NEXT_PUBLIC_VERSION

Version string shown in the UI (optional).

HTTPS Configuration

HTTPS_ENABLED

Enable HTTPS server on port 3443. When true, the application requires valid HTTPS_CERT_PATH and HTTPS_KEY_PATH to start.
Default: false
Example: true

HTTPS_CERT_PATH

Path to TLS certificate file (PEM format) inside the container.
Default: /certs/tls.crt
Example: /certs/tls.crt

HTTPS_KEY_PATH

Path to TLS private key file (PEM format) inside the container.
Default: /certs/tls.key
Example: /certs/tls.key

NODE_EXTRA_CA_CERTS

Path to additional CA certificate bundle (PEM format) for trusting corporate or self-signed CAs. Applied globally to all outbound HTTPS connections. Must be set before the Node.js process starts.
Default: (not set)
Example: /certs/ca.pem

nginx configuration (summary)

The nginx templates define the following locations (shared between SSL and HTTP-only variants):

Location Purpose

/configs-service/

proxy_pass ${BACKEND_URL} with resolver 127.0.0.11 (Docker DNS, so nginx can start before the backend is resolvable). Forwards Host, X-Real-IP, X-Forwarded-For, X-Forwarded-Proto. Timeouts: connect/send/read = 60s.

/health

return 200 '{"status":"ok"}' with access_log off — liveness probe.

/ready

proxy_pass ${BACKEND_URL}/actuator/health with 5s timeouts — readiness probe.

~* \.map$

return 404 — source maps are always blocked.

`~* (index\.html

runtime-config\.js)$`

Cache-Control: no-store, no-cache, must-revalidate, expires -1.

`~* \.(js

css

png

jpg

jpeg

gif

ico

svg

woff2?)$`

Cache-Control: public, immutable, expires 1y.

/

try_files $uri $uri/ /index.html — SPA fallback.

/nginx_status

Global defaults: worker_processes 1, worker_connections 1024, client_max_body_size 50M, gzip on, sendfile on, keepalive_timeout 65.

CI/CD Notes (repository evidence)

GitHub Actions workflows under .github/workflows/ build and push Docker images and Vite dist/ artifacts:

  • build-test-upload.yml — runs lint/typecheck/test/build on feature branches and uploads dist/ as the vite-build artifact.

  • ft-configs-ui-build-release.yml — on main/dev, runs the same gates, then downloads the vite-build artifact and produces a multi-arch image (linux/amd64, linux/arm64) from docker/Dockerfile.simple, pushed to Harbor.

  • build-test-qa-deploy.yml — manual QA release: reuses the build artifact, bumps release_notes.txt / version.json, creates an annotated tag v${VERSION}-b${BUILD}, and publishes multi-arch images tagged :latest and :v${VERSION}-b${BUILD}.

Dockerfile.simple is the image used by release workflows — it consumes the dist/ produced by the earlier build job, which keeps the container-build job small and cache-friendly.

Use these files as the source of truth for how the image is produced in your environment.

Serving Path

The current deployment serves the UI at the root path (/). SPA routes are handled by the nginx try_files fallback to index.html; backend calls are proxied through /configs-service/.