Hi there! I'm Maneshwar. Right now, I’m building LiveAPI, a first-of-its-kind tool that helps you automatically index API endpoints across all your repositories. LiveAPI makes it easier to discover, understand, and interact with APIs in large infrastructures.
When you're deploying a React app, especially a custom build (like SSR output or static HTML), you want a clean, production-grade setup to serve those static files efficiently.
Let’s break down how to do this using a multi-stage Docker build and NGINX, with your own customized static output path.
The Setup
We’ll build a React (or in your case, a Solid/Node hybrid) project inside a Docker container, extract only the built static files, and serve them using NGINX.
Your folder contains a custom SSR build that ends up looking something like this:
src/
ssr/
index.html
public/
styles.css
We want this to be served at http://<host>:3090/signin/
.
Multi-Stage Dockerfile
Stage 1: Build the app
# ========== Stage 1: Build ==========
FROM node:18 AS builder
RUN apt-get update && apt-get install -y git
WORKDIR /onelogin
# Copy everything and install deps
COPY . .
RUN npm install && npm run build
# Move the SSR output to /app/signin
RUN mkdir -p /app/signin && \
cp -r src/ssr/* /app/signin/ && \
mv /app/signin/public/styles.css /app/signin/styles.css || true && \
sed -i 's|/public/styles.css|styles.css|g' /app/signin/index.html
Explanation:
- We clone/build the app and extract only the relevant SSR/static files into
/app/signin
. - A small fix rewrites the CSS path in
index.html
.
Stage 2: Serve with NGINX
# ========== Stage 2: Serve ==========
FROM nginx:alpine
# Copy the static output
COPY --from=builder /app/signin /usr/share/nginx/html/signin
# Custom NGINX config
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 3090
CMD ["nginx", "-g", "daemon off;"]
nginx.conf
events {}
http {
include /etc/nginx/mime.types;
sendfile on;
server {
listen 3090;
location /signin/ {
alias /usr/share/nginx/html/signin/;
index index.html;
}
location / {
return 404;
}
}
}
Key points:
-
alias
serves files from a specific directory. -
/signin/
path maps to static SSR files. - Everything else returns a 404 (strict routing).
Run & Test
Build the image:
docker build -t react-static-server .
Run the container:
docker run -p 3090:3090 react-static-server
Now open:
👉 http://localhost:3090/signin/
You should see your statically served index.html
.
When to Use This
- Deploying custom SSR builds (e.g. from Vite or Rollup).
- Serving a lightweight login screen or microfrontend.
- Isolating parts of your app to different subpaths (
/signin
,/docs
, etc). - You don’t need Node.js running in production—just static files.
Bonus: Lock It Down
Want to add Auth headers, cache control, or redirect behavior? Just extend the nginx.conf
. For example:
add_header Cache-Control "no-store";
Or basic auth, rate limiting, CORS, etc.
Clean Separation of Concerns
This pattern keeps build logic in Node, and static file serving in NGINX. It results in:
- Smaller production images
- Faster cold starts
- Easier horizontal scaling
LiveAPI helps you get all your backend APIs documented in a few minutes.
With LiveAPI, you can generate interactive API docs that allow users to search and execute endpoints directly from the browser.
If you're tired of updating Swagger manually or syncing Postman collections, give it a shot.
Top comments (0)