This project is a production-style DevOps portfolio platform that demonstrates how a simple static website can evolve into a fully automated, secure, observable, and operationally monitored cloud workload.
It showcases practical DevOps and cloud engineering workflows including:
- Infrastructure as Code (Terraform)
- CI/CD automation (GitHub Actions)
- Secure global content delivery (AWS CloudFront + ACM)
- Custom domain integration (Namecheap)
- Monitoring and alerting (Prometheus, Grafana, Alertmanager, Blackbox Exporter)
- Incident notification workflows (Slack integration)
π Live Site: https://philipoludolamu.com
This repository serves two purposes:
- It powers my live portfolio website at
https://philipoludolamu.com - It demonstrates how a simple frontend application can be engineered, automated, secured, monitored, and operated using modern DevOps practices
Many portfolio websites stop at frontend deployment alone.
This project extends beyond static hosting by introducing:
- Infrastructure as Code
- CI/CD automation
- CDN-backed HTTPS delivery
- DNS and certificate management
- Monitoring and observability
- Alert routing and incident notifications
- Operational validation through controlled failure testing
The goal is to treat a simple web application like a real production-style workload rather than a static showcase page.
This repository demonstrates practical implementation across multiple areas of DevOps and cloud engineering:
- Static frontend delivery on AWS
- Infrastructure provisioning with Terraform
- CI/CD automation using GitHub Actions
- CDN-backed HTTPS delivery with CloudFront and ACM
- DNS integration using Namecheap
- Infrastructure observability with Prometheus and Grafana
- Host-level metrics collection with Node Exporter
- External uptime probing with Blackbox Exporter
- Alert routing with Alertmanager
- Incident notifications through Slack
- Operational validation through simulated failure scenarios
| Area | What this project demonstrates |
|---|---|
| Frontend | Responsive portfolio frontend optimized for recruiter visibility |
| Hosting | AWS S3 static website hosting |
| Delivery | Automated CI/CD deployment pipeline |
| Infrastructure | Terraform-managed AWS resources |
| Security | HTTPS via CloudFront + ACM |
| DNS | Namecheap custom domain routing |
| Observability | Prometheus, Grafana, Alertmanager, Node Exporter, Blackbox Exporter |
| Notifications | Real-time incident and recovery notifications via Slack |
| Endpoint | Purpose |
|---|---|
https://philipoludolamu.com |
Primary live portfolio URL |
https://www.philipoludolamu.com |
Secondary live portfolio URL |
http://philipdev-portfolio-website.s3-website-us-east-1.amazonaws.com/ |
Direct S3 website endpoint |
Note: the CloudFront domain is managed by AWS and may change after distribution updates. Use the custom domain above for the latest site version.
When the monitoring stack is running locally with Docker Compose, these endpoints are available:
| Service | URL |
|---|---|
| Grafana | http://localhost:3001 |
| Prometheus | http://localhost:9091 |
| Alertmanager | http://localhost:9094 |
| Node Exporter | http://localhost:9101 |
| Blackbox Exporter | http://localhost:9116 |
flowchart LR
A[Developer] --> B[GitHub Repository]
B --> C[GitHub Actions]
C --> D[AWS S3 Static Website Bucket]
E[Terraform] --> D
E --> F[AWS CloudFront]
E --> G[AWS ACM Certificate]
H[Namecheap DNS] --> F
D --> F
F --> I[philipoludolamu.com]
F --> J[www.philipoludolamu.com]
I --> K[End Users]
J --> K
flowchart LR
A[Node Exporter] --> B[Prometheus]
C[Blackbox Exporter] --> B
D[https://philipoludolamu.com] --> C
B --> E[Grafana]
B --> F[Alertmanager]
F --> G[Slack Channel]
This section outlines both the delivery pipeline and the observability workflow used to deploy, monitor, and operate the application in a production-style environment.
GitHubstores the application source, infrastructure files, and deployment workflows.GitHub Actionsautomatically deploys updated frontend assets to the S3 website bucket after changes are pushed tomain.Terraformprovisions and manages the AWS infrastructure required for application delivery.S3stores the static frontend assets.CloudFrontprovides global CDN delivery, HTTPS termination, and edge caching.ACMmanages the TLS certificate attached to the CloudFront distribution.Namecheaproutes the custom domain to CloudFront through DNS configuration.
Node Exporterexposes host-level infrastructure metrics such as CPU, memory, and system usage.Blackbox Exporterprobes the live portfolio endpoint and returns uptime and response metrics.Prometheusscrapes exporters, stores time-series metrics, and evaluates alert rules.Grafanavisualizes infrastructure and application monitoring data through dashboards.Alertmanagerreceives alerts from Prometheus and manages routing, grouping, and notification delivery.Slackserves as the operational notification channel for incidents and recovery events.
- Build and operate a production-style personal portfolio platform
- Automate infrastructure provisioning using Terraform
- Implement repeatable CI/CD deployment workflows
- Secure public delivery using CloudFront and HTTPS
- Integrate DNS and certificate management for a real custom domain
- Introduce observability and operational monitoring
- Validate monitoring and alerting workflows through controlled failure testing
- Demonstrate practical DevOps, cloud, and platform engineering workflows in a single repository
| Category | Tools / Services | Why they are here |
|---|---|---|
| Frontend | HTML, CSS, JavaScript | Build the portfolio frontend |
| Cloud Platform | AWS S3, CloudFront, ACM | Host, secure, and globally distribute the site |
| DNS | Namecheap | Route the custom domain to CloudFront |
| IaC | Terraform | Provision infrastructure consistently |
| CI/CD | GitHub Actions | Automate deployment workflows |
| Monitoring | Prometheus, Grafana | Collect and visualize operational metrics |
| Exporters | Node Exporter, Blackbox Exporter | Expose host metrics and probe website uptime |
| Alerting | Alertmanager, Slack | Route incidents and recovery notifications |
| Runtime | Docker Compose | Run the local observability stack |
| OS / Shell | Linux, Bash | Local development and operational workflows |
Devops-Portfolio-Website/
βββ index.html
βββ style.css
βββ script.js
βββ README.md
βββ .gitignore
βββ assets/
β βββ portfolio-devops-website.png
β βββ Philip-Oludolamu-Resume.pdf
β βββ ACM_certificate_issued.png
β βββ cloudfront_custom_domain_config.png
β βββ docker_compose_running.png
β βββ grafana_full_monitoring_dashboard.png
β βββ prometheus_probe_success.png
β βββ slack_alert_firing.png
β βββ other screenshots used inline throughout this README
βββ .github/
β βββ workflows/
β βββ deploy.yml
βββ terraform/
β βββ versions.tf
β βββ main.tf
β βββ variables.tf
β βββ outputs.tf
β βββ terraform.tfvars
β βββ .terraform.lock.hcl
βββ monitoring/
βββ docker-compose.yml
βββ prometheus/
β βββ prometheus.yml
β βββ alerts.yml
βββ grafana/
β βββ dashboards/
β β βββ node-exporter-overview.json
β βββ provisioning/
β βββ datasources/
β β βββ prometheus.yml
β βββ dashboards/
β βββ default.yml
βββ alertmanager/
β βββ alertmanager.yml
βββ blackbox/
β βββ blackbox.yml
βββ secrets/
βββ slack_webhook_url # local only, gitignored
monitoring/secrets/slack_webhook_urlis intentionally kept local and excluded from Git. The webhook is treated as a secret and should never be committed to source control.
The following tools and services were used to build and operate this project:
- AWS account
- Namecheap account (for custom domain configuration)
- GitHub repository with Actions enabled
- Terraform installed locally
- Docker and Docker Compose installed locally
- Slack workspace with an incoming webhook for alert notifications
Because the frontend is fully static, it can be served locally using any lightweight web server.
python3 -m http.server 8000Then open:
http://localhost:8000
From the repository root:
cd monitoring
docker compose up -dThen open:
- Grafana:
http://localhost:3001 - Prometheus:
http://localhost:9091 - Alertmanager:
http://localhost:9094
Current Grafana credentials from monitoring/docker-compose.yml:
- username:
admin - password:
*******
This project is structured as a progressive engineering journey from Task 1 through Task 7.
- Tasks 1 to 6 cover frontend delivery, infrastructure provisioning, CI/CD automation, HTTPS enablement, and custom domain integration.
- Task 7 focuses on observability, monitoring, alerting, and operational validation workflows.
Each task section explains:
- why the implementation matters
- what was deployed or configured
- how the components interact operationally
- where the relevant files are located
- supporting screenshots and validation evidence
| Task | Focus | Outcome |
|---|---|---|
| Task 1 | Frontend engineering | Built a recruiter-focused portfolio frontend |
| Task 2 | AWS S3 static hosting | Deployed the site as a publicly accessible static workload |
| Task 3 | Terraform | Converted infrastructure provisioning into reusable IaC |
| Task 4 | GitHub Actions CI/CD | Automated deployments from GitHub to AWS |
| Task 5 | CloudFront + HTTPS | Enabled secure CDN-backed HTTPS delivery |
| Task 6 | Custom domain | Integrated Namecheap DNS, ACM, and CloudFront |
| Task 7 | Monitoring and alerting | Implemented observability, dashboards, alerting, and Slack notifications |
Build a clean, recruiter-focused portfolio frontend that serves as the application layer for the broader DevOps and cloud delivery workflow.
Without an actual application workload, the project would remain infrastructure-only.
Task 1 establishes the frontend application that is later deployed, automated, secured, monitored, and operationally validated throughout the rest of the project lifecycle.
This creates a realistic foundation for demonstrating:
- deployment workflows
- infrastructure automation
- CI/CD pipelines
- CDN delivery
- monitoring and alerting
- production-style operational practices
- Sticky navigation for fast section access
- Hero section with a clear DevOps engineering value proposition
- About, Skills, Projects, Resume, and Contact sections
- Responsive layout for desktop and mobile viewing
- Theme toggle and improved UI interactions
- Project cards designed to showcase real engineering work and technical projects
- Resume integration for recruiter accessibility
index.htmlstyle.cssscript.js
The frontend intentionally uses a lightweight static architecture while maintaining a professional and recruiter-focused user experience.
index.htmldefines the structure and content layoutstyle.cssmanages responsiveness, theming, spacing, and visual presentationscript.jshandles UI interactions and frontend behavior
Using a static frontend architecture simplifies downstream infrastructure and delivery workflows because the application can be efficiently:
- hosted on S3
- distributed globally through CloudFront
- deployed through CI/CD pipelines
- monitored externally through uptime probes
This keeps the operational model simple while still enabling production-style DevOps practices around the application.
The application was fully functional locally before cloud deployment and infrastructure automation were introduced:
- A simple static application is sufficient for demonstrating real DevOps workflows
- Clean frontend structure simplifies automation, deployment, and observability later in the project
- Lightweight applications are ideal for learning infrastructure automation and operational workflows without unnecessary backend complexity
Deploy the portfolio application to AWS and make it publicly accessible through cloud-based static hosting.
Task 1 established the application layer locally.
Task 2 transitions the project into a real cloud runtime environment by introducing public hosting on AWS. This marks the shift from local-only development into externally accessible infrastructure.
It also establishes the delivery foundation that is later enhanced with:
- Terraform automation
- CI/CD deployment workflows
- CloudFront CDN integration
- HTTPS encryption
- custom domain routing
- monitoring and observability
- S3 bucket creation for static website hosting
- Static website hosting configuration
- Public access configuration for website delivery
- Upload and hosting of frontend application assets
- Initial public endpoint exposure through the S3 website URL
| Component | Purpose |
|---|---|
| Amazon S3 | Stores and serves static frontend assets |
| Static Website Hosting | Enables browser-based public website access |
| Bucket Policy / Public Access | Allows external users to access website files |
Amazon S3 can directly host static frontend assets including:
- HTML
- CSS
- JavaScript
- Images
- PDF documents
At this stage, requests are served directly from the S3 static website endpoint.
This provides a fast and cost-effective method for publicly hosting frontend applications, but there are still important limitations:
- no HTTPS encryption
- no CDN edge caching
- no custom domain support
- limited production-grade traffic optimization
These limitations are addressed later through CloudFront, ACM, and DNS integration.
The S3 bucket was created for static website hosting:
Static website hosting was enabled successfully:
The application became publicly accessible through the S3 website endpoint:
- S3 provides a lightweight and highly cost-effective hosting model for static applications
- Static hosting creates a strong foundation for later CDN and HTTPS integration
- Separating frontend delivery from backend infrastructure simplifies deployment workflows
- Direct S3 hosting is useful for initial deployment validation before introducing production-grade delivery optimizations
Replace manual AWS configuration with Terraform to make the infrastructure reproducible, version-controlled, and easier to manage operationally.
Manual infrastructure provisioning through the AWS Console introduces several operational challenges:
- inconsistent environments
- difficult change tracking
- limited reproducibility
- increased risk of configuration drift
Terraform addresses these issues by defining infrastructure declaratively as code.
This enables the infrastructure to become:
- reusable
- reviewable
- auditable
- easier to maintain over time
Introducing Infrastructure as Code (IaC) also establishes the foundation for scalable automation and repeatable cloud delivery workflows.
terraform/versions.tfterraform/main.tfterraform/variables.tfterraform/outputs.tfterraform/terraform.tfvars
Terraform provisions and manages the core AWS delivery infrastructure, including:
- S3 bucket and static website configuration
- Bucket ownership controls and public access settings
- Bucket policy for public content delivery
- CloudFront CDN distribution
- ACM certificate request configuration
- Certificate validation outputs
- Custom domain aliases for CloudFront
- Infrastructure outputs used for DNS routing and verification
This transitions the project from manually configured infrastructure into a codified and repeatable cloud environment.
- Infrastructure provisioning was manual and console-driven
- Resource configuration required repetitive click-based setup
- Reproducing environments was time-consuming
- Infrastructure changes were difficult to audit or version-control
- Infrastructure is defined declaratively in code
- Cloud resources are version-controlled alongside the application
- Infrastructure changes are easier to review and maintain
- CloudFront, ACM, and DNS workflows became more structured and repeatable
- Deployment environments became easier to reproduce consistently
This repository currently uses a local Terraform state file as part of the project scope.
In collaborative or production-grade environments, Terraform state should typically be stored remotely using a backend such as:
- Amazon S3 for remote state storage
- DynamoDB for state locking and concurrency protection
This helps prevent state conflicts and improves collaboration across teams and deployment environments.
Terraform was used to provision and manage the AWS infrastructure required for application delivery and HTTPS configuration:
- Terraform improves both infrastructure automation and operational consistency
- Infrastructure definitions become version-controlled alongside application code
- Declarative infrastructure simplifies repeatability and long-term maintenance
- Infrastructure as Code enables more reliable CI/CD and cloud delivery workflows
Automate deployment workflows so that updates pushed to main are automatically delivered to the live production environment.
Before automation, deployments required manual file uploads and infrastructure interaction.
This introduces operational risks such as:
- inconsistent deployments
- missed files
- manual deployment errors
- slower release workflows
GitHub Actions introduces Continuous Integration and Continuous Delivery (CI/CD) automation into the project lifecycle.
This transforms deployments into:
- repeatable workflows
- source-controlled delivery pipelines
- automated release operations
- faster and more reliable deployment processes
On every push to the main branch, the GitHub Actions workflow automatically:
- Checks out the latest repository code
- Configures AWS authentication using GitHub Secrets
- Syncs updated frontend assets to the S3 bucket
- Invalidates the CloudFront cache to refresh edge-delivered content
This creates a lightweight but production-style deployment pipeline for the static application.
AWS credentials are stored securely using GitHub Secrets rather than hardcoded into the repository.
This helps:
- protect sensitive credentials
- separate secrets from source control
- align with secure CI/CD practices
CloudFront caches assets at edge locations to improve performance and reduce latency.
Without cache invalidation:
- users may continue receiving stale frontend assets
- newly deployed updates may not appear immediately
Including automated invalidation ensures that updated files propagate consistently across edge locations after deployment.
This improves delivery reliability and reduces deployment inconsistency.
The CI/CD workflow completed successfully and deployed updates automatically to the live environment:
- CI/CD workflows are valuable even for static frontend applications
- Deployment automation improves reliability and reduces operational overhead
- GitHub Actions enables infrastructure-aware deployment workflows directly from source control
- CDN cache behavior must be considered in production-style deployment pipelines
Introduce CloudFront in front of the S3 origin to enable secure HTTPS delivery, improve performance through CDN edge caching, and establish a production-style public delivery layer.
Although S3 static website hosting is suitable for initial frontend deployment, it is not ideal as a direct production-facing entry point.
Introducing CloudFront significantly improves the delivery architecture by adding:
- HTTPS encryption
- Global CDN edge caching
- Lower latency for end users
- Improved scalability and availability
- Centralized traffic handling
- A foundation for custom domain integration
This marks the transition from basic static hosting into a more production-oriented cloud delivery model.
- CloudFront distribution
- HTTPS delivery through CloudFront
- HTTP-to-HTTPS redirection
- CDN-backed frontend delivery
- Edge caching for improved response performance
- CloudFront cache invalidation integration with GitHub Actions
User β S3 Website Endpoint
User β CloudFront CDN β S3 Website Bucket
This architectural change introduces a dedicated delivery layer between end users and the application origin.
Instead of accessing S3 directly, users now interact with CloudFront, which becomes the secure public-facing entry point for the application.
In this architecture:
- CloudFront acts as the public entry layer
- S3 remains the application origin storing static assets
- Frequently requested content is cached at CloudFront edge locations
- HTTPS termination is handled at the CDN layer
- Users are automatically redirected from HTTP to HTTPS
- GitHub Actions invalidates cached assets after deployments
This improves both delivery performance and operational consistency.
CloudFront became the secure public delivery layer for the application:
- CloudFront introduces a production-style CDN delivery architecture
- CDN-backed delivery improves scalability, latency, and user experience
- HTTPS encryption is handled at the edge through CloudFront and ACM
- Separating the application origin (S3) from the delivery layer (CloudFront) is a common cloud architecture pattern
- CDN cache invalidation becomes an important operational consideration during deployments
Replace the default CloudFront distribution URL with a branded custom domain while maintaining secure HTTPS delivery and infrastructure-driven configuration.
Custom domain integration transforms the application from a technically functional deployment into a more realistic production-style system.
This task introduces several important cloud and networking concepts including:
- TLS/SSL certificate management
- DNS-based domain ownership validation
- Public DNS routing
- CDN alias configuration
- HTTPS delivery for custom domains
It also improves the professional presentation and accessibility of the application.
https://philipoludolamu.comhttps://www.philipoludolamu.com
These domains now serve as the primary public entry points for the application.
Terraform was used to request an ACM certificate for the custom domains.
The initial Terraform apply outputs the DNS validation records required to prove domain ownership.
This step is critical because CloudFront requires ACM certificates to exist in the us-east-1 region.
The ACM-generated DNS validation records were added in Namecheap to validate ownership of:
philipoludolamu.comwww.philipoludolamu.com
Once propagated, ACM was able to verify domain ownership successfully.
After DNS propagation completed, ACM validated the records and issued the TLS certificate.
This certificate is later attached to the CloudFront distribution to enable HTTPS delivery for the custom domain.
The CloudFront distribution was updated with:
- Alternate domain names (CNAME aliases)
- The validated ACM certificate
This enables CloudFront to securely serve the application over HTTPS using the custom domain.
DNS routing records were configured in Namecheap to direct traffic from the public domains to the CloudFront distribution.
Routing configuration included:
- Root domain (
@) β CloudFront wwwsubdomain β CloudFront
After DNS propagation completed, the application became publicly accessible through the custom domain over HTTPS.
The custom domain now acts as the primary production-style public endpoint for the application.
- DNS validation and DNS routing serve different operational purposes
- ACM certificate validation must complete before HTTPS delivery can function properly
- Custom domain integration requires coordination across multiple services:
- Terraform
- ACM
- CloudFront
- Namecheap DNS
- CloudFront aliases differ from DNS redirects and serve a different routing role
- Proper sequencing is important:
- certificate request
- DNS validation
- certificate issuance
- CloudFront configuration
- DNS routing
- HTTPS delivery depends on successful integration between DNS, CloudFront, and ACM
π‘ Task 7: Monitoring and Alerting with Prometheus, Grafana, Alertmanager, Blackbox Exporter, and Slack
Introduce observability and operational monitoring into the platform so the application is not only deployed, but also measurable, monitorable, and capable of generating actionable alerts during failure conditions.
Many portfolio projects stop after deployment.
In production environments, deployment alone is not sufficient. Systems must also provide operational visibility into:
- infrastructure health
- service availability
- uptime status
- response performance
- failure detection
- incident notification workflows
This task introduces a complete observability and alerting workflow that allows the platform to be monitored and operationally validated in real time.
It transforms the project from a simple hosted application into a production-style observable system.
| Component | Role in this project |
|---|---|
| Prometheus | Scrapes metrics and evaluates alert rules |
| Grafana | Visualizes infrastructure and application metrics |
| Node Exporter | Exposes host-level metrics such as CPU and memory |
| Blackbox Exporter | Probes the public website endpoint externally |
| Alertmanager | Routes and manages alert notifications |
| Slack | Receives real-time incident and recovery alerts |
monitoring/docker-compose.ymlmonitoring/prometheus/prometheus.ymlmonitoring/prometheus/alerts.ymlmonitoring/grafana/provisioning/datasources/prometheus.ymlmonitoring/grafana/provisioning/dashboards/default.ymlmonitoring/grafana/dashboards/node-exporter-overview.jsonmonitoring/alertmanager/alertmanager.ymlmonitoring/blackbox/blackbox.yml
The observability stack was initialized locally using Docker Compose:
cd monitoring
docker compose up -dThis provisions the monitoring components as isolated containers and establishes the local operational environment.
Prometheus was first configured to scrape its own metrics in order to validate:
- service availability
- scrape configuration correctness
- metric ingestion functionality
The up metric returned 1, confirming successful metric collection and healthy target status.
Grafana was integrated with Prometheus as the primary metrics data source.
Initial validation included:
- manual datasource verification
- query testing in Grafana Explore
The setup was later improved by provisioning the datasource through configuration files rather than manual UI configuration.
This makes the monitoring environment more reproducible and infrastructure-driven.
Node Exporter was introduced to expose host-level infrastructure metrics including:
- CPU utilization
- memory consumption
- system-level operational metrics
Prometheus successfully scraped the exporter metrics:
This introduced infrastructure observability into the stack.
Grafana dashboards were provisioned automatically through mounted configuration files.
This avoids manual dashboard setup and improves reproducibility across environments.
Expanded dashboards included operational metrics such as:
- CPU utilization
- memory usage
- exporter health status
- infrastructure visibility panels
Prometheus alert rules were introduced to detect infrastructure and service failures automatically.
Example alert rule:
up{job="node-exporter"} == 0
This alert transitions to FIRING if the Node Exporter target remains unavailable for more than one minute.
This introduces automated failure detection into the monitoring workflow.
Alertmanager was added to receive alerts from Prometheus and manage notification routing.
This component acts as the central alert orchestration layer for the monitoring stack.
Alertmanager enables:
- alert routing
- grouping
- deduplication
- notification delivery workflows
Blackbox Exporter was configured to probe the live production endpoint externally:
https://philipoludolamu.com
This introduced uptime and response monitoring for the publicly accessible application.
Key metrics included:
probe_successprobe_duration_seconds
This extends monitoring beyond infrastructure metrics into application availability monitoring.
Additional alert rules were introduced for application-level monitoring:
PortfolioWebsiteDownPortfolioWebsiteSlow
These alerts monitor:
- website availability
- external uptime status
- response latency thresholds
This introduces service-level operational monitoring into the stack.
The final Grafana dashboard consolidated:
- infrastructure metrics
- exporter health
- website uptime
- response latency
- operational visibility indicators
This creates a centralized operational visibility layer for the application.
Alertmanager was configured to send notifications to Slack through an incoming webhook integration.
monitoring/secrets/slack_webhook_url
This enables:
- real-time incident notifications
- recovery notifications
- operational visibility outside the monitoring stack itself
This completes the end-to-end alerting workflow from detection through notification delivery.
cd monitoring
docker compose stop node-exporterWait approximately one minute, then verify:
- Prometheus alert status
- Alertmanager notification routing
- Slack alert delivery
To restore the exporter:
docker compose start node-exporterTo validate the monitoring and alerting workflows under realistic operational conditions, controlled failure scenarios were intentionally simulated and observed end-to-end.
This ensured that the monitoring stack was not only configured, but operationally verified.
The Node Exporter container was stopped to simulate infrastructure-level monitoring failure:
docker compose stop node-exporter- Prometheus marked the exporter target as
DOWN - Alert rule
NodeExporterDowntransitioned toFIRING - Alertmanager received and processed the alert
- Slack received a real-time incident notification
After restarting the exporter:
docker compose start node-exporter- The alert transitioned to
RESOLVED - Slack received a recovery notification
This validated the full infrastructure alert lifecycle from detection through recovery.
The live portfolio endpoint was monitored through Blackbox Exporter to validate external uptime monitoring behavior.
- Blackbox probe failed (
probe_success = 0) - Prometheus triggered
PortfolioWebsiteDown - Alertmanager routed the incident
- Slack received a firing alert notification
After service recovery:
- Probe returned to success (
probe_success = 1) - The alert resolved automatically
- Slack received a recovery notification
This validated application-level availability monitoring and external uptime detection workflows.
- Alerts trigger only after configured thresholds are exceeded
- False-positive risk is reduced through alert timing controls
- Alert lifecycle transitions function correctly (
FIRING β RESOLVED) - Slack integration provides real-time operational visibility
- Both infrastructure and application failures are detected successfully
- The full alert pipeline (
Prometheus β Alertmanager β Slack) is operationally validated
This validation process demonstrates that the platform is not only deployed, but also operationally observable and incident-aware.
Key operational behaviors were actively verified rather than assumed:
- monitoring functionality
- failure detection
- alert routing
- notification delivery
- recovery handling
This reflects real-world DevOps and Site Reliability Engineering (SRE) practices where system reliability must be continuously observable and operationally validated.
Task 7 transforms the project from a simple deployed application into a production-style observable platform.
The project evolves from:
"I can deploy a website"
to:
"I can deploy, monitor, detect failures, and respond to incidents"
This reflects a broader operational engineering mindset where deployment, observability, reliability, and incident response are treated as equally important parts of the system lifecycle.
During frontend development and deployment validation, browser caching occasionally caused stale assets to appear even after successful updates.
This affected:
- CSS modifications
- Resume (PDF) updates
- Image replacements
A hard browser refresh was required to force retrieval of the latest assets:
Ctrl + Shift + R
Client-side caching can create misleading deployment validation results if stale assets remain stored locally.
This reinforces the importance of:
- cache invalidation strategies
- deployment verification
- CDN cache awareness during frontend delivery workflows
Running multiple local services and previously existing containers resulted in host port conflicts during monitoring stack initialization.
To resolve this, custom host ports were assigned:
- Grafana β
3001 - Prometheus β
9091 - Alertmanager β
9094 - Node Exporter β
9101 - Blackbox Exporter β
9116
This ensured all observability services remained accessible simultaneously without interfering with existing local workloads.
Port allocation planning becomes increasingly important when operating multiple local services, monitoring stacks, or development environments concurrently.
When configuration files or mounted volumes were updated, restarting containers alone did not always apply changes correctly.
Forcing container recreation ensured updated configurations were fully reflected:
docker compose up -d --force-recreateContainer recreation is sometimes required when:
- mounted configuration files change
- provisioning files are updated
- persistent container state causes stale behavior
Understanding the difference between restarting and recreating containers is important when troubleshooting containerized environments.
At one stage, commits were executed from inside the terraform/ directory instead of the repository root.
Although git add . was used, only changes within that subdirectory were staged and committed.
This resulted in:
- monitoring files being excluded
- frontend updates not being committed
- README changes missing from GitHub
Git command scope depends on the current working directory.
To ensure complete repository visibility during commits:
Always execute Git commands from the repository root unless intentionally targeting a specific subdirectory.
Using destructive Git commands such as:
git reset --hard
git push --forceresulted in:
- loss of uncommitted local changes
- overwritten branch history
- reverted monitoring and frontend updates
Destructive Git operations should be used carefully and only with a clear understanding of their impact.
Before performing resets or force pushes:
- commit changes
- create backup branches
- or stash uncommitted work
This reduces the risk of accidental data loss during repository recovery operations.
While inspecting historical commits, the repository entered a detached HEAD state:
git checkout <commit-hash>In this state:
- changes are not attached to a branch
- new work can become difficult to recover
- commits may become orphaned if not preserved properly
Detached HEAD mode is useful for repository inspection, but unsafe for ongoing development work.
Before continuing development:
Always return to an active branch such as
mainor a dedicated recovery branch.
Attempting to switch branches while local changes were still uncommitted caused Git to block the checkout operation:
Your local changes would be overwritten by checkout
This highlighted the importance of preserving work before changing contexts.
Before switching branches:
- commit work
- stash changes
- or create a temporary recovery branch
This prevents accidental overwrites and improves workflow safety during active development.
During repository recovery and troubleshooting, git stash became an important safety mechanism for temporarily preserving local changes.
Commands used:
git stash list
git stash apply stash@{0}This allowed restoration of:
- CSS changes
- README updates
- frontend modifications
git stash is highly valuable when:
- switching branches
- troubleshooting risky operations
- recovering interrupted work
- preserving temporary changes without committing incomplete work
Even after successful GitHub Actions deployments, updates were not immediately visible on the public website.
This occurred because CloudFront continued serving cached assets from edge locations.
- CloudFront invalidation was integrated into the CI/CD workflow
- Propagation time was allowed for edge cache refresh
Successful deployment does not always guarantee immediate frontend visibility.
CDN caching layers must be considered as part of deployment verification and operational troubleshooting workflows.
This project reinforced that DevOps engineering extends beyond deployment alone.
It also involves:
debugging β recovering β validating β improving
The troubleshooting process provided deeper operational understanding across:
- Git recovery workflows
- infrastructure behavior
- deployment troubleshooting
- container operations
- monitoring validation
- caching and CDN behavior
Slack webhook URLs are treated as sensitive credentials and are intentionally excluded from version control.
In this project:
- the webhook is stored in a gitignored local file
- the secret is injected into the Alertmanager container at runtime
This approach helps prevent accidental credential exposure and aligns with common secure configuration management practices used in production environments.
DNS validation and DNS routing serve different operational purposes during custom domain integration.
| Function | Purpose |
|---|---|
| DNS Validation | Proves domain ownership for ACM certificate issuance |
| DNS Routing | Directs public traffic to the CloudFront distribution |
Understanding this distinction is important when troubleshooting HTTPS and custom domain configuration workflows.
This project demonstrates the ability to design, automate, deploy, monitor, and operate a production-style cloud application workflow.
Key capabilities demonstrated include:
- Building a recruiter-facing frontend workload
- Provisioning AWS infrastructure with Terraform
- Automating deployments using GitHub Actions
- Delivering content securely through CloudFront and ACM
- Integrating a real custom domain through DNS configuration
- Collecting infrastructure metrics with Prometheus and Node Exporter
- Monitoring external application availability using Blackbox Exporter
- Visualizing operational metrics through Grafana dashboards
- Routing alerts with Alertmanager
- Sending real-time incident notifications through Slack
- Validating monitoring workflows through controlled failure testing
In summary, the project evolved from a simple static website into a production-style DevOps workflow covering:
build β automate β deploy β secure β observe β alert β validate
DevOps and Cloud Engineer focused on infrastructure automation, CI/CD workflows, observability, and production-style cloud operations.
- π Portfolio: https://philipoludolamu.com
- π» GitHub: https://github.com/holuphilix
- π LinkedIn: https://www.linkedin.com/in/philip-oludolamu
This project is licensed under the MIT License.
You are free to use, modify, and distribute the code with attribution.
See the LICENSE file for full license details.





































