Docker cookbook · Beginner
Serve a single-page app from a small image
Build a React or Vue app and serve it with correct routing fallback and caching headers.
Approach
Build static files in a Node stage, then serve them from nginx with a fallback to index.html for client
routes, long caching for hashed assets and no caching for index.html.
Solution
FROM node:24-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:1.29-alpine
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=build /app/dist /usr/share/nginx/html
server {
listen 80;
root /usr/share/nginx/html;
location /assets/ { add_header Cache-Control "public, max-age=31536000, immutable"; }
location = /index.html { add_header Cache-Control "no-cache"; }
location / { try_files $uri $uri/ /index.html; }
}
Pitfalls
- Missing
try_filesfallback: deep links return 404 on refresh. - Caching
index.htmlfor a long time, so users keep loading old asset references after deploys. - Baking API URLs into the build per environment; read them from a runtime config file instead.
Variations
- Serve from a CDN or object storage and skip the container entirely.
- Generate
/config.jsat container start from environment variables for runtime configuration.
Further reading: Vite: deploying a static site
Reference: https://vite.dev/guide/static-deploy
More Docker recipes
- Add a healthcheck to an HTTP service
- Build a layered image for a Spring Boot app
- Build a production image for a Node.js service
- Build a production image for a Python web app
- Build, cache and push images in GitHub Actions
- Build one image tag for amd64 and arm64
- Configure nginx from environment variables
- Develop with Compose and live reload