A few weeks ago, I had never touched a mobile core network.
I didn't know what an EPC was. I didn't know what AGW stood for. And I definitely didn't understand why someone would want to run a 4G network stack on their laptop.
Then I joined the Magma mentorship program.
By the end of it, I had deployed a mobile core network locally, investigated a real compatibility problem, traced the issue across the codebase, updated the affected Dockerfiles and CI configuration, and shipped a pull request that became part of the project.
This is the story of how I went from knowing almost nothing about telecom infrastructure to contributing to a production open-source codebase.
What Is Magma?
Magma is an open-source mobile core network platform under the Linux Foundation Networking ecosystem.
At a high level, a mobile core sits behind the radio access network and handles things such as authentication, subscriber management, routing, and connectivity between mobile users and external networks.
Magma provides software components that allow organizations and developers to build and operate mobile networks using open-source technologies rather than relying entirely on proprietary infrastructure.
For someone coming from a computer science and AI background, it sounded intimidating.
I joined the Spring 2026 Magma mentorship cohort with essentially zero telecom experience.
That turned out to be a good thing.
Instead of trying to learn everything about telecommunications before touching the code, I learned by getting the system running and then following the problems wherever they led.
The First Challenge: Getting the Stack Running
My first goal wasn't to fix anything.
It was simply:
Get Magma running locally.
The Access Gateway (AGW) uses Docker containers, and getting the complete environment working involves multiple components communicating with each other.
There are services for things like:
- The Access Gateway
- Subscriber databases
- Orchestration
- Networking
- Monitoring and supporting services
At first, this looked like a huge collection of unrelated components.
But getting the system running forced me to understand how they fit together.
That was probably one of the most valuable parts of the entire experience.
Instead of reading a definition of a mobile core network and memorizing it, I could actually see the software running.
I could inspect containers.
I could read logs.
I could follow dependencies.
And whenever something broke, I had a concrete problem to investigate.
From Running the System to Reading the Code
Once I had the environment working, I started looking through the project's Dockerfiles and build configuration.
That's where I found something interesting.
Some of the project's assumptions about the underlying Ubuntu environment were no longer valid on newer versions of Ubuntu.
The problems weren't necessarily obvious.
A Dockerfile might request a package that had existed for years.
That doesn't mean the package still exists in a newer Ubuntu release.
And that's where software maintenance becomes interesting.
The code may still look perfectly reasonable.
The assumption underneath it may simply be outdated.
Finding the Ubuntu 24.04 Breakage
Two examples immediately stood out.
python3-distutils
The project depended on python3-distutils, but distutils had been removed from newer Python/Ubuntu environments.
As a result, an installation that worked on an older environment could fail when the same assumptions were applied to Ubuntu 24.04.
libssl1.1
Another issue involved:
libssl1.1
Ubuntu 24.04 no longer provides that old OpenSSL package in the same way.
The newer environment uses the OpenSSL 3 series, including libssl3.
These aren't spectacular errors.
There isn't always a giant message saying:
"This Dockerfile is outdated."
Instead, you get an installation failure somewhere in the middle of a much larger build.
You have to ask:
What exactly is this Dockerfile assuming about the operating system?
That question led me much deeper into the codebase.
The Fix Wasn't Just Changing Two Package Names
At first glance, the solution looked simple.
Replace the old packages.
Build again.
Done.
It wasn't.
The same underlying assumptions appeared in multiple places.
I ended up tracing the issue across 14 files where Ubuntu versions, packages, or OS-specific assumptions were involved.
The changes included:
- Updating the
subscriberdb Dockerfile to use Ubuntu 24.04 as the base.
- Replacing packages that were no longer available with their appropriate 24.04 equivalents.
- Updating the GitHub Actions CI workflow.
- Adding Ubuntu 24.04 alongside Ubuntu 20.04 in the testing matrix.
- Checking the affected Dockerfiles for additional compatibility assumptions.
That last part was important.
Fixing the immediate error is one thing.
Making sure the same problem is less likely to happen again is another.
Why the CI Change Mattered
One of the changes I was particularly happy about was the CI matrix.
Instead of testing against only one Ubuntu environment, the workflow could test multiple versions:
ubuntu-20.04
and
ubuntu-24.04
This means an OS-specific compatibility issue has a better chance of being detected automatically.
That changes the contribution from:
"I fixed my environment."
to:
"I helped make the project more resilient to different environments."
That's a much more satisfying kind of open-source contribution.
What Open-Source Maintenance Actually Feels Like
Before this experience, I imagined contributing to a large open-source project mostly meant finding an issue, writing some code, and opening a pull request.
The reality was different.
A lot of maintenance is archaeology.
You encounter code written at a particular point in time.
At that time:
- A particular package existed.
- A particular Ubuntu version was common.
- A particular Python version was installed.
- A particular dependency behaved in a particular way.
Years later, those assumptions may no longer hold.
But the code still contains them.
Nobody necessarily wrote down why they were there because, when the code was originally written, the reason was obvious.
As a contributor, you have to reconstruct that history from the code, configuration files, documentation, build logs, and CI failures.
That was one of the biggest lessons I took away from the project.
The other thing that surprised me was how approachable the underlying debugging process became.
"Mobile core network" sounds like an enormous specialized field.
And it is.
There is a huge amount of telecommunications knowledge behind technologies such as LTE, EPC, 5G, authentication, subscriber management, and network interfaces.
But I didn't need to become a telecom expert before contributing.
The debugging process was still familiar.
Something doesn't work.
You check the logs.
You trace the dependency.
You inspect the configuration.
You reproduce the problem.
You look at the relevant code.
You make a small change.
You test it.
Then you discover that your "small change" actually affects five other things.
That's software engineering.
The domain was unfamiliar.
The debugging mindset wasn't.
From Beginner to Contributor
The most valuable part of the experience wasn't the specific Ubuntu fix.
It was realizing that you don't need to understand an entire codebase before you can contribute to it.
You need a sufficiently small problem to start with.
My starting point was essentially:
"Why isn't this environment building correctly?"
That question eventually led me through Docker, Ubuntu packages, CI workflows, networking infrastructure, and parts of the Magma architecture.
I learned the domain because I needed the knowledge to solve the problem.
That was much more effective for me than trying to learn telecommunications completely in isolation.
What I Learned
If I had to summarize the experience into a few lessons, they would be these.
1. Don't be afraid of unfamiliar codebases
A repository can look enormous.
You don't need to understand all of it.
Find one small problem and follow it.
2. Read the assumptions, not just the code
A Dockerfile isn't just a list of commands.
It contains assumptions about the operating system, packages, architecture, and runtime environment.
Those assumptions are often where compatibility problems hide.
3. Reproducing a problem is already progress
Before fixing something, I needed to understand exactly what was failing.
Being able to reproduce a problem turns a vague bug into something concrete.
4. A good fix prevents future problems
Changing one package name might solve today's build.
Adding compatibility testing can help prevent tomorrow's regression.
5. Open source is one of the best ways to learn systems
You get to work with software that other people actually depend on.
The feedback loop is completely different from building another isolated tutorial project.
Someone else reviews your changes.
CI tests them.
Maintainers question your assumptions.
And suddenly the code you wrote has to survive outside your laptop.
What's Next?
I recently graduated from the Spring 2026 Magma mentorship program, and I'm continuing to explore open-source systems work.
At the same time, I'm going deeper into machine learning, agentic AI frameworks, research papers, and projects that connect AI with systems and real-world software.
The Magma experience reinforced something I want to keep doing:
Go into areas I don't fully understand, find difficult problems, and learn by building.
A few weeks before this project, I didn't know what an AGW was.
I certainly didn't expect my first serious telecom contribution to involve tracking Ubuntu package compatibility across 14 files.
But that's exactly what made the experience worth it.
Sometimes the best way to learn a new field isn't to wait until you're ready.
It's to find a problem that's slightly beyond your current understanding—and start digging.
Originally published on my DEV Community and personal blog.