From 4600a7b0a96fd80bd522f5409c903215d75a1add Mon Sep 17 00:00:00 2001 From: Ryan Malloy Date: Fri, 13 Feb 2026 03:37:14 -0700 Subject: [PATCH] Production deployment: SSR frontend, Caddy path routing Update frontend Dockerfile prod stage from Caddy static to Node.js running Astro SSR server. Configure caddy-docker-proxy labels to route /api/* to backend and everything else to frontend on the same domain. Support empty PUBLIC_API_URL for same-origin API calls. --- docker-compose.prod.yml | 6 +++++- frontend/Dockerfile | 29 ++++++++++++++--------------- frontend/src/lib/api.ts | 7 ++++--- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 69fb556..5620bf6 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -9,6 +9,10 @@ services: networks: - default - caddy + labels: + caddy: "${SPICEBOOK_DOMAIN:-spicebook.localhost}" + caddy.@api.path: "/api/* /health /docs /openapi.json /redoc" + caddy.reverse_proxy_0: "@api {{upstreams 8000}}" frontend: build: @@ -22,7 +26,7 @@ services: - caddy labels: caddy: "${SPICEBOOK_DOMAIN:-spicebook.localhost}" - caddy.reverse_proxy: "{{upstreams 4321}}" + caddy.reverse_proxy_1: "{{upstreams 4321}}" networks: caddy: diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 4c9e84f..69f6057 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -26,23 +26,22 @@ RUN npm run build # ── Prod stage ─────────────────────────────────────────────────────── -FROM caddy:2-alpine AS prod +FROM node:20-slim AS prod -COPY --from=build /app/dist /srv +WORKDIR /app -# Simple Caddyfile for serving the static Astro build -RUN echo ':4321 {\n\ - root * /srv\n\ - encode gzip\n\ - try_files {path} {path}/index.html /index.html\n\ - file_server\n\ - header {\n\ - X-Content-Type-Options nosniff\n\ - X-Frame-Options DENY\n\ - Referrer-Policy strict-origin-when-cross-origin\n\ - }\n\ -}' > /etc/caddy/Caddyfile +# Copy the built Astro SSR server +COPY --from=build /app/dist ./dist +COPY --from=build /app/node_modules ./node_modules +COPY --from=build /app/package.json ./ + +# Run as non-root +RUN useradd --create-home --shell /bin/bash astro +USER astro + +ENV HOST=0.0.0.0 +ENV PORT=4321 EXPOSE 4321 -CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile"] +CMD ["node", "dist/server/entry.mjs"] diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index 3f08171..30d59f2 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -8,9 +8,10 @@ import type { } from './types'; const API_BASE = (() => { - // In SSR context, import.meta.env may not have PUBLIC_ vars populated the same way. - // Prefer the PUBLIC_ env var, fall back to localhost for dev. - if (typeof import.meta !== 'undefined' && import.meta.env?.PUBLIC_API_URL) { + // PUBLIC_API_URL controls where API requests go: + // - Dev (local): 'http://localhost:8099' + // - Production: '' (empty = same origin, Caddy routes /api/* to backend) + if (typeof import.meta !== 'undefined' && import.meta.env?.PUBLIC_API_URL != null) { return import.meta.env.PUBLIC_API_URL; } return 'http://localhost:8099';