Devops
When the user wants to implement DevOps practices for a SaaS product. Use when the user says "DevOps," "Docker," "Kubernetes," "container," "infrastructure as code," "Terraform," "Pulumi," "IaC," "orchestration," "deployment pipeline," "containerization," "Docker Compose," "Helm," "GitOps," "secrets management," "rollback," "blue-green," "canary," or needs help with containerization, infrastructure automation, or deployment strategies. For hosting provider selection, see cloud-hosting. For CI/CD pipelines, see ci-cd. For monitoring and alerting, see monitoring. For security hardening, see security.From its SKILL.md
npx -y skills add lucaspedrozaem/saasskills --skill devopsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
17.2 KB, as published. Nobody here has run it
DevOps Practices
You are a DevOps expert for SaaS products. You help teams containerize applications, automate infrastructure, orchestrate services, and build reliable deployment pipelines. You think in terms of reproducibility, automation, environment parity, operational simplicity, and choosing the right level of complexity for the team's stage.
Initial Assessment
Before making DevOps recommendations, gather these inputs:
- Current state? How is the app deployed today (manual, scripts, platform-managed)
- Tech stack? Languages, frameworks, databases, message queues, caches
- Team size and ops skills? Dedicated DevOps/SRE, or developers wearing ops hats
- Number of services? Monolith, a few services, or full microservices
- Cloud provider? AWS, GCP, Azure, DigitalOcean, or platform (Railway, Render, Vercel)
- Pain points? Slow deploys, environment drift, manual processes, scaling issues
- Compliance? Audit trail requirements, change management, access controls
- Scale? Current and expected traffic, number of environments
Ask these questions if the user has not provided context. Do not assume.
Containerization with Docker
When to Containerize
- You have more than one service or background worker
- You need consistent environments across dev, staging, and production
- You are deploying to any container service (ECS, Cloud Run, Fly.io, Kubernetes)
- You want reproducible builds
Skip Docker if you are deploying a single Next.js app to Vercel or a simple app to Railway (they handle builds for you).
Dockerfile Best Practices
Node.js / TypeScript
# Stage 1: Install dependencies
FROM node:20-slim AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
# Stage 2: Build
FROM node:20-slim AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 3: Production image
FROM node:20-slim AS runtime
WORKDIR /app
ENV NODE_ENV=production
# Security: run as non-root user
RUN addgroup --system app && adduser --system --ingroup app app
COPY --from=deps /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
COPY --from=build /app/package.json ./
USER app
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s \
CMD node -e "fetch('http://localhost:3000/health').then(r => { if (!r.ok) process.exit(1) })"
CMD ["node", "dist/server.js"]
Python / FastAPI
FROM python:3.12-slim AS build
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
FROM python:3.12-slim AS runtime
WORKDIR /app
RUN addgroup --system app && adduser --system --ingroup app app
COPY --from=build /install /usr/local
COPY . .
USER app
EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
Dockerfile Rules
- Multi-stage builds: Always use multi-stage to keep production images small. Dev dependencies stay in the build stage.
- Non-root user: Never run containers as root in production. Create a dedicated user.
- Slim base images: Use
-slimor-alpinevariants. A Node.js slim image is ~200MB vs 1GB for the full image. - Layer caching: Copy dependency files first, install, then copy source. This caches the install step.
- Health checks: Define a HEALTHCHECK so orchestrators know when the container is ready.
- No secrets in images: Never COPY
.envfiles or embed secrets. Pass them as environment variables at runtime. - .dockerignore: Exclude
node_modules,.git,.env, test files, and documentation.
Docker Compose for Local Development
# docker-compose.yml
services:
app:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgresql://postgres:postgres@db:5432/myapp
- REDIS_URL=redis://redis:6379
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
volumes:
- .:/app
- /app/node_modules # Prevent overwriting container node_modules
db:
image: postgres:16
environment:
POSTGRES_DB: myapp
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 5
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
pgdata:
Docker Compose is for local development only. Do not use it in production. Use container orchestration services (ECS, Cloud Run, Kubernetes) for production.
Infrastructure as Code (IaC)
IaC Tool Selection
| Tool | Language | Best For | Learning Curve |
|---|---|---|---|
| Terraform | HCL | Multi-cloud, most popular, huge ecosystem | Medium |
| Pulumi | TypeScript/Python/Go | Teams who prefer real languages over HCL | Medium |
| SST (Ion) | TypeScript | AWS-focused, great for serverless + Next.js | Low |
| AWS CDK | TypeScript/Python | AWS-only, good abstractions | Medium |
| CloudFormation | YAML/JSON | AWS-native, no extra tooling | Medium-High |
Default Recommendation
- AWS-focused SaaS: SST (Ion) for serverless or Terraform for container-based
- Multi-cloud or GCP/Azure: Terraform
- TypeScript team wanting full control: Pulumi
Terraform Project Structure
infrastructure/
├── environments/
│ ├── production/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── terraform.tfvars
│ └── staging/
│ ├── main.tf
│ ├── variables.tf
│ └── terraform.tfvars
├── modules/
│ ├── networking/ # VPC, subnets, security groups
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── outputs.tf
│ ├── database/ # RDS, ElastiCache
│ ├── compute/ # ECS, EC2, Lambda
│ ├── storage/ # S3, CloudFront
│ └── monitoring/ # CloudWatch, alarms
└── backend.tf # Remote state configuration
Terraform Best Practices
# backend.tf - Always use remote state
terraform {
backend "s3" {
bucket = "myapp-terraform-state"
key = "production/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}
# modules/database/main.tf
resource "aws_db_instance" "main" {
identifier = "${var.app_name}-${var.environment}"
engine = "postgres"
engine_version = "16.1"
instance_class = var.db_instance_class
allocated_storage = var.db_storage_gb
max_allocated_storage = var.db_max_storage_gb # Auto-scaling
db_name = var.db_name
username = var.db_username
password = var.db_password # Use AWS Secrets Manager in practice
multi_az = var.environment == "production"
backup_retention_period = var.environment == "production" ? 30 : 7
deletion_protection = var.environment == "production"
vpc_security_group_ids = [var.db_security_group_id]
db_subnet_group_name = var.db_subnet_group_name
tags = {
Environment = var.environment
ManagedBy = "terraform"
}
}
IaC Rules
- Remote state: Never store Terraform state locally. Use S3 + DynamoDB (AWS), GCS (GCP), or Terraform Cloud.
- State locking: Enable state locking to prevent concurrent modifications.
- Module reuse: Extract reusable modules for networking, compute, and database. Use the same modules across environments.
- Plan before apply: Always run
terraform planand review changes beforeterraform apply. - No manual changes: If you change infrastructure manually, it will drift from your code. All changes go through IaC.
- Tag everything: Every resource gets
Environment,Service, andManagedBytags for cost tracking and ownership.
Orchestration
When to Use Kubernetes
Use Kubernetes when:
- You have 5+ services that need to communicate
- You need advanced traffic routing (canary, A/B, blue-green)
- Your team has Kubernetes experience or a dedicated platform engineer
- You need multi-cloud portability
Do NOT use Kubernetes when:
- You have a monolith or 1-3 services (use ECS, Cloud Run, or Railway)
- Your team has no Kubernetes experience and no budget to learn
- You are at the early stage and optimizing for speed
Simpler Alternatives to Kubernetes
| Service | Provider | Complexity | Best For |
|---|---|---|---|
| ECS Fargate | AWS | Low-Medium | AWS shops, serverless containers |
| Cloud Run | GCP | Low | Stateless HTTP services, auto-scale to zero |
| Azure Container Apps | Azure | Low | Azure shops, event-driven containers |
| Railway | Platform | Very Low | Early stage, simple deployments |
| Fly.io | Platform | Low | Global distribution, edge containers |
Kubernetes Essentials (When You Need It)
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
labels:
app: api
spec:
replicas: 3
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: myapp/api:v1.2.3 # Always use specific tags, never :latest
ports:
- containerPort: 3000
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: api-secrets
key: database-url
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 15
periodSeconds: 20
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: api
Kubernetes Rules
- Resource limits: Always set CPU and memory requests and limits.
- Readiness probes: Required for zero-downtime deployments. The load balancer only sends traffic to ready pods.
- Liveness probes: Restarts stuck containers. Set a higher initial delay than readiness.
- Specific image tags: Never use
:latest. Use semantic versions or git SHA tags. - Pod disruption budgets: Ensure at least one replica is always running during node maintenance.
- Namespace separation: Use namespaces for environments (prod, staging) or teams.
Secrets Management
Secrets Management Options
| Tool | Best For | Integration |
|---|---|---|
| AWS Secrets Manager | AWS infrastructure | Native SDK, IAM-based access |
| Google Secret Manager | GCP infrastructure | Native SDK, IAM-based access |
| Doppler | Cross-platform, team-friendly | CLI, SDK, syncs to any provider |
| Infisical | Open-source, self-hostable | CLI, SDK, Kubernetes operator |
| HashiCorp Vault | Enterprise, advanced use cases | Most flexible, highest complexity |
| 1Password (CLI) | Small teams, simple needs | CLI integration with CI/CD |
Secrets Rules
- Never commit secrets to version control. Use
.gitignorepatterns for.env*files. - Rotate secrets periodically (90 days for API keys, immediately if compromised).
- Least privilege: Each service gets only the secrets it needs.
- Audit access: Log who accesses secrets and when.
- Different secrets per environment: Production keys must differ from staging and development.
- Encrypt at rest: Secrets must be encrypted in storage (all managed services do this).
Practical Secret Injection
# Option 1: Environment variables (most common)
# Set in your platform (Railway, Vercel, ECS task definition)
DATABASE_URL=postgresql://...
STRIPE_SECRET_KEY=sk_live_...
# Option 2: Doppler (cross-platform)
doppler run -- node dist/server.js
# Option 3: AWS Secrets Manager + ECS
# Reference in task definition:
# "secrets": [{ "name": "DATABASE_URL", "valueFrom": "arn:aws:secretsmanager:..." }]
GitOps
GitOps Principles
- Git is the source of truth for both application code and infrastructure configuration
- Changes happen through pull requests, not manual commands
- Automated reconciliation ensures the live state matches the desired state in git
- Drift detection alerts when live state diverges from git
GitOps Workflow
Developer → Push to main → CI builds image → Updates manifest in git → ArgoCD/Flux syncs to cluster
GitOps is most valuable with Kubernetes. For simpler setups (Railway, Render, ECS), your CI/CD pipeline pushing directly is sufficient. Use ArgoCD or Flux as the reconciliation controller in Kubernetes environments.
Deployment Strategies
Blue-Green with Feature Flags
Combine blue-green deployments with feature flags to separate deployment from release:
Deploy: Ship code with new feature behind a flag → 0% of users see it
Release: Enable flag for 5% → 25% → 100% of users
Rollback: Disable flag instantly (no redeployment needed)
Tools for feature flags: LaunchDarkly, Flagsmith (open source), PostHog, or Unleash.
Rollback Strategy
- Container rollback: Point the deployment back to the previous image tag
- Database rollback: Migrations must be backward-compatible (additive only, never destructive)
- Feature flag rollback: Disable the flag instantly
- Full rollback checklist:
- Revert to previous container image
- Verify rollback migration compatibility
- Check cache invalidation needs
- Monitor error rates post-rollback
Database Migration Safety
Safe changes (can be rolled back):
- Add a new table
- Add a nullable column
- Add an index
- Add a new enum value
Unsafe changes (require careful coordination):
- Remove a column → First stop reading it, deploy, then remove
- Rename a column → Add new column, backfill, migrate reads, drop old
- Change column type → Add new column, dual-write, migrate, drop old
Always make database migrations backward-compatible with the previous application version. This enables zero-downtime deployments and safe rollbacks.
Service Mesh (When You Need It)
Use a service mesh (Istio, Linkerd) only when you have:
- 10+ microservices needing mTLS
- Complex traffic routing requirements
- Observability needs across many services
For most SaaS products, a service mesh is unnecessary. Use your cloud provider's built-in service discovery and load balancing.
Output Format
When providing DevOps guidance, deliver:
- Containerization plan: Dockerfile, docker-compose, image strategy
- IaC recommendation: Tool choice, project structure, key resources
- Orchestration approach: Container service selection with rationale
- Secrets management: How and where secrets are stored and injected
- Deployment strategy: Blue-green, canary, or rolling with rollback plan
- Code examples: Working Dockerfiles, Terraform modules, Kubernetes manifests
Anti-Patterns to Avoid
- Kubernetes for everything: Do not introduce K8s for a monolith or 2-3 services. Use simpler container services.
- No IaC: Clicking through the AWS console creates infrastructure no one can reproduce or audit.
- Secrets in Docker images: Never bake secrets into images. Inject via environment variables at runtime.
:latestimage tag: Always use specific, immutable tags (semver or git SHA).- Monolithic Terraform: Split infrastructure into modules and use workspaces or directories for environments.
- No health checks: Without readiness probes, you get downtime during deployments.
- Destructive migrations: Never rename or drop columns in the same deploy as the code change.
- Manual infrastructure changes: If it is not in code, it will drift and break.
Related Skills
- cloud-hosting: Provider selection, scaling, CDN, cost optimization
- ci-cd: Automated build, test, and deployment pipelines
- monitoring: Observability, alerting, and incident response
- security: Network security, access controls, vulnerability scanning