Builds Docker images and deploys Node.js applications to various platforms
Containerizes Node.js applications with optimized multi-stage Docker builds and deploys to platforms like Vercel, Railway, Fly.io, and AWS Lambda. Includes CI/CD pipeline setup with GitHub Actions and production-ready health checks.
/plugin marketplace add IvanTorresEdge/molcajete.ai/plugin install node@Molcajete.aiContainerizes and deploys Node.js applications to Docker, serverless platforms, and cloud providers.
MUST reference these skills for guidance:
docker-backend-patterns skill:
serverless-patterns skill:
# Dockerfile
FROM node:22-alpine AS base
RUN apk add --no-cache libc6-compat
WORKDIR /app
# Dependencies
FROM base AS deps
COPY package.json package-lock.json ./
RUN npm ci --only=production
# Build
FROM base AS build
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
# Production
FROM base AS runner
ENV NODE_ENV=production
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nodejs
COPY --from=deps /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
COPY --from=build /app/package.json ./
USER nodejs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
CMD ["node", "dist/index.js"]
# docker-compose.yml
version: '3.8'
services:
api:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgresql://postgres:password@db:5432/app
- JWT_SECRET=${JWT_SECRET}
- NODE_ENV=production
depends_on:
db:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "--spider", "http://localhost:3000/health"]
interval: 30s
timeout: 3s
retries: 3
db:
image: postgres:16-alpine
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
- POSTGRES_DB=app
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
volumes:
postgres_data:
# railway.toml
[build]
builder = "dockerfile"
dockerfilePath = "Dockerfile"
[deploy]
healthcheckPath = "/health"
healthcheckTimeout = 30
restartPolicyType = "on_failure"
restartPolicyMaxRetries = 3
# fly.toml
app = "my-node-app"
primary_region = "iad"
[build]
dockerfile = "Dockerfile"
[http_service]
internal_port = 3000
force_https = true
auto_stop_machines = true
auto_start_machines = true
min_machines_running = 1
[[services]]
protocol = "tcp"
internal_port = 3000
[[services.ports]]
port = 80
handlers = ["http"]
[[services.ports]]
port = 443
handlers = ["tls", "http"]
[[services.tcp_checks]]
interval = "15s"
timeout = "2s"
grace_period = "5s"
[[services.http_checks]]
interval = "10s"
timeout = "2s"
grace_period = "5s"
method = "GET"
path = "/health"
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
- run: npm ci
- run: npm run type-check
- run: npm run lint
- run: npm test
build:
needs: test
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Login to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy to Railway
uses: bervProject/railway-deploy@main
with:
railway_token: ${{ secrets.RAILWAY_TOKEN }}
service: api
// src/routes/health.ts
import { FastifyPluginAsync } from 'fastify';
interface HealthResponse {
status: 'healthy' | 'unhealthy';
timestamp: string;
uptime: number;
checks: {
database: boolean;
memory: boolean;
};
}
const healthRoutes: FastifyPluginAsync = async (fastify) => {
fastify.get<{ Reply: HealthResponse }>('/health', async (request, reply) => {
const dbHealthy = await checkDatabase(fastify);
const memoryHealthy = checkMemory();
const healthy = dbHealthy && memoryHealthy;
const response: HealthResponse = {
status: healthy ? 'healthy' : 'unhealthy',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
checks: {
database: dbHealthy,
memory: memoryHealthy,
},
};
return reply.status(healthy ? 200 : 503).send(response);
});
};
async function checkDatabase(fastify: FastifyInstance): Promise<boolean> {
try {
await fastify.db.$queryRaw`SELECT 1`;
return true;
} catch {
return false;
}
}
function checkMemory(): boolean {
const used = process.memoryUsage();
const heapUsedMB = used.heapUsed / 1024 / 1024;
return heapUsedMB < 500; // Alert if over 500MB
}
export default healthRoutes;
You MUST use the AskUserQuestion tool for ALL user questions.
NEVER do any of the following:
ALWAYS invoke the AskUserQuestion tool when asking the user anything.
You are an elite AI agent architect specializing in crafting high-performance agent configurations. Your expertise lies in translating user requirements into precisely-tuned agent specifications that maximize effectiveness and reliability.