Everything, not just what fits on the homepage — two production case studies
alongside three smaller side builds. Each links out to a full write-up.
URL Shortener on ECS Fargate
In progress
A Python/FastAPI URL shortener being built in phases from an empty AWS account,
targeting ECS Fargate provisioned by Terraform, with a GitHub Actions pipeline and
CloudWatch observability in later phases. Phases 0 and 1 are done: IAM bootstrap,
and a multi-stage Docker build running as a non-root user — 230MB versus the 1GB+
a naive single-stage build produces.
The build log is the point. It records the real reasoning — why
root credentials can't be scoped down the way an IAM identity can, why health
checks belong on a dedicated dependency-light route rather than a business-logic
one — and the mistakes as they happened, including a credential exposure that led
to adding gitleaks pre-commit scanning, and the discovery that
gitleaks' AWS rule doesn't flag a bare AKIA key without valid
checksum structure. Most portfolios show only the polished result.
Full write-up
Source & build log go public with the finished project — early access on request via email.
DevOps Engineering Portfolio
Live
Three sanitized case studies from production work at Keel Digital. Each documents
the actual constraints and the reasoning behind the architecture that answered
them, with before/after diagrams rather than just the finished state.
Proxmox bare-metal migration
14 DigitalOcean droplets migrated to a single OVHcloud bare-metal box behind a
NAT gateway — ~$1,500 CAD/month saved, no VM left with a public IP.
Self-managed OpenSearch SIEM
40+ services, Sigma detection rules, and tiered retention for a HIPAA/FedRAMP-scoped
environment — data residency ruled out a third-party SIEM.
OPKSSH dev access
Static SSH keys replaced with OPKSSH-issued, Google Workspace-bound certificates
for tiered dev access — offboarding no longer depends on someone remembering to
edit authorized_keys.
Keycloak Monitoring via Zabbix
Live
A Zabbix template and alert pipeline for Keycloak's Prometheus metrics, built to
avoid standing up a separate Prometheus/Alertmanager stack just to watch one
service. A single HTTP master item scrapes the metrics endpoint every minute;
dependent items extract series using Zabbix's Prometheus preprocessing, so
identity monitoring lives in the same place — and the same on-call rotation — as
everything else.
Triggers watch for what actually matters: failed-login spikes,
service-to-service auth errors that a quietly rotated client secret would cause,
and the metrics endpoint going dark. Alerts route to Google Chat through a custom
webhook media type, and a CI job lints the template on every push.
Terraform Module: Scheduled Lambda + EventBridge
Live
A Terraform module for the ops-automation shape I kept rebuilding by hand: a
Lambda function on an EventBridge schedule, with the IAM and logging defaults
done correctly instead of copy-pasted. The execution role is scoped to just this
function's log group rather than the AWS-managed policy's account-wide
logs:* grant, and the CloudWatch log group is created explicitly so
retention is enforced from the first invocation.
The module packages a pre-built Lambda zip rather than building it — CI stays out
of the module and in the pipeline where it belongs. Ships with a working example,
tflint checks, and release-please for versioned releases.
Grocery Bot: Meal Planning on GitHub Actions
Live
Weekly meal-planning and grocery-list automation with zero hosting cost — the whole
thing runs as two scheduled GitHub Actions jobs talking to a Discord channel. Sunday
morning, one workflow posts the week's meal options from a YAML file and adds a
reaction emoji per meal; family votes by reacting during the day; Sunday evening, a
second workflow reads the reactions, maps each pick back to its ingredients, and
posts an aggregated, deduped grocery list.
The two-run design exists because GitHub Actions can't listen for replies in real
time — reactions become the persistence layer instead of a live bot process. The
morning post's message ID is committed back to the repo so the evening run can find
it, and Discord is driven through a small hand-rolled REST wrapper (with 429
retry handling) rather than a full bot framework — no gateway connection, no
privileged intents. Ingredients live in a manually maintained YAML pool instead of
a recipe API: exact quantities, no rate limits, no extra keys.