Skip to content

Instantly share code, notes, and snippets.

@kksudo
Last active June 25, 2025 14:22
Show Gist options
  • Select an option

  • Save kksudo/36a314ea6470fa747694f13b0c369020 to your computer and use it in GitHub Desktop.

Select an option

Save kksudo/36a314ea6470fa747694f13b0c369020 to your computer and use it in GitHub Desktop.
Dockerfile for React Node Apps in 2025: Best Practices
# node
node_modules
build
dist
# IDE
.idea
*.xml
*.iml
.vscode
# OS
.DS_Store
Thumbs.db
# Other
.git
.gitignore
.env
*.env
*.log
*.md
# Define Build Arguments
# Allows flexible image and environment configuration.
ARG NODE_ENV=production
ARG BASE_IMAGE=node:20.19.3-alpine3.21
ARG FINAL_IMAGE=node:20.19.3-alpine3.21
# Use Lightweight Base Image
# `node:20.19.3-alpine3.21` minimizes image size (~150MB).
FROM ${BASE_IMAGE} AS builder
# Set Environment Variable
# Optimizes app for production via build-time argument.
ENV NODE_ENV=${NODE_ENV}
# Organize Work Directory
WORKDIR /app
# Cache Dependencies
# Use `--mount=type=cache` for faster builds, install prod-only deps.
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm \
npm ci --omit=dev
# Build App
COPY src .
RUN npm run build
# Production Stage
# Use same lightweight base for runtime.
FROM ${FINAL_IMAGE} AS final
# Run as non-root for security
USER node
WORKDIR /app
# Copy Artifacts with Permissions
# Set ownership and execution permissions.
COPY --from=builder --chmod=755 /app/entrypoint.sh /entrypoint.sh
COPY --from=builder --chown=node:node /app/package.json ./package.json
COPY --from=builder --chown=node:node /app/dist ./dist
COPY --from=builder --chown=node:node /app/public ./public
COPY --from=builder --chown=node:node /app/node_modules ./node_modules
# Expose Port
EXPOSE 3000
# Define Entry Point and Command
ENTRYPOINT ["/entrypoint.sh"]
CMD ["node", "server.js"]
# Exclude Unnecessary Files
# Use `.dockerignore`:
# node_modules
# build
# dist
# .idea
# *.xml
# *.iml
# .vscode
# .DS_Store
# Thumbs.db
# .git
# .gitignore
# .env
# *.env
# *.log
# *.md
# Secure Environment Variables
# Pass at runtime: docker run -e REACT_APP_API_URL=https://api.example.com
#!/bin/ash
set -e
set -o pipefail
echo "Loading config..."
echo "$CONFIG" | jq -r 'to_entries[] | "\(.key)=\(.value)"' > .env
echo "Loading secrets..."
echo "$SECRETS" | jq -r 'to_entries[] | "\(.key)=\(.value)"' >> .env
# Check if .env file is not empty
if [ ! -s .env ]; then
echo ".env file is empty or not created properly."
else
echo "Set up environment variables for current session from .env file."
# Convert .env to a format suitable for sourcing and use it for every session and source
while IFS='=' read -r key val; do
[ -n "$key" ] && export "$key=$val"
done < .env
fi
# Execute the main container command
exec "$@"

Dockerfile for React Node Apps(and not only for react) in 2025: Best Practices

Docker ensures your React Node app runs consistently across environments. In 2025, optimizing Dockerfiles is critical for performance, security, and scalability. This guide, styled like a Dockerfile, outlines practices like lightweight images, multi-stage builds, cached dependencies, production-only packages, permission management, and secure variables, reflecting the updated Dockerfile and .dockerignore.

# Define Build Arguments
# Allows flexible image and environment configuration.
ARG NODE_ENV=production
ARG BASE_IMAGE=node:20.19.3-alpine3.21
ARG FINAL_IMAGE=node:20.19.3-alpine3.21

# Use Lightweight Base Image
# `node:20.19.3-alpine3.21` minimizes image size (~150MB).
FROM ${BASE_IMAGE} AS builder

# Set Environment Variable
# Optimizes app for production via build-time argument.
ENV NODE_ENV=${NODE_ENV}

# Organize Work Directory
WORKDIR /app

# Cache Dependencies
# Use `--mount=type=cache` for faster builds, install prod-only deps.
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm \
npm ci --omit=dev

# Build App
COPY src .
RUN npm run build

# Production Stage
# Use same lightweight base for runtime.
FROM ${FINAL_IMAGE} AS final

# Run as non-root for security
USER node
WORKDIR /app

# Copy Artifacts with Permissions
# Set ownership and execution permissions.
COPY --from=builder --chmod=755 /app/entrypoint.sh /entrypoint.sh
COPY --from=builder --chown=node:node /app/package.json ./package.json
COPY --from=builder --chown=node:node /app/dist ./dist
COPY --from=builder --chown=node:node /app/public ./public
COPY --from=builder --chown=node:node /app/node_modules ./node_modules

# Expose Port
EXPOSE 3000

# Define Entry Point and Command
ENTRYPOINT ["/entrypoint.sh"]
CMD ["node", "server.js"]

# Exclude Unnecessary Files
# Use `.dockerignore`:
# node_modules
# build
# dist
# .idea
# *.xml
# *.iml
# .vscode
# .DS_Store
# Thumbs.db
# .git
# .gitignore
# .env
# *.env
# *.log
# *.md

# Secure Environment Variables
# Pass at runtime: docker run -e REACT_APP_API_URL=https://api.example.com

About These Practices

These efficient, secure, and scalable practices optimize Dockerized React Node apps. Lightweight images and multi-stage builds reduce size, cached dependencies with --mount=type=cache and npm ci --omit=dev speed up builds, and --chown/--chmod ensure secure permissions. ARG for NODE_ENV and images adds flexibility.

Troubleshooting

Slow Builds: Check .dockerignore and caching. Permission Issues: Verify --chown and --chmod. Port Conflicts: Remap with docker run -p 4000:3000.

Resources

Docker Docs: Dockerfile Docker Docs: Build Cache

@kksudo
Copy link
Author

kksudo commented Jun 25, 2025

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment