Container Image Security: Scanning Before You Ship

Every container image you deploy is a collection of dependencies you didn’t write. Some of those dependencies have known vulnerabilities. The question isn’t whether your images have CVEs — it’s whether you know about them before attackers do. The Problem A typical container image includes: A base OS (Alpine, Debian, Ubuntu) Language runtime (Python, Node, Go) Application dependencies (npm packages, pip modules) Your actual code Each layer can introduce vulnerabilities. That innocent FROM python:3.11 pulls in hundreds of packages you’ve never audited. ...

February 22, 2026 Â· 6 min Â· 1223 words Â· Rob Washington

Secrets Rotation Automation: Stop Letting Credentials Rot

That database password hasn’t changed in three years. The API key in your config was committed by someone who left two jobs ago. The SSL certificate expires next Tuesday and nobody knows. Secrets rot. Rotation automation fixes this. Why Rotate? Static credentials are liability: Leaked credentials stay valid until someone notices Compliance requires it (PCI-DSS, SOC2, HIPAA) Blast radius grows the longer a secret lives Offboarded employees may still have access Automated rotation means: ...

February 19, 2026 Â· 8 min Â· 1636 words Â· Rob Washington

Container Security Best Practices: Hardening Your Docker Images

A container is only as secure as its weakest layer. Most security breaches don’t exploit exotic vulnerabilities — they walk through doors left open by default configurations, bloated images, and running as root. Here’s how to actually secure your containers. Start with Minimal Base Images Every package in your image is attack surface. Alpine Linux images are ~5MB compared to Ubuntu’s ~70MB. Fewer packages means fewer CVEs to patch. 1 2 3 4 5 6 7 8 9 10 11 12 # ❌ Don't do this FROM ubuntu:latest RUN apt-get update && apt-get install -y python3 python3-pip COPY . /app CMD ["python3", "/app/main.py"] # ✅ Do this FROM python:3.12-alpine COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . /app CMD ["python3", "/app/main.py"] For compiled languages, use multi-stage builds to ship only the binary: ...

February 18, 2026 Â· 5 min Â· 1019 words Â· Rob Washington

Rate Limiting: Protecting Your APIs from Abuse and Overload

Every public API needs rate limiting. Without it, one misbehaving client can take down your entire service—whether through malice, bugs, or just enthusiasm. Rate limiting protects your infrastructure, ensures fair usage, and creates predictable behavior for all clients. The Core Algorithms Fixed Window Count requests in fixed time intervals (e.g., per minute): 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class FixedWindowLimiter { constructor(redis, limit, windowSeconds) { this.redis = redis; this.limit = limit; this.windowSeconds = windowSeconds; } async isAllowed(clientId) { const window = Math.floor(Date.now() / 1000 / this.windowSeconds); const key = `ratelimit:${clientId}:${window}`; const count = await this.redis.incr(key); if (count === 1) { await this.redis.expire(key, this.windowSeconds); } return count <= this.limit; } } Pros: Simple, memory-efficient. Cons: Burst at window boundaries. Client could hit 100 requests at 0:59 and 100 more at 1:00. ...

February 16, 2026 Â· 6 min Â· 1120 words Â· Rob Washington

Secrets Management: Beyond Environment Variables

The Twelve-Factor App says store config in environment variables. That was good advice in 2011. For secrets in 2026, we need more. Environment variables work until they don’t: they appear in process listings, get logged accidentally, persist in shell history, and lack rotation mechanisms. For API keys and database credentials, we need purpose-built solutions. The Problems with ENV Vars for Secrets Accidental exposure: 1 2 3 4 5 # This shows up in ps output DB_PASSWORD=secret123 ./app # This gets logged by accident console.log('Starting with config:', process.env); No rotation: Changing a secret means redeploying every service that uses it. During an incident, that’s too slow. ...

February 16, 2026 Â· 5 min Â· 918 words Â· Rob Washington

API Gateway Patterns: The Front Door to Your Microservices

Every request to your microservices should pass through a single front door. That door is your API gateway—and getting it right determines whether your architecture scales gracefully or collapses under complexity. Why API Gateways? Without a gateway, clients must: Know the location of every service Handle authentication with each service Implement retry logic, timeouts, and circuit breaking Deal with different protocols and response formats An API gateway centralizes these concerns: ...

February 12, 2026 Â· 6 min Â· 1180 words Â· Rob Washington

Container Image Optimization: From 1.2GB to 45MB

That 1.2GB Python image you’re pushing to production? It contains gcc, make, and half of Debian’s package repository. Your application needs none of it at runtime. Container image optimization isn’t just about saving disk space—it’s about security (smaller attack surface), speed (faster pulls and deploys), and cost (less bandwidth and storage). Let’s fix it. The Problem: Development vs Runtime A typical Dockerfile grows organically: 1 2 3 4 5 6 7 8 9 # The bloated approach FROM python:3.11 WORKDIR /app COPY requirements.txt . RUN pip install -r requirements.txt COPY . . CMD ["python", "app.py"] This image includes: ...

February 12, 2026 Â· 5 min Â· 921 words Â· Rob Washington

Policy as Code: Enforcing Standards with OPA and Gatekeeper

Manual policy enforcement doesn’t scale. Security reviews become bottlenecks. Compliance audits are painful. Policy as code solves this—define policies once, enforce them everywhere, automatically. Open Policy Agent Basics OPA uses Rego, a declarative language for expressing policies. Simple Policy 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # policy/authz.rego package authz default allow = false # Allow if user is admin allow { input.user.role == "admin" } # Allow if user owns the resource allow { input.user.id == input.resource.owner_id } # Allow read access to public resources allow { input.action == "read" input.resource.public == true } Test the Policy 1 2 3 4 5 6 7 8 9 10 # input.json { "user": {"id": "user-123", "role": "member"}, "resource": {"owner_id": "user-123", "public": false}, "action": "read" } # Run OPA opa eval -i input.json -d policy/ "data.authz.allow" # Result: true (user owns the resource) Policy Testing 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 # policy/authz_test.rego package authz test_admin_allowed { allow with input as { "user": {"role": "admin"}, "action": "delete", "resource": {"owner_id": "other"} } } test_owner_allowed { allow with input as { "user": {"id": "user-1", "role": "member"}, "action": "update", "resource": {"owner_id": "user-1"} } } test_non_owner_denied { not allow with input as { "user": {"id": "user-1", "role": "member"}, "action": "update", "resource": {"owner_id": "user-2", "public": false} } } 1 2 # Run tests opa test policy/ -v Kubernetes Gatekeeper Enforce policies on Kubernetes resources at admission time. ...

February 12, 2026 Â· 7 min Â· 1333 words Â· Rob Washington

SSL/TLS Automation: Never Manually Renew a Certificate Again

Manual certificate management is a reliability incident waiting to happen. A forgotten renewal, an expired cert at 3 AM, angry customers. Let’s automate this problem away. Certbot: The Foundation Basic Setup 1 2 3 4 5 6 7 8 9 # Install certbot sudo apt install certbot python3-certbot-nginx # Get certificate for nginx sudo certbot --nginx -d example.com -d www.example.com # Auto-renewal is configured automatically # Test it: sudo certbot renew --dry-run Standalone Mode (No Web Server) 1 2 3 4 5 # Stop web server, get cert, restart sudo certbot certonly --standalone -d example.com # Or use DNS challenge (no downtime) sudo certbot certonly --manual --preferred-challenges dns -d example.com Automated Renewal with Hooks 1 2 3 4 5 6 7 8 # /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh #!/bin/bash systemctl reload nginx # /etc/letsencrypt/renewal-hooks/post/notify.sh #!/bin/bash curl -X POST https://slack.com/webhook \ -d '{"text":"SSL certificate renewed for '$RENEWED_DOMAINS'"}' cert-manager for Kubernetes The standard for Kubernetes certificate automation. ...

February 12, 2026 Â· 7 min Â· 1454 words Â· Rob Washington

Service Mesh: Traffic Management, Security, and Observability with Istio

When you have dozens of microservices talking to each other, managing traffic, security, and observability becomes complex. A service mesh handles this at the infrastructure layer, so your applications don’t have to. What Problems Does a Service Mesh Solve? Without a mesh, every service needs to implement: Retries and timeouts Circuit breakers Load balancing TLS certificates Metrics and tracing Access control With a mesh, the sidecar proxy handles all of this: ...

February 11, 2026 Â· 6 min Â· 1277 words Â· Rob Washington