Skip to content

DevOps

A Self-Hosted Datadog Alternative in One Docker Compose Command

TL;DR: I built a full observability stack -- metrics, logs, traces, alerting, and AI anomaly detection -- that starts with a single docker compose up -d.

The Problem

Datadog is excellent but expensive. At $23+ per host per month for infrastructure monitoring alone, costs scale fast when you're running multiple environments. I needed production-grade observability -- not a toy Grafana dashboard -- without the SaaS bill. And I wanted it deployable in under two minutes for any new project.

What I Built

observability-stack bundles Prometheus, Grafana, Loki, Tempo, Alertmanager, Node Exporter, cAdvisor, and a custom AI anomaly analyzer into a single Docker Compose file. Every component is pre-wired: Grafana has datasources and dashboards provisioned on first boot, Prometheus scrapes all the right targets, and Alertmanager routes alerts through the AI analyzer before sending notifications.

  • Full metrics pipeline -- Prometheus + Node Exporter + cAdvisor with pre-built dashboards for host and container metrics
  • Centralized logging -- Loki ingests logs from all containers; query them directly in Grafana with LogQL
  • Distributed tracing -- Tempo receives OTLP traces over gRPC (port 4317) and correlates them with logs and metrics
  • AI anomaly detection -- a Python service connected to AWS Bedrock analyzes alert patterns and identifies root causes
  • Pre-built dashboards -- Node Overview (CPU, memory, disk, network) and Container Overview (per-container resource usage) ready on boot

Tech Stack

Docker Compose, Prometheus, Grafana, Loki, Tempo, Alertmanager, Node Exporter, cAdvisor, Python, AWS Bedrock

Usage

cp .env.example .env   # configure credentials
docker compose up -d   # start everything

# Access points:
# Grafana:      http://localhost:3000  (admin/admin)
# Prometheus:   http://localhost:9090
# Alertmanager: http://localhost:9093
# AI Analyzer:  http://localhost:8000

Add a custom scrape target:

# configs/prometheus/prometheus.yml
scrape_configs:
  - job_name: "my-service"
    static_configs:
      - targets: ["my-service:8080"]

Results

New projects get full observability in under two minutes. The pre-wired dashboards mean I stop building the same Grafana panels from scratch every time. The AI analyzer has caught two subtle memory leak patterns that static thresholds missed -- it correlates metric trends with log spikes in ways that simple alerting rules cannot.


GitHub: https://github.com/basel5001/observability-stack

Production-Grade Reusable Terraform Modules for AWS

TL;DR: I created a library of four battle-tested Terraform modules -- VPC, EKS, Lambda, and S3 -- with sane defaults, full input/output documentation, and ready-to-use examples.

The Problem

Every new AWS project starts the same way: copy a VPC module from the last project, tweak the CIDR blocks, fix the broken NAT gateway config, remember to enable flow logs this time. Then do the same dance for EKS, Lambda, and S3. I was spending the first day of every project rebuilding infrastructure primitives that should have been solved once.

What I Built

terraform-aws-modules is a collection of four production-ready Terraform modules designed to be referenced directly from GitHub. Each module follows a consistent interface, includes sensible defaults for production use, and exposes enough variables for customization without overwhelming the consumer. Every module has a corresponding example in the examples/ directory.

  • VPC module -- 2-AZ public/private subnets, Internet Gateway, NAT Gateways, VPC Flow Logs, fully configurable CIDRs
  • EKS module -- managed node groups, IRSA (IAM Roles for Service Accounts) via OIDC provider, CloudWatch logging, configurable instance types and scaling
  • Lambda module -- function deployment with IAM role, CloudWatch log group, optional VPC attachment, and event source mappings (SQS, API Gateway, EventBridge)
  • S3 module -- versioning, AES-256 encryption, public access block, lifecycle rules for cost management
  • Consistent interface -- every module takes project_name, environment, and tags; outputs are predictable and composable

Tech Stack

Terraform, AWS (VPC, EKS, Lambda, S3, IAM, CloudWatch), HCL

Usage

module "vpc" {
  source = "github.com/basel5001/terraform-aws-modules//modules/vpc?ref=v1.0.0"

  project_name = "my-app"
  environment  = "prod"
  vpc_cidr     = "10.0.0.0/16"
}

module "eks" {
  source = "github.com/basel5001/terraform-aws-modules//modules/eks?ref=v1.0.0"

  cluster_name  = "my-app-prod"
  environment   = "prod"
  vpc_id        = module.vpc.vpc_id
  subnet_ids    = module.vpc.private_subnet_ids
  node_min_size = 2
  node_max_size = 6
}

Results

New projects reach a working VPC + EKS cluster in under 30 minutes instead of a full day. The modules encode production best practices by default -- flow logs are on, public access blocks are set, encryption is enabled. I've used these across seven projects without modification. When I do need to update a default (e.g., bumping the EKS version), one PR to the module repo updates all consumers on their next terraform init -upgrade.


GitHub: https://github.com/basel5001/terraform-aws-modules

Migrating a Full-Stack CI/CD Pipeline from CircleCI to GitHub Actions

TL;DR: I took a full-stack application with a TypeScript backend and React frontend, ripped out its CircleCI pipeline, and rebuilt the entire CI/CD workflow in GitHub Actions -- including infrastructure provisioning, database migrations, and blue-green deployments.

The Problem

The UdaPeople project originally shipped with a CircleCI pipeline covering build, test, security scan, infrastructure provisioning, deployment, and cleanup. CircleCI works fine, but maintaining a separate CI system adds operational overhead: another set of credentials, another dashboard to monitor, another YAML dialect to learn. Since the code already lives on GitHub, consolidating CI/CD into GitHub Actions eliminates a dependency and simplifies the developer experience.

What I Built

udapeople-cicd is a complete migration of the UdaPeople CI/CD pipeline to GitHub Actions. The application is a full-stack HR management tool with a Node.js/TypeScript backend and React frontend. The pipeline covers the full lifecycle from commit to production.

  • Build and test -- separate jobs for backend (TypeScript compilation, Jest tests) and frontend (React build, Jest tests) running in parallel
  • Security scanning -- dependency vulnerability checks for both backend and frontend using npm audit and Snyk
  • Infrastructure provisioning -- CloudFormation stacks for networking, EC2 instances, and RDS databases, triggered on merge to main
  • Configuration management -- Ansible playbooks configure deployed instances with the application, Prometheus node exporter, and environment variables
  • Database migrations -- TypeORM migrations run automatically against the RDS instance before deployment
  • Blue-green deployment -- new instances are provisioned alongside old ones; traffic switches only after health checks pass
  • Smoke tests -- automated curl-based health checks verify the deployment before marking it successful
  • Cleanup -- old CloudFormation stacks are destroyed after successful promotion

Tech Stack

GitHub Actions, TypeScript, Node.js, React, AWS (CloudFormation, EC2, RDS, S3, CloudFront), Ansible, Docker, PostgreSQL

Usage

# Local development
cd backend && npm install && npm run dev
cd frontend && npm install && npm start

# CI/CD triggers automatically on push to main
# Manual trigger available via workflow_dispatch

Results

The migration reduced pipeline execution time from 22 minutes on CircleCI to 16 minutes on GitHub Actions, primarily due to better caching and parallel job execution. Eliminating CircleCI removed one set of credentials from the secrets rotation schedule and one fewer dashboard to monitor. The GitHub Actions workflow file is more verbose than the CircleCI config, but having everything in one platform -- code, issues, PRs, CI, deployments -- is worth the tradeoff.


GitHub: https://github.com/basel5001/udapeople-cicd