Cloud Infrastructure & Migration

In 2016–2017, I was responsible for moving a collection of legacy PHP applications from our on-premise infrastructure to the cloud.
It sounds straightforward today. At the time, it wasn’t.
We had around seven sites running across two SaaS platforms. One was built on an old Symfony 1 stack running on PHP 5.x. The other was newer, but still based on Symfony 2 and an aging PHP runtime. None of the applications had been designed with containers or cloud infrastructure in mind.
And there was another problem: the organization wasn’t particularly ready to leave on-premise behind.
The wider company group was already leaning heavily towards AWS. Some companies had started moving there, and there was a natural expectation that we should follow the same path.
I wasn’t convinced.
I looked at the platforms, the pricing, the developer experience, the available managed services and where I believed the products were heading. At that point, Google Cloud felt like the better fit for what we were trying to build.
My reasoning was partly technical and partly instinctive. Google was, at its core, a technology company. It had spent decades building some of the world’s largest distributed systems. Amazon was a retail company that had built an incredibly successful cloud business.
At the time, I used a deliberately provocative shorthand when explaining my choice:
“Google is a technology company. Amazon is a webshop that has a cloud business.”
It wasn’t meant as a technical assessment of AWS. It was a way of explaining why I believed Google’s engineering DNA and product direction were a better match for us.
Google Cloud still had gaps compared with AWS at the time. But the pricing was attractive, the interfaces felt cleaner to me, and I believed the platform was improving rapidly.
In hindsight, it was a good bet.
The migration went well enough that other companies within the group eventually moved towards Google Cloud as well. That gave the group significantly more leverage when negotiating cloud pricing and discounts.
There was no dedicated cloud or DevOps team.
I was the CTO, and initially I was also the person designing and building the entire infrastructure.
That meant learning a lot by doing it — and occasionally learning by doing it wrong.
The platform evolved through many iterations. The foundation was built around:
- Google Cloud Platform
- Terraform (IaC)
- GKE / Kubernetes
- Helm
- Docker, multi-stage builds
- Kaniko
- Jenkins, Groovy pipelines
- Container Registry
- Managed MySQL
- GlusterFS
- Prometheus & Grafana
- Pub/Sub & Cloud Functions
The goal wasn’t simply to put the existing servers somewhere else. The goal was to create an environment where deployments, infrastructure and operations could become repeatable.
A typical change could go from:
GitHub → build → test → container image → deployment
with Jenkins orchestrating the process and Helm handling the Kubernetes side.
We also introduced deployment strategies such as blue-green and canary deployments, so releases didn’t have to mean taking the entire platform offline.
One of the lessons I learned very early was that cloud migration doesn’t mean forcing a legacy application to become cloud-native overnight.
The applications needed surprisingly few changes.
Instead, I tried to make the infrastructure compatible with what we already had.
Storage was a good example.
The applications relied heavily on shared filesystem storage for things like uploaded CVs, images and media. Moving everything directly to object storage would have meant making much larger application changes than we wanted at that stage.
So instead of rewriting the applications around a new storage model, I introduced an NFS-compatible storage layer using GlusterFS.
It wasn’t necessarily the architecture I would choose for a greenfield application today.
But it was the right architectural decision for that system, at that point in time.
That distinction became one of the most important lessons of the whole migration.
The technical migration was only half of the job.
The harder part was convincing senior management that remaining entirely on-premise was becoming a strategic constraint.
On-premise infrastructure had become almost a belief system. It was familiar, tangible and considered safe. Moving to the cloud meant giving up some of that control and trusting infrastructure we didn’t physically own.
I had to make the case that staying where we were also carried risk.
We eventually moved, step by step, rather than trying to make one enormous migration.
By the end, the entire on-premise infrastructure had been retired.
Later, a dedicated DevOps engineer joined the team. I trained him and gradually transferred significant parts of the infrastructure work to him, but I continued to stay closely involved in the architecture and operation of the platform for the rest of my time there.
The migration delivered more than just a new hosting environment. We ended up with:
- Minimal downtime during migrations and releases
- Fully automated deployments
- Faster and more predictable releases
- Infrastructure defined as code
- Automated backups and stronger data protection
- Centralized observability
- Better isolation between customers
- No dependency on physical on-premise infrastructure
- Easier infrastructure provisioning and replication
- Clearer infrastructure and operational ownership
We also went through an infrastructure audit after the migration.
The feedback was extremely positive — the infrastructure was assessed as being exceptionally well designed and close to textbook quality.
That was particularly satisfying because much of it had been built incrementally, rather than from a pre-existing cloud architecture blueprint.
The migration changed the way I think about infrastructure.
“In the cloud, performance isn’t just a technical metric. It can become a line item on your bill.”
The most important thing I took away from the project wasn’t a particular Google Cloud service, Kubernetes configuration or deployment strategy.
It was learning how to make infrastructure evolve alongside software.
We didn’t have the luxury of starting with modern applications designed specifically for the cloud.
We had old PHP applications, shared filesystems, legacy integrations, organizational resistance and a lot of unknowns.
So we built the platform around those realities, modernized what made sense, automated what we could, and gradually removed the things that were holding us back.
That’s what I think good infrastructure work is about: not building the most modern system possible, but building the right system for the problem you actually have.
