Vite’s Docker build, when done right, can be surprisingly lightweight and fast because it leverages native ES modules and avoids bundling for development.
Let’s see Vite in action. Imagine we have a simple React app.
# vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
export default defineConfig({
plugins: [react()],
build: {
outDir: path.resolve(__dirname, 'dist'), // Explicitly set output directory
emptyOutDir: true, // Ensure the output directory is clean
},
});
And our Dockerfile:
# Stage 1: Build the application
FROM node:20-alpine as builder
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN npm install -g pnpm # Install pnpm globally
RUN pnpm install
COPY . .
RUN pnpm run build
# Stage 2: Serve the application
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
When you build this, pnpm install pulls dependencies, pnpm run build transpiles your code into optimized static assets within the dist directory. Nginx then serves these static files. The magic is that the node image with all its build tools is discarded, leaving only the lean nginx image with your compiled app.
This multi-stage build is crucial for production because it dramatically reduces the final image size. You’re not shipping your entire Node.js environment, just the compiled static assets and a web server to serve them. This means faster deployments, smaller storage footprints, and reduced attack surfaces.
The key levers you control are the node version (choose a slim Alpine variant for smaller builds), the package manager (pnpm, yarn, npm), and the base image for serving (nginx, caddy, or even a simple http-server if you’re feeling adventurous). The outDir in vite.config.ts must match the COPY destination in the Dockerfile.
The one thing most people don’t realize is that Vite’s development server is incredibly powerful and can be used in Docker for development builds, but it’s not suitable for production. The production build (vite build) is what optimizes your code for speed and size, producing static files. Relying on the dev server in a production Docker image would be slow, insecure, and unnecessary.
The next logical step is to explore optimizing Nginx configuration for caching and security headers.