DEV Community

Cover image for Monitor websites changes with Firecrawl Observer and Docker
lvn1
lvn1

Posted on

Monitor websites changes with Firecrawl Observer and Docker

Prerequisites

Deployment Guide

Step 1: Clone, initialze and prepare the environment

git clone https://github.com/mendableai/firecrawl-observer.git
cd firecrawl-observer
npm install
npx convex dev

# Open a new terminal and set up authentication for production
npx @convex-dev/auth --prod

# Set encryption key (REQUIRED - only run if not already set)
npx convex env set ENCRYPTION_KEY "$(openssl rand -base64 32)" --prod

# Verify all required variables are set
npx convex env list --prod
Enter fullscreen mode Exit fullscreen mode

Required variables should include:

  • SITE_URL ✓ (already set)
  • JWT_PRIVATE_KEY ✓ (already set)
  • JWKS ✓ (already set)
  • ENCRYPTION_KEY (set this now)
# Deploy Convex functions to production
npx convex deploy --prod
Enter fullscreen mode Exit fullscreen mode

Step 2: Update Environment File. Replace YOUR_PUBLIC_URL with the domain provided by convex.

# Create/update .env file
cat > .env << 'EOF'
NEXT_PUBLIC_CONVEX_URL=https://YOUR_PUBLIC_URL.convex.cloud
NODE_ENV=production
EOF
Enter fullscreen mode Exit fullscreen mode

Step 3: Update Dockerfile

# Stage 1: Build the application
FROM node:20-slim AS builder
WORKDIR /app

# Declare build-time arguments
ARG NEXT_PUBLIC_CONVEX_URL
ARG NODE_ENV=production

# Set environment variables for build
ENV NEXT_PUBLIC_CONVEX_URL=$NEXT_PUBLIC_CONVEX_URL
ENV NODE_ENV=$NODE_ENV

# Install dependencies
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
RUN \
    if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
    elif [ -f package-lock.json ]; then npm ci --only=production; \
    elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i --frozen-lockfile --prod; \
    else echo "Lockfile not found." && exit 1; \
    fi

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

# Stage 2: Production runtime
FROM node:20-slim AS runner
WORKDIR /app

# Install curl for health checks
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*

# Create non-root user
RUN addgroup --system --gid 1001 nodejs && \
    adduser --system --uid 1001 nextjs

# Copy built application with proper ownership
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json

# Switch to non-root user
USER nextjs

# Expose port and set environment
EXPOSE 3000
ENV PORT=3000
ENV NODE_ENV=production

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

# Start application
CMD ["npm", "start"]
Enter fullscreen mode Exit fullscreen mode

Step 4: Update docker-compose.yml (Production Ready)

version: '3.8'

services:
  app:
    image: firecrawl-observer-app
    build:
      context: .
      args:
        - NEXT_PUBLIC_CONVEX_URL=${NEXT_PUBLIC_CONVEX_URL}
        - NODE_ENV=production
    restart: unless-stopped
    env_file:
      - .env
    networks:
      - app-network
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    # Resource limits (adjust based on your server)
    deploy:
      resources:
        limits:
          memory: 1G
          cpus: '0.5'
        reservations:
          memory: 512M
          cpus: '0.25'

  nginx:
    image: nginx:alpine
    restart: unless-stopped
    ports:
      - "5001:80"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
      - ./nginx/nginx.htpasswd:/etc/nginx/conf.d/nginx.htpasswd:ro
    depends_on:
      app:
        condition: service_healthy
    networks:
      - app-network
    # Security and performance
    deploy:
      resources:
        limits:
          memory: 128M
          cpus: '0.1'

networks:
  app-network:
    driver: bridge

# Optional: Volumes for persistence
volumes:
  nginx_cache:
Enter fullscreen mode Exit fullscreen mode

Step 5: Optimize Nginx Configuration

# Update nginx.conf with production optimizations
cat > nginx/nginx.conf << 'EOF'
# Upstream definition
upstream app_backend {
    server app:3000;
    keepalive 32;
}

server {
    listen 80;
    server_name _;

    # 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;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # Gzip compression
    gzip on;
    gzip_vary on;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

    # Basic auth for all routes
    auth_basic "Restricted Content";
    auth_basic_user_file /etc/nginx/conf.d/nginx.htpasswd;

    # Health check endpoint (bypass auth)
    location /api/health {
        access_log off;
        proxy_pass http://app_backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }

    # Main application
    location / {
        proxy_pass http://app_backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # Timeouts
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;

        # Buffer settings
        proxy_buffering on;
        proxy_buffer_size 8k;
        proxy_buffers 8 8k;

        # Cache static assets
        location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
            expires 1y;
            add_header Cache-Control "public, immutable";
            proxy_pass http://app_backend;
        }
    }
}
EOF
Enter fullscreen mode Exit fullscreen mode

Step 6: Add Health Check Endpoint

# Create health check API route
mkdir -p src/app/api/health
cat > src/app/api/health/route.ts << 'EOF'
import { NextResponse } from 'next/server';

export async function GET() {
  try {
    // Basic health check - can be expanded to check dependencies
    return NextResponse.json({ 
      status: 'healthy',
      timestamp: new Date().toISOString(),
      uptime: process.uptime(),
      environment: process.env.NODE_ENV || 'development'
    }, { status: 200 });
  } catch (error) {
    return NextResponse.json({ 
      status: 'unhealthy',
      error: 'Health check failed',
      timestamp: new Date().toISOString()
    }, { status: 503 });
  }
}
EOF
Enter fullscreen mode Exit fullscreen mode

Step 7: Deploy with Zero-Downtime Strategy

# Stop current containers gracefully
docker-compose down

# Clean up old images (optional, saves disk space)
docker image prune -f

# Build and deploy with improved logging
docker-compose up --build -d

# Monitor startup
echo "Waiting for services to start..."
sleep 10

# Check service health
docker-compose ps
docker-compose logs --tail=50 app
Enter fullscreen mode Exit fullscreen mode

Step 8: Verification and Monitoring. Replace SERVER_IP & PORT with yours

# Test health endpoint
curl -I http://SERVER_IP:PORT/api/health

# Test full application (will prompt for basic auth)
curl -I http://SERVER_IP:PORT/

# Monitor logs in real-time
docker-compose logs -f --tail=20

# Check resource usage
docker stats --no-stream
Enter fullscreen mode Exit fullscreen mode

Production Monitoring Script

# Create monitoring script
cat > monitor.sh << 'EOF'
#!/bin/bash
echo "=== Container Status ==="
docker-compose ps

echo -e "\n=== Health Check ==="
curl -s http://localhost:3000/api/health | jq '.' 2>/dev/null || echo "Health check failed"

echo -e "\n=== Resource Usage ==="
docker stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}"

echo -e "\n=== Recent Logs ==="
docker-compose logs --tail=5 app
EOF

chmod +x monitor.sh
Enter fullscreen mode Exit fullscreen mode

Automated Deployment Script

# Create deployment script for future updates
cat > deploy.sh << 'EOF'
#!/bin/bash
set -e

echo "Starting deployment..."

# Pull latest changes (if using git)
# git pull origin main

# Deploy Convex functions
echo "Deploying Convex functions..."
npx convex deploy --prod

# Build and deploy containers
echo "Building and deploying containers..."
docker-compose down
docker-compose up --build -d

# Wait for health check
echo "Waiting for application to be healthy..."
for i in {1..30}; do
    if curl -f http://localhost:3000/api/health >/dev/null 2>&1; then
        echo "Application is healthy!"
        break
    fi
    echo "Waiting... ($i/30)"
    sleep 10
done

echo "Deployment complete!"
docker-compose ps
EOF

chmod +x deploy.sh
Enter fullscreen mode Exit fullscreen mode

Security Checklist

  • Non-root containers: App runs as user nextjs
  • Basic authentication: Password protection via nginx
  • Security headers: XSS, CSRF, and frame protection
  • Resource limits: Memory and CPU constraints
  • Health checks: Automated container health monitoring
  • Encrypted secrets: API keys stored in Convex cloud
  • ⚠️ HTTPS: Consider adding SSL/TLS for production
  • ⚠️ Firewall: Ensure only necessary ports are open

Troubleshooting

Quick Diagnostics

# Full system check
./monitor.sh

# View all logs
docker-compose logs

# Restart specific service
docker-compose restart app

# Force rebuild
docker-compose up --build --force-recreate -d
Enter fullscreen mode Exit fullscreen mode

Common Issues

  1. Auth errors: Check Convex environment variables with npx convex env list --prod
  2. Build failures: Verify .env file and NEXT_PUBLIC_CONVEX_URL
  3. Health check failures: Check if port 3000 is accessible inside container
  4. nginx issues: Verify nginx.htpasswd file permissions

Your application is now production-ready with monitoring, health checks, and optimized performance!

Top comments (0)