Docker & DevOps Skill
This skill provides comprehensive guidance for Docker orchestration, CI/CD pipelines, and deployment strategies in IntelliFill.
Table of Contents
- •Docker Architecture
- •Dockerfile Patterns
- •Docker Compose
- •GitHub Actions
- •Environment Management
- •Health Checks
- •Deployment Strategies
Docker Architecture
IntelliFill uses a multi-container architecture with Docker Compose.
Container Structure
code
IntelliFill/ ├── quikadmin/ # Backend API container │ ├── Dockerfile.dev # Development build │ ├── Dockerfile.prod # Production build │ └── Dockerfile.test # Testing build ├── quikadmin-web/ # Frontend UI container │ ├── Dockerfile.dev │ └── Dockerfile.prod ├── docker-compose.yml # Development orchestration └── docker-compose.e2e.yml # E2E testing orchestration
Service Overview
yaml
# Services in docker-compose.yml services: postgres: # PostgreSQL database redis: # Redis cache & queues backend: # Express API frontend: # React UI nginx: # Reverse proxy (production)
Dockerfile Patterns
IntelliFill uses multi-stage builds for optimization.
Backend Dockerfile (Development)
dockerfile
# quikadmin/Dockerfile.dev
FROM node:18-alpine AS base
# Install dependencies for native modules
RUN apk add --no-cache \
python3 \
make \
g++ \
cairo-dev \
jpeg-dev \
pango-dev \
giflib-dev
WORKDIR /app
# Copy package files
COPY package*.json ./
COPY prisma ./prisma/
# Install dependencies
RUN npm ci
# Copy source code
COPY . .
# Generate Prisma Client
RUN npx prisma generate
# Expose port
EXPOSE 3002
# Development command with hot reload
CMD ["npm", "run", "dev"]
Backend Dockerfile (Production)
dockerfile
# quikadmin/Dockerfile.prod
FROM node:18-alpine AS builder
# Install build dependencies
RUN apk add --no-cache \
python3 \
make \
g++ \
cairo-dev \
jpeg-dev \
pango-dev
WORKDIR /app
# Copy package files
COPY package*.json ./
COPY prisma ./prisma/
# Install dependencies (production only)
RUN npm ci --only=production
# Copy source
COPY . .
# Generate Prisma Client
RUN npx prisma generate
# Build TypeScript
RUN npm run build
# Production stage
FROM node:18-alpine AS production
WORKDIR /app
# Copy from builder
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/prisma ./prisma
COPY --from=builder /app/package*.json ./
# Create non-root user
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
USER nodejs
EXPOSE 3002
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD node -e "require('http').get('http://localhost:3002/api/health', (r) => { process.exit(r.statusCode === 200 ? 0 : 1); });"
CMD ["node", "dist/index.js"]
Frontend Dockerfile (Development)
dockerfile
# quikadmin-web/Dockerfile.dev FROM oven/bun:1 AS base WORKDIR /app # Copy package files COPY package.json bun.lock ./ # Install dependencies RUN bun install # Copy source COPY . . EXPOSE 8080 # Development server with hot reload CMD ["bun", "run", "dev", "--host", "0.0.0.0"]
Frontend Dockerfile (Production)
dockerfile
# quikadmin-web/Dockerfile.prod FROM oven/bun:1 AS builder WORKDIR /app # Copy package files COPY package.json bun.lock ./ # Install dependencies RUN bun install # Copy source COPY . . # Build production bundle RUN bun run build # Production stage with nginx FROM nginx:alpine AS production # Copy nginx config COPY nginx.conf /etc/nginx/conf.d/default.conf # Copy built files COPY --from=builder /app/dist /usr/share/nginx/html EXPOSE 80 # Health check HEALTHCHECK --interval=30s --timeout=3s \ CMD wget --quiet --tries=1 --spider http://localhost/ || exit 1 CMD ["nginx", "-g", "daemon off;"]
Testing Dockerfile
dockerfile
# quikadmin/Dockerfile.test
FROM node:18-alpine
WORKDIR /app
# Install dependencies
RUN apk add --no-cache \
python3 \
make \
g++
# Copy package files
COPY package*.json ./
COPY prisma ./prisma/
# Install all dependencies (including devDependencies)
RUN npm ci
# Copy source
COPY . .
# Generate Prisma Client
RUN npx prisma generate
# Run tests
CMD ["npm", "run", "test:ci"]
Docker Compose
IntelliFill uses Docker Compose for local development and E2E testing.
Development Compose
yaml
# docker-compose.yml
version: '3.8'
services:
postgres:
image: postgres:15-alpine
container_name: intellifill-postgres
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: intellifill
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
container_name: intellifill-redis
ports:
- "6379:6379"
volumes:
- redis_data:/data
command: redis-server --appendonly yes
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
backend:
build:
context: ./quikadmin
dockerfile: Dockerfile.dev
container_name: intellifill-backend
environment:
NODE_ENV: development
PORT: 3002
DATABASE_URL: postgresql://postgres:postgres@postgres:5432/intellifill
REDIS_URL: redis://redis:6379
ports:
- "3002:3002"
volumes:
- ./quikadmin:/app
- /app/node_modules
- backend_uploads:/app/uploads
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
command: sh -c "npx prisma migrate deploy && npm run dev"
frontend:
build:
context: ./quikadmin-web
dockerfile: Dockerfile.dev
container_name: intellifill-frontend
environment:
VITE_API_URL: http://localhost:3002/api
ports:
- "8080:8080"
volumes:
- ./quikadmin-web:/app
- /app/node_modules
depends_on:
- backend
volumes:
postgres_data:
redis_data:
backend_uploads:
E2E Testing Compose
yaml
# docker-compose.e2e.yml
version: '3.8'
services:
postgres-test:
image: postgres:15-alpine
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: intellifill_test
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 5
redis-test:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
backend-test:
build:
context: ./quikadmin
dockerfile: Dockerfile.dev
environment:
NODE_ENV: test
PORT: 3002
DATABASE_URL: postgresql://postgres:postgres@postgres-test:5432/intellifill_test
REDIS_URL: redis://redis-test:6379
ports:
- "3002:3002"
depends_on:
postgres-test:
condition: service_healthy
redis-test:
condition: service_healthy
command: sh -c "npx prisma migrate deploy && npm run dev"
frontend-test:
build:
context: ./quikadmin-web
dockerfile: Dockerfile.dev
environment:
VITE_API_URL: http://backend-test:3002/api
ports:
- "8080:8080"
depends_on:
- backend-test
e2e-runner:
build:
context: ./e2e
dockerfile: Dockerfile
environment:
BASE_URL: http://frontend-test:8080
API_URL: http://backend-test:3002/api
depends_on:
- frontend-test
- backend-test
command: npm run test:e2e
Production Compose (Example)
yaml
# docker-compose.prod.yml
version: '3.8'
services:
postgres:
image: postgres:15-alpine
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- postgres_data:/var/lib/postgresql/data
restart: unless-stopped
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
restart: unless-stopped
backend:
build:
context: ./quikadmin
dockerfile: Dockerfile.prod
environment:
NODE_ENV: production
PORT: 3002
DATABASE_URL: ${DATABASE_URL}
REDIS_URL: redis://redis:6379
depends_on:
- postgres
- redis
restart: unless-stopped
frontend:
build:
context: ./quikadmin-web
dockerfile: Dockerfile.prod
depends_on:
- backend
restart: unless-stopped
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
- ./nginx/ssl:/etc/nginx/ssl
depends_on:
- backend
- frontend
restart: unless-stopped
volumes:
postgres_data:
redis_data:
GitHub Actions
IntelliFill uses GitHub Actions for CI/CD.
Test Workflow
yaml
# .github/workflows/test.yml
name: Tests
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
backend-tests:
name: Backend Tests
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15-alpine
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: intellifill_test
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
redis:
image: redis:7-alpine
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 6379:6379
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
cache-dependency-path: quikadmin/package-lock.json
- name: Install dependencies
working-directory: quikadmin
run: npm ci
- name: Generate Prisma Client
working-directory: quikadmin
run: npx prisma generate
- name: Run migrations
working-directory: quikadmin
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/intellifill_test
run: npx prisma migrate deploy
- name: Run tests
working-directory: quikadmin
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/intellifill_test
REDIS_URL: redis://localhost:6379
run: npm run test:coverage
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
files: ./quikadmin/coverage/lcov.info
flags: backend
frontend-tests:
name: Frontend Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v1
- name: Install dependencies
working-directory: quikadmin-web
run: bun install
- name: Run tests
working-directory: quikadmin-web
run: bun run test:coverage
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
files: ./quikadmin-web/coverage/lcov.info
flags: frontend
e2e-tests:
name: E2E Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build and run E2E tests
run: |
docker-compose -f docker-compose.e2e.yml up --abort-on-container-exit --exit-code-from e2e-runner
- name: Upload test results
if: always()
uses: actions/upload-artifact@v3
with:
name: e2e-results
path: e2e/test-results/
Build and Deploy Workflow
yaml
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
build:
name: Build and Push Docker Images
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push backend
uses: docker/build-push-action@v5
with:
context: ./quikadmin
file: ./quikadmin/Dockerfile.prod
push: true
tags: |
${{ secrets.DOCKER_USERNAME }}/intellifill-backend:latest
${{ secrets.DOCKER_USERNAME }}/intellifill-backend:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Build and push frontend
uses: docker/build-push-action@v5
with:
context: ./quikadmin-web
file: ./quikadmin-web/Dockerfile.prod
push: true
tags: |
${{ secrets.DOCKER_USERNAME }}/intellifill-frontend:latest
${{ secrets.DOCKER_USERNAME }}/intellifill-frontend:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
deploy:
name: Deploy to Production
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to server
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /app/intellifill
docker-compose pull
docker-compose up -d
docker system prune -f
Environment Management
Environment Files
bash
# .env.example (template) NODE_ENV=development PORT=3002 # Database DATABASE_URL=postgresql://user:password@localhost:5432/intellifill DIRECT_URL=postgresql://user:password@localhost:5432/intellifill # Redis REDIS_URL=redis://localhost:6379 # Supabase SUPABASE_URL=https://xxx.supabase.co SUPABASE_ANON_KEY=xxx SUPABASE_SERVICE_ROLE_KEY=xxx # JWT JWT_SECRET=your-secret-key
Docker Environment Variables
yaml
# docker-compose.yml
services:
backend:
env_file:
- ./quikadmin/.env
environment:
# Override specific variables
DATABASE_URL: postgresql://postgres:postgres@postgres:5432/intellifill
Build Arguments
dockerfile
# Dockerfile with build args FROM node:18-alpine ARG NODE_ENV=production ENV NODE_ENV=$NODE_ENV ARG API_URL ENV VITE_API_URL=$API_URL WORKDIR /app # ...
yaml
# docker-compose.yml with build args
services:
frontend:
build:
context: ./quikadmin-web
args:
NODE_ENV: development
API_URL: http://localhost:3002/api
Health Checks
Application Health Endpoint
typescript
// quikadmin/src/api/health.routes.ts
import { Router } from 'express';
import prisma from '../utils/prisma';
import redis from '../utils/redis';
const router = Router();
router.get('/health', async (req, res) => {
try {
// Check database
await prisma.$queryRaw`SELECT 1`;
// Check Redis
await redis.ping();
res.status(200).json({
status: 'healthy',
timestamp: new Date().toISOString(),
services: {
database: 'up',
redis: 'up',
},
});
} catch (error) {
res.status(503).json({
status: 'unhealthy',
timestamp: new Date().toISOString(),
error: error.message,
});
}
});
export default router;
Docker Health Checks
dockerfile
# Dockerfile with health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD node -e "require('http').get('http://localhost:3002/api/health', (r) => { process.exit(r.statusCode === 200 ? 0 : 1); });"
yaml
# docker-compose.yml with health checks
services:
backend:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3002/api/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
Deployment Strategies
Zero-Downtime Deployment
bash
#!/bin/bash # deploy.sh # Pull latest images docker-compose pull # Start new containers docker-compose up -d --no-deps --scale backend=2 backend # Wait for health check sleep 10 # Stop old containers docker-compose up -d --no-deps --scale backend=1 backend # Clean up docker system prune -f
Blue-Green Deployment
yaml
# docker-compose.blue-green.yml
services:
backend-blue:
# Current production
backend-green:
# New version
nginx:
# Switch between blue/green
Rolling Updates
bash
# Update one container at a time for service in backend-1 backend-2 backend-3; do docker-compose up -d $service sleep 30 # Wait for health check done
Best Practices
- •Multi-stage builds - Separate build and production stages
- •Layer caching - Order Dockerfile commands for optimal caching
- •Non-root user - Run containers as non-root
- •Health checks - Implement health checks for all services
- •Resource limits - Set memory and CPU limits
- •Secrets management - Use Docker secrets or external secret managers
- •Volume mounts - Use named volumes for persistence
- •Network isolation - Use custom networks
- •Graceful shutdown - Handle SIGTERM signals
- •Logging - Configure proper log drivers