SSH Config Patterns for Managing Multiple Servers

If you’re still typing ssh -i ~/.ssh/mykey.pem ec2-user@ec2-54-123-45-67.compute-1.amazonaws.com, you’re working too hard. SSH config transforms verbose commands into simple ssh prod invocations. The Basics Create or edit ~/.ssh/config: H o s t H U I p o s d r s e e o t r n d N t a e i m c t e 2 y - F 5 u i 4 s l . e e 1 r 2 ~ 3 / . . 4 s 5 s . h 6 / 7 p r o d - k e y . p e m Now ssh prod connects with the right key and user. No more remembering IP addresses. ...

February 28, 2026 Â· 16 min Â· 3216 words Â· Rob Washington

Makefiles for Modern Development Workflows

Makefiles are ancient. They’re also incredibly useful for modern development. Here’s how to use them as your project’s command center. Why Make in 2026? Every project has commands you run repeatedly: Start development servers Run tests Build containers Deploy to environments Format and lint code You could remember them all. Or document them in a README that gets stale. Or put them in a Makefile where they’re executable documentation. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 .PHONY: dev test build deploy dev: docker-compose up -d npm run dev test: pytest -v build: docker build -t myapp:latest . deploy: kubectl apply -f k8s/ Now make dev starts everything. New team member? Run make help. ...

February 28, 2026 Â· 6 min Â· 1075 words Â· Rob Washington

Environment Variable Patterns for 12-Factor Apps

Environment variables are the 12-factor way to configure applications. But “just use env vars” glosses over real complexity. Here’s how to do it well. The Basics Done Right Type Coercion Environment variables are always strings. Handle conversion explicitly: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import os def get_env_bool(key: str, default: bool = False) -> bool: value = os.getenv(key, "").lower() if value in ("true", "1", "yes", "on"): return True if value in ("false", "0", "no", "off"): return False return default def get_env_int(key: str, default: int = 0) -> int: try: return int(os.getenv(key, default)) except ValueError: return default # Usage DEBUG = get_env_bool("DEBUG", False) PORT = get_env_int("PORT", 8080) WORKERS = get_env_int("WORKERS", 4) Required vs Optional Fail fast on missing required config: ...

February 28, 2026 Â· 5 min Â· 994 words Â· Rob Washington

Container Health Check Patterns That Actually Work

Your container says it’s healthy. Your users say the app is broken. Sound familiar? Basic health checks only tell you if a process is running. Here’s how to build checks that catch real problems. Beyond “Is It Alive?” Most health checks look like this: 1 HEALTHCHECK CMD curl -f http://localhost:8080/health || exit 1 This tells you the HTTP server responds. It doesn’t tell you: Can the app reach the database? Is the cache connected? Are critical background workers running? Is the disk filling up? Layered Health Checks Implement three levels: ...

February 28, 2026 Â· 4 min Â· 744 words Â· Rob Washington

htop: Process Monitoring for Humans

top works. htop works better. It’s colorful, interactive, and actually pleasant to use. Here’s how to get the most from it. Installation 1 2 3 4 5 6 7 8 # Debian/Ubuntu sudo apt install htop # RHEL/CentOS/Fedora sudo dnf install htop # macOS brew install htop The Interface Launch with htop. You’ll see: Top section: CPU bars (one per core) Memory and swap usage Tasks, load average, uptime Process list: ...

February 27, 2026 Â· 4 min Â· 664 words Â· Rob Washington

Git Hooks: Automate Quality Before It's Too Late

Code review catches problems. CI catches problems. But the fastest feedback loop? Catching problems before you even commit. Git hooks run scripts at key points in your workflow. Use them to lint, test, and validate — automatically. Where Hooks Live 1 2 3 4 5 6 ls .git/hooks/ # applypatch-msg.sample pre-commit.sample # commit-msg.sample pre-push.sample # post-update.sample pre-rebase.sample # pre-applypatch.sample prepare-commit-msg.sample # pre-merge-commit.sample update.sample Remove .sample to activate. Hooks must be executable (chmod +x). ...

February 27, 2026 Â· 6 min Â· 1159 words Â· Rob Washington

Linux Signals: Graceful Shutdowns and Process Control

Your application is running in production. You need to restart it for a config change. Do you: A) kill -9 and hope for the best B) Send a signal it can handle gracefully If you picked A, you’ve probably lost data. Let’s fix that. The Essential Signals Signal Number Default Action Use Case SIGTERM 15 Terminate Graceful shutdown request SIGINT 2 Terminate Ctrl+C, interactive stop SIGHUP 1 Terminate Config reload (by convention) SIGKILL 9 Terminate Force kill (cannot be caught) SIGUSR1/2 10/12 Terminate Application-defined SIGCHLD 17 Ignore Child process state change SIGTERM is the polite ask. “Please shut down when convenient.” SIGKILL is the eviction notice. No cleanup, no saving state, immediate death. ...

February 27, 2026 Â· 5 min Â· 985 words Â· Rob Washington

Cron Jobs That Don't Wake You Up at Night

Cron is deceptively simple. Five fields, a command, done. Until your job runs twice simultaneously, silently fails for a week, or fills your disk with output nobody reads. Here’s how to write cron jobs that actually work in production. The Basics Done Right 1 2 3 4 5 6 7 8 # Bad: No logging, no error handling 0 * * * * /opt/scripts/backup.sh # Better: Redirect output, capture errors 0 * * * * /opt/scripts/backup.sh >> /var/log/backup.log 2>&1 # Best: Timestamped logging with chronic 0 * * * * chronic /opt/scripts/backup.sh chronic (from moreutils) only outputs when the command fails. Perfect for cron — silent success, loud failure. ...

February 27, 2026 Â· 5 min Â· 896 words Â· Rob Washington

Structured Logging: Stop Grepping, Start Querying

Unstructured logs are a trap. They look simple until you need to find something. [ [ [ 2 2 2 0 0 0 2 2 2 6 6 6 - - - 0 0 0 2 2 2 - - - 2 2 2 7 7 7 0 0 0 5 5 5 : : : 3 3 3 0 0 0 : : : 1 1 1 5 6 7 ] ] ] I E W N R A F R R O O N R U H s F i e a g r i h l j e m o d e h m n t o @ o r e y x p a r u m o s p c a l e g e s e . s c d o o e m r t d e l e c o r t g e g 1 d e 2 : d 3 4 8 i 5 7 n : % f c r o o n m n e 1 c 9 t 2 i . o 1 n 6 8 t . i 1 m . e 5 o 0 u t Quick: find all login failures from a specific IP range in the last hour. Now try parsing the order ID from error messages. Hope you enjoy regex. ...

February 27, 2026 Â· 6 min Â· 1228 words Â· Rob Washington

Shell Scripting Patterns That Prevent 3 AM Pages

Every ops engineer has a story about a shell script that worked perfectly — until it didn’t. Usually at 3 AM. Usually in production. These patterns won’t make your scripts bulletproof, but they’ll stop the most common failures. Start Every Script Right 1 2 3 #!/usr/bin/env bash set -euo pipefail IFS=$'\n\t' This preamble should be muscle memory: set -e: Exit on any error (non-zero return code) set -u: Exit on undefined variables set -o pipefail: Catch errors in pipes (not just the last command) IFS=$'\n\t': Safer word splitting (no spaces) Without these, a typo like rm -rf $UNSET_VAR/ could wipe your root filesystem. ...

February 27, 2026 Â· 5 min Â· 1001 words Â· Rob Washington