Skip to content

Build & Deploy

Build process, Docker configuration, and deployment workflows.

NPM Scripts

From package.json:

Script Command Purpose
dev next dev Development server
build next build Production build
start next start Production server
lint eslint . Run linter

Development

npm run dev
# Runs on http://localhost:3000

Production Build

npm run build
npm start

Linting

npm run lint

Docker

Dockerfile

Multi-stage build for optimized production image:

# Stage 1: Builder
FROM node:20-slim AS builder

WORKDIR /app

# Install dependencies
COPY package*.json ./
RUN npm ci --legacy-peer-deps

# Copy source and build
COPY . .
RUN npm run build

# Stage 2: Production
FROM node:20-slim

WORKDIR /app

# Install production dependencies only
COPY package*.json ./
RUN npm ci --only=production --legacy-peer-deps

# TypeScript needed for next.config.ts
RUN npm install typescript

# Copy built assets
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/public ./public
COPY --from=builder /app/next.config.ts ./
COPY --from=builder /app/lib ./lib

# Expose port
EXPOSE 3000

# Set environment
ENV NODE_ENV=production

# Start server
CMD ["npm", "start"]

Build Image

docker build -t coordinator-web .

Run Container

docker run -p 3000:3000 \
  -e NEXT_PUBLIC_AUTH_API_URL=https://auth.example.com \
  -e NEXT_PUBLIC_API_BASE_URL=https://api.example.com \
  -e NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_xxx \
  -e LOKI_URL=https://loki.example.com \
  coordinator-web

Docker Compose

version: "3.8"

services:
  web:
    build: .
    ports:
      - "3000:3000"
    environment:
      - NEXT_PUBLIC_AUTH_API_URL=https://auth.example.com
      - NEXT_PUBLIC_API_BASE_URL=https://api.example.com
      - NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_xxx
      - LOKI_URL=https://loki.example.com
      - NODE_ENV=production
    restart: unless-stopped

CI/CD Workflows

GitHub Actions

.github/workflows/ contains workflow files.

Build & Test

name: Build and Test

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: "npm"

      - name: Install dependencies
        run: npm ci --legacy-peer-deps

      - name: Run linter
        run: npm run lint

      - name: Build
        run: npm run build
        env:
          NEXT_PUBLIC_AUTH_API_URL: ${{ vars.AUTH_API_URL }}
          NEXT_PUBLIC_API_BASE_URL: ${{ vars.API_BASE_URL }}
          NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: ${{ secrets.STRIPE_PK }}

Docker Build & Push

name: Docker Build

on:
  push:
    branches: [main]

jobs:
  docker:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Login to Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and Push
        uses: docker/build-push-action@v5
        with:
          push: true
          tags: ghcr.io/${{ github.repository }}:${{ github.sha }}

Documentation Deploy

name: Deploy Docs

on:
  push:
    branches: [main]
    paths:
      - "docs/**"

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Setup Python
        uses: actions/setup-python@v4
        with:
          python-version: "3.x"

      - name: Install MkDocs
        run: pip install mkdocs mkdocs-material

      - name: Build docs
        run: mkdocs build

      - name: Deploy to GitHub Pages
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./site

Deployment Platforms

Vercel

  1. Connect GitHub repository
  2. Set environment variables in Vercel dashboard
  3. Deploy automatically on push

Railway

  1. Create new project from GitHub
  2. Add environment variables
  3. Deploy

Self-Hosted

# Clone repository
git clone https://github.com/org/coordinator-web.git
cd coordinator-web

# Install dependencies
npm ci --legacy-peer-deps

# Build
npm run build

# Start with PM2
pm2 start npm --name "coordinator-web" -- start

Kubernetes

apiVersion: apps/v1
kind: Deployment
metadata:
  name: coordinator-web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: coordinator-web
  template:
    metadata:
      labels:
        app: coordinator-web
    spec:
      containers:
        - name: web
          image: ghcr.io/org/coordinator-web:latest
          ports:
            - containerPort: 3000
          env:
            - name: NEXT_PUBLIC_AUTH_API_URL
              valueFrom:
                configMapKeyRef:
                  name: coordinator-config
                  key: auth-url
---
apiVersion: v1
kind: Service
metadata:
  name: coordinator-web
spec:
  selector:
    app: coordinator-web
  ports:
    - port: 80
      targetPort: 3000

Health Checks

Endpoint

Create a health endpoint:

// app/api/health/route.ts
export async function GET() {
  return Response.json({ status: "ok", timestamp: new Date().toISOString() });
}

Docker Health Check

HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:3000/api/health || exit 1

Build Optimization

Bundle Analysis

npm install @next/bundle-analyzer

# In next.config.ts
const withBundleAnalyzer = require("@next/bundle-analyzer")({
  enabled: process.env.ANALYZE === "true"
});

module.exports = withBundleAnalyzer(nextConfig);

# Run analysis
ANALYZE=true npm run build

Caching

Next.js caching is automatic. For Docker:

# Cache node_modules
COPY package*.json ./
RUN npm ci

# Separate layer for source
COPY . .
RUN npm run build

Environment-Specific Builds

# Development
npm run dev

# Staging
NODE_ENV=staging npm run build

# Production
NODE_ENV=production npm run build