bunjs-production
This skill provides production-ready patterns for deploying Bun.js TypeScript applications, covering Docker multi-stage builds, AWS ECS/Fargate orchestration, Redis caching implementation, security hardening with headers and rate limiting, structured logging, and CI/CD pipeline setup. Use it when preparing Bun applications for production deployment, containerizing with Docker, scaling on AWS infrastructure, or implementing enterprise-grade security and monitoring practices.
git clone --depth 1 https://github.com/MadAppGang/claude-code /tmp/bunjs-production && cp -r /tmp/bunjs-production/plugins/dev/skills/backend/bunjs-production ~/.claude/skills/bunjs-productionSKILL.md
# 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)
```jArchitecture Decision Records (ADR) documentation practice. Use when documenting architectural decisions, recording technical trade-offs, creating decision logs, or establishing architectural patterns. Trigger keywords - "ADR", "architecture decision", "decision record", "trade-offs", "architectural decision", "decision log".
Use when deciding whether to launch an agent, selecting which agent to use, or coordinating multiple agents. Covers delegation criteria, external-model patterns, task isolation, and agent selection strategies.
Use when designing REST or GraphQL APIs, defining endpoints, implementing pagination/filtering, handling API versioning, or establishing API documentation with OpenAPI/Swagger.
Use when integrating Apidog + OpenAPI specifications with your React app. Covers MCP server setup, type generation, and query layer integration. Use when setting up API clients, generating types from OpenAPI, or integrating with Apidog MCP.
Analyzes API documentation from OpenAPI specs to provide TypeScript interfaces, request/response formats, and implementation guidance. Use when implementing API integrations, debugging API errors (400, 401, 404), replacing mock APIs, verifying data types, or when user mentions endpoints, API calls, or backend integration.
Use when analyzing architecture and system design. Find design patterns, map layers, identify core abstractions via PageRank. Uses claudemem AST structural analysis for efficient architecture investigation.
On-demand security and code quality audit. Use when checking for vulnerabilities, security issues, code smells, or compliance problems. Trigger keywords - "audit", "security check", "vulnerability scan", "code quality", "compliance", "security audit".
Use when implementing authentication (JWT, sessions, OAuth), authorization (RBAC, ABAC), password hashing, MFA, or security best practices for backend services.