DEV Community

Cover image for How I Built a Serverless Power Outage Tracker for Nigeria on AWS | Part 1

How I Built a Serverless Power Outage Tracker for Nigeria on AWS | Part 1

The Problem Every Nigerian Knows

You're at home in Ikeja trying to work. Suddenly "NEPA" takes light.
You don't know if it's just your street, your whole estate, or the entire LGA. You check WhatsApp groups people are complaining but nobody has answers. You call EKEDC customer care nobody picks up. You just sit and wait, not knowing if light is coming back in 30 minutes or 3 days.
This is the daily reality for millions of Nigerians. And it's the problem I decided to solve with my AWS Community Builder $500 credit.

What I Built

A serverless, crowdsourced power outage tracking and alert system for Nigeria built entirely on AWS using SAM (Serverless Application Model).

Here's how it works:

  1. Light goes off in Amuwo Odofin a resident opens the app and submits a report in 10 seconds
  2. More residents from Festac, Satellite Town, and Apple Junction in Amuwo Odofin report the same outage
  3. When 5 or more reports come in within 2 hours, the system confirms a real neighbourhood outage
  4. Everyone subscribed to Amuwo Odofin alerts receives an email " Power Outage confirmed in Amuwo Odofin LGA"
  5. Citizens can make informed decisions turn on the generator, buy fuel, reschedule work

No more guessing. No more unanswered EKEDC calls. Just real, community-driven outage awareness powered by AWS serverless technology.

The Architecture

The entire backend is serverless no EC2 instances, no servers to manage, no patching. AWS handles all the infrastructure.

Service Role
API Gateway Front door receives outage reports from citizens
Lambda (Ingest) Validates and stores each report
Lambda (Query) Fetches active outages for the dashboard
Lambda (Aggregator) Counts reports per LGA, checks the 5-report threshold
Lambda (Alert) Sends SNS email alerts to subscribers
DynamoDB Stores all outage reports with 90-day auto-expiry
DynamoDB Streams Triggers the Aggregator whenever a new report lands
SNS Delivers email alerts to subscribed citizens

The flow looks like this:

architecture diagram showing serverless resources

The API

Method Endpoint Description
POST /reports Submit an outage report
GET /reports Fetch all active outages across Nigeria
GET /reports?lga=Ikeja Fetch outages for a specific LGA
POST /alert Manually trigger an alert

A citizen submitting a report sends a payload like this:

{
  "lga": "Ikeja",
  "state": "Lagos",
  "outage_type": "total",
  "notes": "No light since 6am, no EKEDC notice"
}
Enter fullscreen mode Exit fullscreen mode

And gets back:

{
  "message": "Report submitted successfully",
  "report_id": "506a3212-b7de-4af8-9739-83d33425e807"
}
Enter fullscreen mode Exit fullscreen mode

The DynamoDB Schema

Getting the DynamoDB schema right was one of the most important design decisions. DynamoDB is query-driven you design the table around your access patterns, not your data structure.
My primary key design:

PK: LGA#Ikeja                    # groups all Ikeja reports together
SK: REPORT#2026-05-09T22:49:48   # orders reports by time
Enter fullscreen mode Exit fullscreen mode

This allows me to query all reports for a specific LGA in a specific time window with a single, fast query no table scans needed.
I also added two Global Secondary Indexes:

  • StateIndex query all outages by state (e.g. all Lagos outages)
  • StatusIndex query all active outages across Nigeria

What Surprised Me Most

I expected deploying to AWS to be complex and time-consuming. But with SAM, I ran sam build && sam deploy and watched CloudFormation spin up my entire infrastructure Lambda functions, API Gateway, DynamoDB table, IAM roles, DynamoDB Streams in under 3 minutes.

The first time I ran:

curl -X POST https://my-api.execute-api.eu-west-1.amazonaws.com/prod/reports \
  -H "Content-Type: application/json" \
  -d '{"lga": "Ikeja", "state": "Lagos", "outage_type": "total"}'
Enter fullscreen mode Exit fullscreen mode

And got back:

{"message": "Report submitted successfully", "report_id": "506a3212..."}
Enter fullscreen mode Exit fullscreen mode

I genuinely couldn't believe it was that fast. A real API, live on AWS, accepting real Nigerian outage reports.

The Hardest Part

The hardest part was understanding that every Lambda function needs to be defined in the SAM template.
As someone who comes from a DevOps background rather than software development, I initially thought of the template.yaml as just a config file. But it's actually the heart of the entire application it wires everything together. Every Lambda function, every API route, every permission, every environment variable.
Once that clicked, everything else made sense.

An Honest Limitation

I want to be transparent about what this system does and doesn't do.
Right now this is a citizen-to-citizen awareness system. When an outage is confirmed in Amuwo Odofin, subscribers in Amuwo Odofin get an email. That's valuable it tells you "it's not just you, the whole LGA is affected." You can make informed decisions.
But EKEDC doesn't receive these alerts. They're not connected to the system.
Phase 2 vision: DisCos like EKEDC, IKEDC, and AEDC subscribe to alerts for their coverage areas. They receive outage confirmations, dispatch repair teams, and update restoration ETAs which citizens then see on the dashboard.
That's the full solution. Phase 1 solves the awareness problem. Phase 2 solves the response problem.

What I'd Do Differently Next Time

As someone who is primarily a DevOps/Cloud engineer rather than a developer, my biggest lesson was this: understand what code is needed and how to write it before you start.
I jumped into building before I fully understood each Lambda function's role. Taking time upfront to map out exactly what each function needed to do its inputs, outputs, and interactions would have saved time and confusion.
Next time: design first, code second.

The Cost

The entire system runs on AWS pay-per-use pricing. At civic app scale:

  • Lambda: effectively free (1M requests/month free tier)
  • PI Gateway: ~$1 per million requests
  • DynamoDB: ~$0.25 per million reads
  • SNS: first 1,000 emails free

Total estimated cost: under $2/month. My $500 AWS CB credit will outlast this project by years.

Try It Yourself

The full source code is on GitHub:
👉 github.com/Emmanuel-DevOps-Portfolio/nigeria-outage-tracker

To deploy your own instance:

git clone https://github.com/Emmanuel-DevOps-Portfolio/nigeria-outage-tracker
cd nigeria-outage-tracker
sam build
sam deploy --guided
Enter fullscreen mode Exit fullscreen mode

What's Next

  • Part 2: Building a real-time outage dashboard for Nigeria with S3 and CloudFront
  • Part 3: Adding SMS alerts with SNS for Nigerian phone numbers
  • Part 4: Connecting DisCos, how EKEDC can receive and respond to outage alerts

Final Thoughts

Nigeria's power problem is complex. One serverless app won't fix epileptic supply overnight. But technology can make the experience less frustrating one reported outage, one confirmed alert, one informed citizen at a time.
If you're a Nigerian engineer, a civic tech enthusiast, or just someone who has ever sat in the dark waiting for "NEPA" I'd love your feedback on this project.
Let's build for Nigeria 🇳🇬.

Emmanuel Ulu is a DevOps/Cloud Engineer based in Lagos, Nigeria, and an AWS Community Builder 2026. Connect on LinkedIn or GitHub.

Top comments (0)