Devops Deployer
Published by navedanan in background-remover
What this skill does
Sets up deployment pipelines, containerization, and infrastructure for applications. Use when creating CI/CD pipelines, writing Dockerfiles, configuring container orchestration, setting up environment management, handling secrets, or automating deployments. Covers GitHub Actions, Docker, Kubernetes basics, and infrastructure as code patterns.
Add Devops Deployer to your agent
Review the source and files first. When you are ready, copy the prompt instruction or use the CLI command supported by your environment.
Install with a prompt
Paste this into a compatible coding agent:
add this skill "devops-deployer" from https://github.com/navedanan/background-removerInstall with the CLI
Run this command in a controlled environment after reviewing the repository:
npx skills add https://github.com/navedanan/background-remover --skill devops-deployerSkill instructions
DevOps & Deployment
Expert guidance for setting up robust deployment pipelines and infrastructure.
When to Use This Skill
- Creating CI/CD pipelines
- Writing Dockerfiles
- Setting up container orchestration
- Managing environment configurations
- Handling secrets securely
- Automating deployments
- Setting up monitoring and logging
- Infrastructure as code
CI/CD Pipeline Design
Pipeline Stages
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ Build │──▶│ Test │──▶│ Analyze │──▶│ Deploy │──▶│ Verify │
│ │ │ │ │ │ │ Staging │ │ │
└─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘
│
▼
┌─────────┐
│ Deploy │
│ Prod │
└─────────┘
GitHub Actions Workflow
# .github/workflows/ci-cd.yml
name: CI/CD Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
env:
NODE_VERSION: '20'
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
# ============================================
# Build and Test
# ============================================
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Type check
run: npm run type-check
- name: Run tests
run: npm run test:coverage
- name: Upload coverage
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
- name: Build
run: npm run build
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: build
path: dist/
# ============================================
# Security Scanning
# ============================================
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run dependency audit
run: npm audit --audit-level=high
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
ignore-unfixed: true
severity: 'CRITICAL,HIGH'
# ============================================
# Build Docker Image
# ============================================
docker:
needs: [build, security]
runs-on: ubuntu-latest
if: github.event_name == 'push'
permissions:
contents: read
packages: write
outputs:
image-tag: ${{ steps.meta.outputs.tags }}
steps:
- uses: actions/checkout@v4
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: build
path: dist/
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=sha,prefix=
type=ref,event=branch
type=semver,pattern={{version}}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
# ============================================
# Deploy to Staging
# ============================================
deploy-staging:
needs: docker
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/develop'
environment: staging
steps:
- name: Deploy to staging
run: |
echo "Deploying ${{ needs.docker.outputs.image-tag }} to staging"
# Add deployment commands here
# ============================================
# Deploy to Production
# ============================================
deploy-production:
needs: docker
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
environment: production
steps:
- name: Deploy to production
run: |
echo "Deploying ${{ needs.docker.outputs.image-tag }} to production"
# Add deployment commands here
Reusable Workflow
# .github/workflows/reusable-deploy.yml
name: Reusable Deploy
on:
workflow_call:
inputs:
environment:
required: true
type: string
image-tag:
required: true
type: string
secrets:
DEPLOY_TOKEN:
required: true
jobs:
deploy:
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}
steps:
- name: Deploy
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
run: |
echo "Deploying ${{ inputs.image-tag }} to ${{ inputs.environment }}"
Docker Configuration
Multi-Stage Dockerfile (Node.js)
# ============================================
# Stage 1: Dependencies
# ============================================
FROM node:20-alpine AS deps
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install production dependencies
RUN npm ci --only=production
# ============================================
# Stage 2: Builder
# ============================================
FROM node:20-alpine AS builder
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install all dependencies
RUN npm ci
# Copy source code
COPY . .
# Build application
RUN npm run build
# ============================================
# Stage 3: Runner
# ============================================
FROM node:20-alpine AS runner
WORKDIR /app
# Create non-root user
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 appuser
# Set environment
ENV NODE_ENV=production
# Copy production dependencies
COPY --from=deps /app/node_modules ./node_modules
# Copy built application
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package.json ./
# Change ownership
RUN chown -R appuser:nodejs /app
# Switch to non-root user
USER appuser
# Expose port
EXPOSE 3000
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
# Start application
CMD ["node", "dist/index.js"]
Multi-Stage Dockerfile (Python)
# ============================================
# Stage 1: Builder
# ============================================
FROM python:3.12-slim AS builder
WORKDIR /app
# Install build dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
&& rm -rf /var/lib/apt/lists/*
# Create virtual environment
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
# Copy and install requirements
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# ============================================
# Stage 2: Runner
# ============================================
FROM python:3.12-slim AS runner
WORKDIR /app
# Create non-root user
RUN useradd --create-home --shell /bin/bash appuser
# Copy virtual environment
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
# Copy application code
COPY --chown=appuser:appuser . .
# Switch to non-root user
USER appuser
# Expose port
EXPOSE 8000
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')" || exit 1
# Start application
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
Docker Compose (Development)
# docker-compose.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
target: builder # Use builder stage for dev
volumes:
- .:/app
- /app/node_modules # Preserve node_modules
ports:
- "3000:3000"
environment:
- NODE_ENV=development
- DATABASE_URL=postgresql://postgres:postgres@db:5432/myapp
- REDIS_URL=redis://redis:6379
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
command: npm run dev
db:
image: postgres:16-alpine
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=myapp
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
volumes:
postgres_data:
redis_data:
Docker Compose (Production)
# docker-compose.prod.yml
version: '3.8'
services:
app:
image: ghcr.io/myorg/myapp:${IMAGE_TAG:-latest}
restart: unless-stopped
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=${DATABASE_URL}
- REDIS_URL=${REDIS_URL}
deploy:
replicas: 3
resources:
limits:
cpus: '0.5'
memory: 512M
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
Environment Management
Environment Configuration
# .env.example (commit this)
# Application
NODE_ENV=development
PORT=3000
LOG_LEVEL=debug
# Database
DATABASE_URL=postgresql://user:password@localhost:5432/myapp
# Redis
REDIS_URL=redis://localhost:6379
# Authentication
JWT_SECRET=your-secret-here
JWT_EXPIRES_IN=1h
# External Services
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USER=
SMTP_PASSWORD=
# Feature Flags
FEATURE_NEW_DASHBOARD=false
Environment Validation
// config/env.ts
import { z } from 'zod';
const envSchema = z.object({
NODE_ENV: z.enum(['development', 'test', 'production']),
PORT: z.string().transform(Number).default('3000'),
DATABASE_URL: z.string().url(),
REDIS_URL: z.string().url(),
JWT_SECRET: z.string().min(32),
JWT_EXPIRES_IN: z.string().default('1h'),
});
export type Env = z.infer<typeof envSchema>;
function validateEnv(): Env {
const result = envSchema.safeParse(process.env);
if (!result.success) {
console.error('❌ Invalid environment variables:');
console.error(result.error.format());
process.exit(1);
}
return result.data;
}
export const env = validateEnv();
Secrets Management
GitHub Secrets Configuration
# Reference secrets in workflows
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
API_KEY: ${{ secrets.API_KEY }}
# Use environments for different secrets per stage
jobs:
deploy:
environment: production # Uses production secrets
HashiCorp Vault Integration
# .github/workflows/vault-deploy.yml
jobs:
deploy:
steps:
- name: Import Secrets
uses: hashicorp/vault-action@v2
with:
url: ${{ secrets.VAULT_URL }}
method: approle
roleId: ${{ secrets.VAULT_ROLE_ID }}
secretId: ${{ secrets.VAULT_SECRET_ID }}
secrets: |
secret/data/myapp DATABASE_URL | DATABASE_URL ;
secret/data/myapp API_KEY | API_KEY
- name: Deploy
env:
DATABASE_URL: ${{ env.DATABASE_URL }}
API_KEY: ${{ env.API_KEY }}
run: ./deploy.sh
Kubernetes Secrets
# k8s/secrets.yaml (use sealed-secrets or external-secrets in practice)
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
type: Opaque
stringData:
DATABASE_URL: "postgresql://..."
JWT_SECRET: "..."
---
# Reference in deployment
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: app
envFrom:
- secretRef:
name: app-secrets
Kubernetes Basics
Deployment Configuration
# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
labels:
app: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: ghcr.io/myorg/myapp:latest
ports:
- containerPort: 3000
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "512Mi"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
env:
- name: NODE_ENV
value: "production"
envFrom:
- secretRef:
name: app-secrets
- configMapRef:
name: app-config
---
apiVersion: v1
kind: Service
metadata:
name: myapp
spec:
selector:
app: myapp
ports:
- port: 80
targetPort: 3000
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
tls:
- hosts:
- myapp.example.com
secretName: myapp-tls
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp
port:
number: 80
ConfigMap
# k8s/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
LOG_LEVEL: "info"
FEATURE_FLAGS: |
{
"newDashboard": true,
"betaFeatures": false
}
Monitoring Setup
Health Check Endpoints
// routes/health.ts
app.get('/health', (req, res) => {
res.status(200).json({ status: 'healthy' });
});
app.get('/ready', async (req, res) => {
try {
// Check database connection
await db.query('SELECT 1');
// Check Redis connection
await redis.ping();
res.status(200).json({ status: 'ready' });
} catch (error) {
res.status(503).json({
status: 'not ready',
error: error.message
});
}
});
Prometheus Metrics
// metrics/prometheus.ts
import { Registry, Counter, Histogram } from 'prom-client';
const register = new Registry();
export const httpRequestsTotal = new Counter({
name: 'http_requests_total',
help: 'Total number of HTTP requests',
labelNames: ['method', 'path', 'status'],
registers: [register],
});
export const httpRequestDuration = new Histogram({
name: 'http_request_duration_seconds',
help: 'HTTP request duration in seconds',
labelNames: ['method', 'path'],
buckets: [0.1, 0.5, 1, 2, 5],
registers: [register],
});
// Middleware
app.use((req, res, next) => {
const end = httpRequestDuration.startTimer({
method: req.method,
path: req.path
});
res.on('finish', () => {
httpRequestsTotal.inc({
method: req.method,
path: req.path,
status: res.statusCode
});
end();
});
next();
});
// Metrics endpoint
app.get('/metrics', async (req, res) => {
res.set('Content-Type', register.contentType);
res.end(await register.metrics());
});
Deployment Strategies
Blue-Green Deployment
┌─────────────────┐
│ Load Balancer │
└────────┬────────┘
│
┌────┴────┐
▼ ▼
┌───────┐ ┌───────┐
│ Blue │ │ Green │
│ (v1) │ │ (v2) │
│ LIVE │ │ IDLE │
└───────┘ └───────┘
After testing Green:
- Switch traffic to Green
- Blue becomes idle (rollback target)
Canary Deployment
┌─────────────────┐
│ Load Balancer │
└────────┬────────┘
│
┌────┴────┐
▼ ▼
┌───────┐ ┌───────┐
│Stable │ │Canary │
│ (v1) │ │ (v2) │
│ 90% │ │ 10% │
└───────┘ └───────┘
Gradually increase canary traffic:
10% → 25% → 50% → 100%
Rolling Update (Kubernetes)
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 25%
maxSurge: 25%
Output Artifacts
When this skill is activated, I can help create:
- CI/CD Pipeline: GitHub Actions, GitLab CI, or other CI configs
- Dockerfile: Optimized multi-stage builds
- Docker Compose: Development and production configurations
- Kubernetes Manifests: Deployments, services, ingress
- Environment Configuration: Env files and validation
- Secrets Management: Vault, sealed-secrets setup
- Monitoring Setup: Health checks, metrics, logging
- Deployment Scripts: Automation scripts for deployments
Files included
- SKILL.md

