Skip to main content

bunjs-production

Use when deploying Bun.js to production, containerizing with Docker, setting up AWS ECS/Fargate, implementing Redis caching, hardening security, or configuring CI/CD pipelines. See bunjs for basics, bunjs-architecture for patterns.

معلومات المصدر

المستودع
MadAppGang/claude-code
آخر نشاط في المصدر
٢٠ يناير ٢٠٢٦ في ٠٨:١٦
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٢٨٤
التفرعات
٢٥

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
bunjs-production
version
1.0.0
description
Use when deploying Bun.js to production, containerizing with Docker, setting up AWS ECS/Fargate, implementing Redis caching, hardening security, or configuring CI/CD pipelines. See bunjs for basics, bunjs-architecture for patterns.
keywords
["production deployment","Docker","AWS ECS","Redis","caching","security","CI/CD","logging","monitoring","rate limiting"]
plugin
dev
updated
2026-01-20T00:00:00.000Z
# Bun.js Production Deployment Patterns ## Overview This skill covers production deployment patterns for Bun.js TypeScript backend applications, including Docker containerization, AWS ECS deployment, Redis caching, security hardening, structured logging, CI/CD pipelines, and production readiness checklists. **When to use this skill:** - Containerizing applications with Docker - Deploying to AWS ECS/Fargate - Implementing Redis caching strategies - Hardening security (headers, CORS, rate limiting) - Setting up CI/CD pipelines - Preparing for production deployment **See also:** - **dev:bunjs** - Core Bun patterns, HTTP servers, database access - **dev:bunjs-architecture** - Layered architecture, camelCase conventions - **dev:bunjs-apidog** - OpenAPI specifications and Apidog integration ## Docker Multi-Stage Build ### Production Dockerfile ```dockerfile # Stage 1: Base FROM oven/bun:1-alpine AS base WORKDIR /app # Stage 2: Dependencies FROM base AS deps COPY package.json bun.lockb ./ COPY prisma ./prisma/ RUN bun install --frozen-lockfile --production # Stage 3: Build FROM base AS build COPY --from=deps /app/node_modules ./node_modules COPY . . RUN bunx prisma generate RUN bun run build # Optional: if you have a build step # Stage 4: Runner FROM base AS runner WORKDIR /app ENV NODE_ENV=production # Create non-root user RUN addgroup -g 1001 bungroup && \ adduser -D -u 1001 -G bungroup bunuser # Copy dependencies and source COPY --from=deps /app/node_modules ./node_modules COPY --from=build /app/src ./src COPY --from=build /app/prisma ./prisma COPY --from=build /app/node_modules/.prisma ./node_modules/.prisma COPY package.json bun.lockb ./ # Set ownership RUN chown -R bunuser:bungroup /app USER bunuser EXPOSE 3000 # Health check HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \ CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1 CMD ["bun", "src/server.ts"] ``` ### docker-compose.yml (Local Development) ```yaml version: '3.8' services: app: build: context: . dockerfile: Dockerfile ports: - "3000:3000" environment: - NODE_ENV=development - DATABASE_URL=postgresql://user:password@postgres:5432/mydb - REDIS_URL=redis://redis:6379 depends_on: - postgres - redis volumes: - ./src:/app/src command: bun --hot src/server.ts postgres: image: postgres:17-alpine environment: POSTGRES_USER: user POSTGRES_PASSWORD: password POSTGRES_DB: mydb ports: - "5432:5432" volumes: - postgres-data:/var/lib/postgresql/data redis: image: redis:7-alpine ports: - "6379:6379" volumes: - redis-data:/data volumes: postgres-data: redis-data: ``` ### Build and Run Commands ```bash # Build image docker build -t myapp:latest . # Run locally docker-compose up -d # Run migrations docker-compose exec app bunx prisma migrate deploy # View logs docker-compose logs -f app # Stop docker-compose down ``` ## Graceful Shutdown ### Server with Shutdown Handling ```typescript // src/server.ts import { serve } from '@hono/node-server'; import { app } from './app'; import { prisma } from '@/database/client'; import { logger } from '@core/logger'; const PORT = Number(process.env.PORT) || 3000; // Start server const server = serve({ fetch: app.fetch, port: PORT }); logger.info(`🚀 Server running on port ${PORT}`); // Graceful shutdown handler async function shutdown(signal: string) { logger.info(`Received ${signal}, initiating graceful shutdown...`); try { // Close HTTP server (stop accepting new requests) server.close(); logger.info('HTTP server closed'); // Close database connections await prisma.$disconnect(); logger.info('Database connections closed'); // Close Redis connections (if used) // await redis.quit(); logger.info('Graceful shutdown complete'); process.exit(0); } catch (error) { logger.error({ error }, 'Error during shutdown'); process.exit(1); } } // Handle termination signals process.on('SIGTERM', () => shutdown('SIGTERM')); process.on('SIGINT', () => shutdown('SIGINT')); // Handle unhandled errors process.on('unhandledRejection', (reason, promise) => { logger.error({ reason, promise }, 'Unhandled promise rejection'); }); process.on('uncaughtException', (error) => { logger.error({ error }, 'Uncaught exception'); shutdown('UNCAUGHT_EXCEPTION'); }); ``` ## AWS ECS Deployment ### Task Definition (JSON) ```json { "family": "myapp", "networkMode": "awsvpc", "requiresCompatibilities": ["FARGATE"], "cpu": "256", "memory": "512", "containerDefinitions": [ { "name": "myapp", "image": "123456789012.dkr.ecr.us-east-1.amazonaws.com/myapp:latest", "essential": true, "portMappings": [ { "containerPort": 3000, "protocol": "tcp" } ], "environment": [ { "name": "NODE_ENV", "value": "production" }, { "name": "PORT", "value": "3000" } ], "secrets": [ { "name": "DATABASE_URL", "valueFrom": "arn:aws:secretsmanager:us-east-1:123456789012:secret:myapp/DATABASE_URL" }, { "name": "JWT_SECRET", "valueFrom": "arn:aws:secretsmanager:us-east-1:123456789012:secret:myapp/JWT_SECRET" } ], "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "/ecs/myapp", "awslogs-region": "us-east-1", "awslogs-stream-prefix": "ecs" } }, "healthCheck": { "command": ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1"], "interval": 30, "timeout": 5, "retries": 3, "startPeriod": 30 } } ] } ``` ### Service Definition (JSON) ```json { "serviceName": "myapp", "cluster": "production-cluster", "taskDefinition": "myapp:1", "desiredCount": 2, "launchType": "FARGATE", "networkConfiguration": { "awsvpcConfiguration": { "subnets": [ "subnet-12345678", "subnet-87654321" ], "securityGroups": [ "sg-12345678" ], "assignPublicIp": "DISABLED" } }, "loadBalancers": [ { "targetGroupArn": "arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/myapp/1234567890123456", "containerName": "myapp", "containerPort": 3000 } ], "healthCheckGracePeriodSeconds": 60 } ``` ### Deployment Script ```bash #!/bin/bash # deploy.sh set -e # Variables AWS_REGION="us-east-1" ECR_REGISTRY="123456789012.dkr.ecr.${AWS_REGION}.amazonaws.com" IMAGE_NAME="myapp" IMAGE_TAG="${GITHUB_SHA:0:7}" CLUSTER_NAME="production-cluster" SERVICE_NAME="myapp" # 1. Login to ECR aws ecr get-login-password --region $AWS_REGION | \ docker login --username AWS --password-stdin $ECR_REGISTRY # 2. Build image docker build -t $IMAGE_NAME:$IMAGE_TAG . # 3. Tag image docker tag $IMAGE_NAME:$IMAGE_TAG $ECR_REGISTRY/$IMAGE_NAME:$IMAGE_TAG docker tag $IMAGE_NAME:$IMAGE_TAG $ECR_REGISTRY/$IMAGE_NAME:latest # 4. Push to ECR docker push $ECR_REGISTRY/$IMAGE_NAME:$IMAGE_TAG docker push $ECR_REGISTRY/$IMAGE_NAME:latest # 5. Update ECS service (force new deployment) aws ecs update-service \ --cluster $CLUSTER_NAME \ --service $SERVICE_NAME \ --force-new-deployment \ --region $AWS_REGION echo "Deployment initiated. Check ECS console for status." ``` ## Caching with Redis ### Redis Client Setup ```typescript // src/utils/redis.ts import Redis from 'ioredis'; import { logger } from '@core/logger'; const redisUrl = process.env.REDIS_URL || 'redis://localhost:6379'; export const redis = new Redis(redisUrl, { maxRetriesPerRequest: 3, retryStrategy(times) { const delay = Math.min(times * 50, 2000); return delay; }, reconnectOnError(err) { logger.error({ error: err }, 'Redis connection error'); return true; } }); redis.on('connect', () => { logger.info('Redis connected'); }); redis.on('error', (err) => { logger.error({ error: err }, 'Redis error'); }); // Graceful shutdown export async function closeRedis() { await redis.quit(); logger.info('Redis connection closed'); } ``` ### Cache Utilities ```typescript // src/utils/cache.ts import { redis } from './redis'; export async function cacheGet<T>(key: string): Promise<T | null> { const value = await redis.get(key); return value ? JSON.parse(value) : null; } export async function cacheSet( key: string, value: any, ttlSeconds: number ): Promise<void> { await redis.set(key, JSON.stringify(value), 'EX', ttlSeconds); } export async function cacheDelete(key: string): Promise<void> { await redis.del(key); } export async function cacheDeletePattern(pattern: string): Promise<void> { const keys = await redis.keys(pattern); if (keys.length > 0) { await redis.del(...keys); } } // Cache-aside pattern export async function cached<T>( key: string, ttl: number, fn: () => Promise<T> ): Promise<T> { const hit = await cacheGet<T>(key); if (hit) return hit; const value = await fn(); await cacheSet(key, value, ttl); return value; } ``` ### Usage in Services ```typescript // src/services/user.service.ts import { cached, cacheDelete } from '@utils/cache'; import { userRepository } from '@/database/repositories/user.repository'; export const getUserById = async (id: string) => { return cached(`user:${id}`, 300, async () => { const user = await userRepository.findById(id); if (!user) throw new NotFoundError('User'); const { password, ...withoutPassword } = user; return withoutPassword; }); }; export const updateUser = async (id: string, data: UpdateUserDto) => { const user = await userRepository.update(id, data); // Invalidate cache after update await cacheDelete(`user:${id}`); const { password, ...withoutPassword } = user; return withoutPassword; }; ``` ### Cache Key Conventions ```typescript // ✅ CORRECT: Namespaced keys const keys = { user: (id: string) => `user:${id}`,
عرض على GitHub
ملف SKILL.md هذا كبير جدا، لذلك يعرض SkillsMP القسم الاول فقط هنا. عرض على GitHub