Skip to content

Docker Deployment Example

Comprehensive guide to deploying PGRestify applications using Docker.

Basic Docker Setup

Dockerfile

dockerfile
# Use official Node.js image
FROM node:18-alpine AS builder

# Set working directory
WORKDIR /app

# Copy package files
COPY package.json pnpm-lock.yaml ./

# Install pnpm
RUN npm install -g pnpm

# Install dependencies
RUN pnpm install --frozen-lockfile

# Copy source code
COPY . .

# Build the application
RUN pnpm build

# Production stage
FROM node:18-alpine

# Set working directory
WORKDIR /app

# Copy built assets from builder stage
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json

# Expose port
EXPOSE 3000

# Set environment to production
ENV NODE_ENV=production

# Start the application
CMD ["pnpm", "start"]

Docker Compose Configuration

yaml
version: '3.8'

services:
  # PostgreSQL Database
  postgres:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: pguser
      POSTGRES_PASSWORD: securepassword
    volumes:
      - postgres-data:/var/lib/postgresql/data
    ports:
      - "5432:5432"

  # PostgREST API
  postgrest:
    image: postgrest/postgrest
    depends_on:
      - postgres
    environment:
      PGRST_DB_URI: postgres://pguser:securepassword@postgres:5432/myapp
      PGRST_DB_SCHEMA: public
      PGRST_DB_ANON_ROLE: web_anon
    ports:
      - "3000:3000"

  # PGRestify Application
  pgrestify-app:
    build: .
    depends_on:
      - postgrest
    environment:
      POSTGREST_URL: http://postgrest:3000
      JWT_SECRET: your-jwt-secret
      NODE_ENV: production
    ports:
      - "4000:4000"

volumes:
  postgres-data:

PGRestify Client Configuration

typescript
import { createClient } from '@webcoded/pgrestify';

// Docker-aware client configuration
const client = createClient(
  process.env.POSTGREST_URL || 'http://localhost:3000', 
  {
    // Docker-specific configuration
    docker: {
      enabled: true,
      
      // Automatic service discovery
      serviceDiscovery: {
        strategy: 'env' // Use environment variables
      },
      
      // Retry configuration for container networks
      retry: {
        enabled: true,
        maxAttempts: 5,
        delay: 2000, // 2 seconds
        
        // Retry only on connection-related errors
        shouldRetry: (error) => 
          error.code === 'ECONNREFUSED' || 
          error.name === 'NetworkError'
      }
    }
  }
);

Kubernetes Deployment

Deployment Configuration

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: pgrestify-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: pgrestify
  template:
    metadata:
      labels:
        app: pgrestify
    spec:
      containers:
      - name: pgrestify
        image: your-pgrestify-image:latest
        ports:
        - containerPort: 4000
        env:
        - name: POSTGREST_URL
          valueFrom:
            configMapKeyRef:
              name: postgrest-config
              key: url
        - name: JWT_SECRET
          valueFrom:
            secretKeyRef:
              name: app-secrets
              key: jwt-secret
        resources:
          requests:
            cpu: 100m
            memory: 128Mi
          limits:
            cpu: 500m
            memory: 512Mi
        readinessProbe:
          httpGet:
            path: /health
            port: 4000
          initialDelaySeconds: 10
          periodSeconds: 5
        livenessProbe:
          httpGet:
            path: /health
            port: 4000
          initialDelaySeconds: 15
          periodSeconds: 10

Service Configuration

yaml
apiVersion: v1
kind: Service
metadata:
  name: pgrestify-service
spec:
  selector:
    app: pgrestify
  ports:
  - port: 80
    targetPort: 4000
  type: LoadBalancer

Health Check Endpoint

typescript
import express from 'express';
import { createClient } from '@webcoded/pgrestify';

const app = express();
const client = createClient(process.env.POSTGREST_URL);

app.get('/health', async (req, res) => {
  try {
    // Perform a simple database health check
    await client
      .from('health_check')
      .select('status')
      .single();

    res.status(200).json({ 
      status: 'healthy', 
      timestamp: new Date().toISOString() 
    });
  } catch (error) {
    res.status(500).json({ 
      status: 'unhealthy', 
      error: error.message 
    });
  }
});

Environment-Specific Configuration

typescript
const client = createClient(process.env.POSTGREST_URL, {
  // Environment-specific settings
  ...(process.env.NODE_ENV === 'production' && {
    cache: {
      enabled: true,
      ttl: 600000 // 10 minutes
    },
    logging: {
      level: 'error'
    }
  }),
  
  ...(process.env.NODE_ENV === 'development' && {
    cache: { enabled: false },
    logging: { level: 'debug' }
  })
});

Docker Security Best Practices

dockerfile
# Use multi-stage build
FROM node:18-alpine AS builder

# Create non-root user
RUN addgroup -S pgrestify && adduser -S pgrestify -G pgrestify

# Set working directory
WORKDIR /app

# Copy files with correct permissions
COPY --chown=pgrestify:pgrestify . .

# Install dependencies as non-root
USER pgrestify

# Run as non-root user
USER pgrestify

# Disable npm audit and fund messages
RUN npm set audit=false && npm set fund=false

Continuous Integration

yaml
# GitHub Actions workflow
name: Docker CI

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

jobs:
  build-and-test:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Build Docker image
      run: docker build -t pgrestify-app .
    
    - name: Run tests in container
      run: |
        docker run --rm pgrestify-app npm test
    
    - name: Push to Docker Hub
      run: |
        docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
        docker push your-docker-repo/pgrestify-app

Monitoring and Logging

yaml
# Docker Compose with monitoring
version: '3.8'
services:
  pgrestify-app:
    # ... existing configuration
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

  prometheus:
    image: prom/prometheus
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
    ports:
      - "9090:9090"

  grafana:
    image: grafana/grafana
    ports:
      - "3001:3000"

Best Practices

  • Use multi-stage builds
  • Minimize image size
  • Run containers as non-root
  • Use environment variables
  • Implement health checks
  • Configure resource limits
  • Use secrets management
  • Enable logging and monitoring
  • Implement CI/CD pipelines

Performance Optimization

  • Use Alpine-based images
  • Leverage Docker layer caching
  • Minimize number of layers
  • Use specific image tags
  • Implement efficient dependency management
  • Configure resource constraints
  • Use volume mounts for persistent data

Released under the MIT License.