-
Notifications
You must be signed in to change notification settings - Fork 2.5k
feat(docker): add container support #422
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,106 @@ | ||
| # ============================================================ | ||
| # World Monitor — Docker Build | ||
| # https://github.com/koala73/worldmonitor | ||
| # | ||
| # Architecture: | ||
| # Stage 1 (builder) — installs deps and builds the Vite frontend | ||
| # Stage 2 (runner) — serves the static build with nginx, | ||
| # plus a Node.js sidecar that mirrors the | ||
| # 60+ Vercel Edge Functions locally | ||
| # ============================================================ | ||
|
|
||
| # ── Stage 1: Build ────────────────────────────────────────── | ||
| FROM node:20-alpine AS builder | ||
|
|
||
| WORKDIR /app | ||
|
|
||
| # Install dependencies first (better layer caching) | ||
| COPY package*.json ./ | ||
| RUN npm ci --ignore-scripts | ||
|
|
||
| # Copy source | ||
| COPY . . | ||
|
|
||
| # Build-time environment variables. | ||
| # Override these at build time with --build-arg: | ||
| # docker build --build-arg VITE_VARIANT=world . | ||
| # | ||
| # VITE_VARIANT options: world | tech | finance | happy | ||
| ARG VITE_VARIANT=world | ||
| ARG VITE_MAPTILER_KEY="" | ||
| ARG VITE_MAPBOX_TOKEN="" | ||
| ARG VITE_POSTHOG_KEY="" | ||
|
|
||
| ENV VITE_VARIANT=${VITE_VARIANT} | ||
| ENV VITE_MAPTILER_KEY=${VITE_MAPTILER_KEY} | ||
| ENV VITE_MAPBOX_TOKEN=${VITE_MAPBOX_TOKEN} | ||
| ENV VITE_POSTHOG_KEY=${VITE_POSTHOG_KEY} | ||
|
|
||
| # Compile the sebuf RPC handlers (api/[domain]/v1/[rpc].ts → .js) | ||
| # Required so the local API sidecar can dynamically load them at runtime | ||
| RUN npm run build:sidecar-sebuf | ||
|
|
||
| # Build the Vite SPA | ||
| RUN npm run build | ||
|
|
||
| # ── Stage 2: Runtime ───────────────────────────────────────── | ||
| # Pin to amd64 so the image runs on x86_64 cluster nodes regardless of | ||
| # the build host platform (avoids QEMU emulation for the npm build stage). | ||
| FROM --platform=linux/amd64 node:20-alpine AS runner | ||
|
Comment on lines
+47
to
+49
|
||
|
|
||
| WORKDIR /app | ||
|
|
||
| # Install nginx to serve the static frontend | ||
| RUN apk add --no-cache nginx | ||
|
|
||
| # Copy the built frontend | ||
| COPY --from=builder /app/dist /usr/share/nginx/html | ||
|
|
||
| # Copy the API handlers and their dependencies | ||
| COPY --from=builder /app/api ./api | ||
| COPY --from=builder /app/server ./server | ||
| COPY --from=builder /app/node_modules ./node_modules | ||
| COPY --from=builder /app/package.json ./package.json | ||
|
|
||
| # Copy the local API sidecar (repurposed from Tauri desktop sidecar) | ||
| COPY --from=builder /app/src-tauri/sidecar/local-api-server.mjs ./sidecar/local-api-server.mjs | ||
|
|
||
| # Copy nginx config | ||
| COPY docker/nginx.conf /etc/nginx/http.d/default.conf | ||
|
|
||
| # Copy the entrypoint script | ||
| COPY docker/entrypoint.sh /entrypoint.sh | ||
| RUN chmod +x /entrypoint.sh | ||
|
|
||
| # Runtime secrets — pass these via `docker run -e` or a .env file. | ||
| # The dashboard works without most keys; missing panels simply won't appear. | ||
| # See .env.example in the repo for full descriptions and registration links. | ||
| ENV NODE_ENV=production | ||
| # Port the Node.js API sidecar listens on (must match nginx proxy_pass upstream) | ||
| ENV LOCAL_API_PORT=3001 | ||
|
|
||
| # Runtime API keys (all optional — dashboard degrades gracefully) | ||
| # See .env.example for full descriptions and registration links. | ||
| ENV GROQ_API_KEY="" | ||
| ENV OPENROUTER_API_KEY="" | ||
| ENV UPSTASH_REDIS_REST_URL="" | ||
| ENV UPSTASH_REDIS_REST_TOKEN="" | ||
| ENV FINNHUB_API_KEY="" | ||
| ENV EIA_API_KEY="" | ||
| ENV FRED_API_KEY="" | ||
| ENV WINGBITS_API_KEY="" | ||
| ENV ACLED_ACCESS_TOKEN="" | ||
| ENV CLOUDFLARE_API_TOKEN="" | ||
| ENV NASA_FIRMS_API_KEY="" | ||
| ENV AISSTREAM_API_KEY="" | ||
| ENV OPENSKY_CLIENT_ID="" | ||
| ENV OPENSKY_CLIENT_SECRET="" | ||
| ENV WS_RELAY_URL="" | ||
| ENV RELAY_SHARED_SECRET="" | ||
|
Comment on lines
+82
to
+99
|
||
|
|
||
| # Expose ports: | ||
| # 80 — nginx (frontend + /api proxy) | ||
| # 3001 — Node.js API sidecar (internal; proxied by nginx) | ||
| EXPOSE 80 3001 | ||
|
|
||
| ENTRYPOINT ["/entrypoint.sh"] | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,135 @@ | ||||||
| # ============================================================ | ||||||
| # World Monitor — Kubernetes Manifests (example / template) | ||||||
| # | ||||||
| # Fill in all <PLACEHOLDER> values for your cluster before applying. | ||||||
| # | ||||||
| # Deploy: | ||||||
| # kubectl apply -f deploy/k8s/worldmonitor.yaml | ||||||
| # | ||||||
| # Build & push image first: | ||||||
| # docker buildx build --provenance=false --sbom=false \ | ||||||
| # -t <your-registry>/worldmonitor:latest --load . | ||||||
| # skopeo copy \ | ||||||
| # 'docker-daemon:<your-registry>/worldmonitor:latest' \ | ||||||
| # 'docker://<your-registry>/worldmonitor:latest' | ||||||
| # ============================================================ | ||||||
| --- | ||||||
| # Secret — API keys injected at runtime. | ||||||
| # Populate values before applying, or replace with an ExternalSecret/SealedSecret. | ||||||
| # All keys are optional; missing ones degrade specific panels gracefully. | ||||||
| # See .env.example for full descriptions and registration links. | ||||||
| apiVersion: v1 | ||||||
| kind: Secret | ||||||
| metadata: | ||||||
| name: worldmonitor-env | ||||||
| namespace: <your-namespace> | ||||||
| type: Opaque | ||||||
| stringData: | ||||||
| GROQ_API_KEY: "" | ||||||
| OPENROUTER_API_KEY: "" | ||||||
| UPSTASH_REDIS_REST_URL: "" | ||||||
| UPSTASH_REDIS_REST_TOKEN: "" | ||||||
| FINNHUB_API_KEY: "" | ||||||
| EIA_API_KEY: "" | ||||||
| FRED_API_KEY: "" | ||||||
| WINGBITS_API_KEY: "" | ||||||
| ACLED_ACCESS_TOKEN: "" | ||||||
| CLOUDFLARE_API_TOKEN: "" | ||||||
| NASA_FIRMS_API_KEY: "" | ||||||
| AISSTREAM_API_KEY: "" | ||||||
| OPENSKY_CLIENT_ID: "" | ||||||
| OPENSKY_CLIENT_SECRET: "" | ||||||
| WS_RELAY_URL: "" | ||||||
| RELAY_SHARED_SECRET: "" | ||||||
| --- | ||||||
| apiVersion: apps/v1 | ||||||
| kind: Deployment | ||||||
| metadata: | ||||||
| name: worldmonitor | ||||||
| namespace: <your-namespace> | ||||||
| labels: | ||||||
| app: worldmonitor | ||||||
| spec: | ||||||
| replicas: 1 | ||||||
| selector: | ||||||
| matchLabels: | ||||||
| app: worldmonitor | ||||||
| template: | ||||||
| metadata: | ||||||
| labels: | ||||||
| app: worldmonitor | ||||||
| spec: | ||||||
| containers: | ||||||
| - name: worldmonitor | ||||||
| image: <your-registry>/worldmonitor:latest | ||||||
| imagePullPolicy: Always | ||||||
| ports: | ||||||
| - name: http | ||||||
| containerPort: 80 | ||||||
| protocol: TCP | ||||||
| env: | ||||||
| - name: NODE_ENV | ||||||
| value: "production" | ||||||
| - name: LOCAL_API_PORT | ||||||
| value: "3001" | ||||||
| # Site variant: world | tech | finance | happy | ||||||
| - name: VITE_VARIANT | ||||||
| value: "world" | ||||||
| envFrom: | ||||||
| - secretRef: | ||||||
| name: worldmonitor-env | ||||||
| resources: | ||||||
| requests: | ||||||
| memory: "256Mi" | ||||||
| cpu: "100m" | ||||||
| limits: | ||||||
| memory: "512Mi" | ||||||
|
||||||
| memory: "512Mi" | |
| memory: "1Gi" |
Copilot
AI
Feb 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The HTTPRoute references sectionName: https (line 129) but does not configure TLS or HTTPS-specific routing rules. If the gateway is configured with TLS termination, this is correct, but if not, consider whether http should be used instead. Additionally, there's no configuration for redirecting HTTP to HTTPS, which may be desired for production deployments. Consider documenting the expected gateway TLS configuration or adding an HTTP-to-HTTPS redirect rule.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,26 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #!/bin/sh | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # World Monitor — container entrypoint | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Starts the Node.js API sidecar, then nginx in the foreground. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| set -e | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| API_PORT="${LOCAL_API_PORT:-3001}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "==> Starting World Monitor API sidecar on port ${API_PORT}..." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # The API sidecar: prefer the repurposed Tauri local-api-server (primary), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # then fall back to a compiled server/index.js (legacy), then warn. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [ -f /app/sidecar/local-api-server.mjs ]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| LOCAL_API_PORT="${API_PORT}" node /app/sidecar/local-api-server.mjs & | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| elif [ -f /app/server/index.js ]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| node /app/server/index.js & | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| elif [ -f /app/server/index.ts ]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Dev image fallback only — ts-node is not installed in production images | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| npx ts-node /app/server/index.ts & | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+17
to
+19
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo " [warn] No API sidecar found — API calls will fall back to worldmonitor.app (cloud)." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+7
to
+23
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| API_PORT="${LOCAL_API_PORT:-3001}" | |
| echo "==> Starting World Monitor API sidecar on port ${API_PORT}..." | |
| # The API sidecar: prefer the repurposed Tauri local-api-server (primary), | |
| # then fall back to a compiled server/index.js (legacy), then warn. | |
| if [ -f /app/sidecar/local-api-server.mjs ]; then | |
| LOCAL_API_PORT="${API_PORT}" node /app/sidecar/local-api-server.mjs & | |
| elif [ -f /app/server/index.js ]; then | |
| node /app/server/index.js & | |
| elif [ -f /app/server/index.ts ]; then | |
| # Dev image fallback only — ts-node is not installed in production images | |
| npx ts-node /app/server/index.ts & | |
| else | |
| echo " [warn] No API sidecar found — API calls will fall back to worldmonitor.app (cloud)." | |
| fi | |
| API_PORT="${LOCAL_API_PORT:-3001}" | |
| SIDECAR_STARTED=0 | |
| echo "==> Starting World Monitor API sidecar on port ${API_PORT}..." | |
| # The API sidecar: prefer the repurposed Tauri local-api-server (primary), | |
| # then fall back to a compiled server/index.js (legacy), then warn. | |
| if [ -f /app/sidecar/local-api-server.mjs ]; then | |
| SIDECAR_STARTED=1 | |
| LOCAL_API_PORT="${API_PORT}" node /app/sidecar/local-api-server.mjs & | |
| elif [ -f /app/server/index.js ]; then | |
| SIDECAR_STARTED=1 | |
| node /app/server/index.js & | |
| elif [ -f /app/server/index.ts ]; then | |
| # Dev image fallback only — ts-node is not installed in production images | |
| SIDECAR_STARTED=1 | |
| npx ts-node /app/server/index.ts & | |
| else | |
| echo " [warn] No API sidecar found — API calls will fall back to worldmonitor.app (cloud)." | |
| fi | |
| if [ "$SIDECAR_STARTED" -eq 1 ]; then | |
| echo "==> Waiting for API sidecar to become healthy..." | |
| ATTEMPTS=0 | |
| MAX_ATTEMPTS=30 | |
| until curl -fsS "http://127.0.0.1:${API_PORT}/api/local-service-status" >/dev/null 2>&1; do | |
| ATTEMPTS=$((ATTEMPTS + 1)) | |
| if [ "$ATTEMPTS" -ge "$MAX_ATTEMPTS" ]; then | |
| echo " [error] API sidecar failed to start or become healthy; exiting." | |
| exit 1 | |
| fi | |
| sleep 1 | |
| done | |
| echo " [ok] API sidecar is responding." | |
| fi |
Copilot
AI
Feb 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The fallback checks for non-existent files. According to the Dockerfile, only local-api-server.mjs is copied to /app/sidecar/, and there are no /app/server/index.js or /app/server/index.ts files built or copied. The server directory in the repo contains only TypeScript modules that are not compiled to a standalone server entry point. These fallback branches will never execute successfully and should be removed to avoid confusion.
| # The API sidecar: prefer the repurposed Tauri local-api-server (primary), | |
| # then fall back to a compiled server/index.js (legacy), then warn. | |
| if [ -f /app/sidecar/local-api-server.mjs ]; then | |
| LOCAL_API_PORT="${API_PORT}" node /app/sidecar/local-api-server.mjs & | |
| elif [ -f /app/server/index.js ]; then | |
| node /app/server/index.js & | |
| elif [ -f /app/server/index.ts ]; then | |
| # Dev image fallback only — ts-node is not installed in production images | |
| npx ts-node /app/server/index.ts & | |
| else | |
| echo " [warn] No API sidecar found — API calls will fall back to worldmonitor.app (cloud)." | |
| fi | |
| # The API sidecar: use the repurposed Tauri local-api-server when present, | |
| # otherwise warn and allow API calls to fall back to worldmonitor.app (cloud). | |
| if [ -f /app/sidecar/local-api-server.mjs ]; then | |
| LOCAL_API_PORT="${API_PORT}" node /app/sidecar/local-api-server.mjs & | |
| else | |
| echo " [warn] No API sidecar found — API calls will fall back to worldmonitor.app (cloud)." | |
| fi |
Copilot
AI
Feb 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment on line 25 states "Remove default PID file location issue on alpine" but nginx is started with default configuration. Alpine's nginx package doesn't have a default PID file issue when running in foreground mode with "daemon off". This comment may be misleading or outdated. If there was a specific PID file issue, the fix should be documented or the nginx.conf should explicitly set the pid directive. Consider removing or clarifying this comment.
| # Remove default PID file location issue on alpine | |
| # Run nginx in the foreground (suitable for containerized environments) |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,42 @@ | ||||||||||||||||||||||||||||||||||||||||||
| server { | ||||||||||||||||||||||||||||||||||||||||||
| listen 80; | ||||||||||||||||||||||||||||||||||||||||||
| server_name _; | ||||||||||||||||||||||||||||||||||||||||||
| root /usr/share/nginx/html; | ||||||||||||||||||||||||||||||||||||||||||
| index index.html; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1
to
+6
|
||||||||||||||||||||||||||||||||||||||||||
| # Gzip | ||||||||||||||||||||||||||||||||||||||||||
| gzip on; | ||||||||||||||||||||||||||||||||||||||||||
| gzip_types text/plain text/css application/json application/javascript text/xml application/xml image/svg+xml; | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
| gzip_types text/plain text/css application/json application/javascript text/xml application/xml image/svg+xml; | |
| gzip_types text/plain text/css text/javascript application/json application/javascript application/x-javascript text/xml application/xml image/svg+xml; |
Copilot
AI
Feb 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The static asset caching pattern uses expires 1y and Cache-Control "public, immutable" (lines 14-15), which is appropriate for content-hashed files. However, the regex pattern includes file types that might not always be content-hashed (svg, png, jpg, webp, ico). If any of these assets are not content-hashed by Vite, they could be cached for a year even after updates. Verify that all matched file types are indeed content-hashed in the build output, or split the caching rules into separate blocks for hashed vs non-hashed assets.
| # Cache static assets with content hashes (Vite output) | |
| location ~* \.(js|css|woff2?|ttf|otf|svg|png|jpg|webp|ico|wasm)$ { | |
| expires 1y; | |
| add_header Cache-Control "public, immutable"; | |
| try_files $uri =404; | |
| } | |
| # Cache static assets that include a content hash in the filename (Vite output) | |
| # Matches files like app-abcdef12.js, style.1234abcd.css, icon-0f1e2d3c.svg, etc. | |
| location ~* [.-][0-9a-f]{8}\.(js|css|woff2?|ttf|otf|svg|png|jpg|webp|ico|wasm)$ { | |
| expires 1y; | |
| add_header Cache-Control "public, immutable"; | |
| try_files $uri =404; | |
| } | |
| # Cache image/icon assets that might not be content-hashed with a shorter duration | |
| location ~* \.(svg|png|jpg|webp|ico)$ { | |
| expires 7d; | |
| add_header Cache-Control "public, max-age=604800"; | |
| try_files $uri =404; | |
| } |
Copilot
AI
Feb 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The proxy configuration is missing the X-Forwarded-Proto header, which is important for the Node.js backend to determine whether the original request came over HTTP or HTTPS. This is especially critical when the application is behind a load balancer or ingress controller that terminates TLS. Add the following line after line 25: proxy_set_header X-Forwarded-Proto $scheme;
| proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | |
| proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | |
| proxy_set_header X-Forwarded-Proto $scheme; |
Copilot
AI
Feb 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The proxy_read_timeout is set to 30 seconds, but there's no corresponding proxy_connect_timeout or proxy_send_timeout configured. For production reliability, consider adding proxy_connect_timeout 10s and proxy_send_timeout 30s to handle connection establishment and request transmission separately from response reading. This provides better control over different phases of the proxy lifecycle.
| proxy_set_header Connection ""; | |
| proxy_set_header Connection ""; | |
| proxy_connect_timeout 10s; | |
| proxy_send_timeout 30s; |
Copilot
AI
Feb 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The PostHog analytics proxy at /ingest/ forwards requests to an external HTTPS endpoint (us.i.posthog.com) without configuring SSL verification or timeout settings. Consider adding proxy_ssl_verify on and a reasonable proxy_connect_timeout (e.g., 10s) for production reliability. Also consider whether you want to expose this analytics endpoint in production containers, as it could be used by third parties if the container is publicly accessible.
| proxy_ssl_server_name on; | |
| proxy_ssl_server_name on; | |
| proxy_ssl_verify on; | |
| proxy_connect_timeout 10s; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The npm ci --ignore-scripts flag (line 19) prevents lifecycle scripts from running during installation. While this is a security best practice for Docker builds, ensure that none of the dependencies require postinstall scripts for proper functioning. If any dependencies need setup scripts, they should be run explicitly after line 19 or the --ignore-scripts flag should be removed with appropriate security review.