⚡ TECH BLOG
Home
Blog
Tags
About
⚡

Powered by Next.js 15 & Modern Web Tech ⚡

Back to Home

Docker for Frontend Developers: A Practical Guide

June 15, 2022
dockerdevopsfrontenddeployment
Docker for Frontend Developers: A Practical Guide

Docker for Frontend Developers: A Practical Guide

Docker isn't just for backend developers. Learn how to containerize frontend applications effectively.

Why Docker for Frontend?

  • Consistent environments - Works the same everywhere
  • Easy onboarding - New developers start quickly
  • CI/CD simplified - Reproducible builds
  • Easy deployment - Ship anywhere

Basic Dockerfile

# Dockerfile
FROM node:18-alpine

WORKDIR /app

COPY package*.json ./
RUN npm install

COPY . .

EXPOSE 3000

CMD ["npm", "start"]

Multi-Stage Build for Production

# Build stage
FROM node:18-alpine AS builder

WORKDIR /app

COPY package*.json ./
RUN npm ci

COPY . .
RUN npm run build

# Production stage
FROM nginx:alpine

COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]

Optimizing Images

Use Alpine Images

# Larger
FROM node:18

# Smaller
FROM node:18-alpine

Layer Caching

# Good: Copy package files first
COPY package*.json ./
RUN npm install

# Then copy source code
COPY . .
RUN npm run build

# Bad: Copy everything first
COPY . .
RUN npm install
RUN npm run build

.dockerignore

node_modules
npm-debug.log
Dockerfile
.dockerignore
.git
.gitignore
.env.local
.env.*.local
coverage
.nyc_output

Development with Docker

Docker Compose for Development

# docker-compose.yml
version: '3.8'

services:
  app:
    build:
      context: .
      target: development
    volumes:
      - .:/app
      - /app/node_modules
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=development
    command: npm run dev

Development Dockerfile

# Dockerfile
FROM node:18-alpine AS development

WORKDIR /app

COPY package*.json ./
RUN npm install

COPY . .

CMD ["npm", "run", "dev"]

# Production stage
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build

FROM nginx:alpine AS production
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Environment Variables

# docker-compose.yml
version: '3.8'

services:
  app:
    build: .
    environment:
      - API_URL=http://api:4000
      - NODE_ENV=production
    env_file:
      - .env
# Dockerfile - Build-time args
ARG API_URL
ENV API_URL=$API_URL

# Build with args
# docker build --build-arg API_URL=https://api.example.com .

NGINX Configuration

# nginx.conf
events {
    worker_connections 1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    server {
        listen 80;
        server_name localhost;
        root /usr/share/nginx/html;
        index index.html;

        # Gzip compression
        gzip on;
        gzip_types text/plain text/css application/json application/javascript;

        # Cache static assets
        location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
            expires 1y;
            add_header Cache-Control "public, immutable";
        }

        # SPA fallback
        location / {
            try_files $uri $uri/ /index.html;
        }

        # Security headers
        add_header X-Frame-Options "SAMEORIGIN" always;
        add_header X-Content-Type-Options "nosniff" always;
        add_header X-XSS-Protection "1; mode=block" always;
    }
}

Next.js with Docker

# Dockerfile for Next.js
FROM node:18-alpine AS base

# Install dependencies only when needed
FROM base AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci

# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .

ENV NEXT_TELEMETRY_DISABLED 1

RUN npm run build

# Production image
FROM base AS runner
WORKDIR /app

ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs

EXPOSE 3000

ENV PORT 3000
ENV HOSTNAME "0.0.0.0"

CMD ["node", "server.js"]

React with Docker

# Dockerfile for React
FROM node:18-alpine AS builder

WORKDIR /app

COPY package*.json ./
RUN npm ci

COPY . .
RUN npm run build

# Production
FROM nginx:alpine

COPY --from=builder /app/build /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]

Vue with Docker

# Dockerfile for Vue
FROM node:18-alpine AS builder

WORKDIR /app

COPY package*.json ./
RUN npm ci

COPY . .
RUN npm run build

# Production
FROM nginx:alpine

COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]

CI/CD Integration

GitHub Actions

# .github/workflows/docker.yml
name: Docker Build and Push

on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2

      - name: Login to DockerHub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Build and push
        uses: docker/build-push-action@v4
        with:
          push: true
          tags: myapp:${{ github.sha }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

Docker Commands Cheat Sheet

# Build image
docker build -t myapp:latest .

# Build with no cache
docker build --no-cache -t myapp:latest .

# Run container
docker run -p 3000:3000 myapp:latest

# Run with environment variables
docker run -p 3000:3000 -e API_URL=http://api myapp:latest

# Run with compose
docker-compose up -d

# View logs
docker logs <container_id>

# Shell into container
docker exec -it <container_id> sh

# Stop all containers
docker stop $(docker ps -aq)

# Remove all images
docker rmi $(docker images -q)

# Prune everything
docker system prune -a

Best Practices

  1. Use multi-stage builds for smaller images
  2. Cache layers by ordering instructions correctly
  3. Use .dockerignore to reduce build context
  4. Run as non-root user for security
  5. Use specific version tags not latest
  6. Scan images for vulnerabilities
  7. Keep images small for faster deployments

Conclusion

Docker simplifies frontend development by providing consistent environments from development to production. Multi-stage builds and proper configuration lead to efficient, secure container images.

Share:

💬 Comments