DEVOPS
Taivo Pikkmets
27. November, 2025
Why does provisioning a simple S3 bucket require a ticket and a platform engineer's time?
We started tracking our platform team's week and noticed a pattern: most infrastructure requests weren't new features. They were the same buckets, databases, and caches, configured the same way, over and over. Meanwhile, the projects that would actually create value kept getting pushed back. We figured there had to be a better approach.
We considered sticking with traditional Terraform modules. They work, and we still use Terraform where it makes sense. But it wouldn’t solve the core problem for application-specific resources: platform engineers acting as human APIs every time a developer needed a database or bucket. And pushing Terraform to developers would just shift the burden to them, now they'd need to learn HCL (Hashicorp Configuration Language) and run pipelines for every change.
We needed something fundamentally different. A way to let developers provision their own application resources using tools they already knew, saving time on both sides without losing control over how things get built.
We built our APIs on Crossplane v2, a Kubernetes-based control plane for cloud infrastructure. We're using compositions to treat application infrastructure as Kubernetes-native APIs. The key idea isn't replacing all infrastructure tooling. Instead, it's about creating self-service abstractions for the resources applications actually need: storage, databases, caches, queues and so on.
The most common objection against this approach is lack of time to build and maintain those abstractions. In practice, this argument ignores where most of the time is actually spent: playing human API, processing repetitive requests that follow the same patterns. Building these abstractions breaks that cycle and frees up time for work that actually matters.
Here's what made Crossplane useful for our use case:
Go based functions and templating - We can write powerful conditional logic and loops directly in our infrastructure definitions, making complex abstractions maintainable. Since our engineers already knew Go, this was an easy fit.
Namespaced by default - Composite and managed resources are namespaced by default in v2, so we can align APIs and permissions with teams and environments instead of global cluster-wide objects. That maps directly to how we already structure ownership.
Fast feedback for developers - Crossplane behaves like any other Kubernetes controller: changes reconcile within seconds. Developers see infrastructure changes in the same deploy loop as their applications, instead of waiting for a separate pipeline or a human.
Automatic drift correction - Crossplane continuously reconciles desired state with actual state. If someone changes a setting in the AWS console, Crossplane will see the drift and correct it back to the declared configuration.
The business impact became clear quickly. The constant back-and-forth disappeared and application-specific infrastructure deployment now matched the speed of application deployment.
Let's look at how we abstract object storage. Instead of developers creating a ticket and waiting for someone to write and apply Terraform, they now include a simple Kubernetes manifest in their application’s Helm chart:
# Example S3Bucket with platform defaults
apiVersion: storage.entigo.com/v1alpha1
kind: S3Bucket
metadata:
name: example-bucket
spec: {}
That’s it.
One small resource in the application’s Helm chart.
Behind the scenes, our Crossplane composition automatically provisions:
From the developer’s perspective, storage becomes “instant infrastructure”:
The same pattern works for more complex infrastructure, like managed caches.
# Example ValkeyInstance with platform defaults
apiVersion: database.entigo.com/v1alpha1
kind: ValkeyInstance
metadata:
name: example-valkey
spec: {}
For teams that need more control, they can specify additional parameters:
# Example ValkeyInstance with custom settings
apiVersion: database.entigo.com/v1alpha1
kind: ValkeyInstance
metadata:
name: example-valkey
spec:
deletionProtection: true
engineVersion: '8.2'
instanceType: 'cache.t4g.large'
numCacheClusters: 3
autoMinorVersionUpgrade: true
maintenanceWindow: 'mon:00:00-mon:03:00'
snapshotWindow: '05:00-06:00'
snapshotRetentionLimit: 10
parameterGroupName: 'default.valkey8'
When deployed, the composition automatically:
Again, the developer doesn't have to understand any of that. They just know they get a Valkey cluster that "just works" and follows the required standards.
But if they want to look under the hood, they can. All composed resources are namespaced and visible via kubectl. If you use ArgoCD like we do, the resource tree visualizes the full hierarchy, making it easy to understand what was provisioned and debug issues when something doesn't work as expected.
Our implementation delivered tangible results within the first month:
Eliminated repetitive infrastructure tickets - Development teams provision their own S3 buckets, Valkey caches, and RDS instances without platform team intervention. Our platform engineers spend time building new features instead of solving tickets with repetitive tasks.
Reduced environment setup time - New microservices get production-ready infrastructure in minutes instead of hours or days. Developers don't have to wait for the platform team to provision application specific infrastructure.
Ensured 100% compliance automatically - Every resource provisioned through compositions inherits required security standards.
Improved infrastructure reliability - Automatic drift correction ensures that infrastructure is always in the desired state, preventing configuration drift that causes outages.
For leadership, this shows up not as a shiny dashboard, but as shorter lead times, fewer incidents caused by misconfigured infra, and lower operational noise.
We use these same patterns across environments, maintaining a library of Kubernetes-native resources built on Crossplane. This approach has consistently delivered fewer infrastructure tickets and faster environment setup.
While our examples focus on AWS, Crossplane providers exist for virtually any platform: Azure, Google Cloud, Kafka, SQL databases and so on. The same composition patterns apply regardless of the provider.
Getting started typically means picking one high-value abstraction that applications consume directly, like an S3 bucket, and proving the time savings, then expanding from there.
The shift to application-specific infrastructure as self-service APIs changes how organizations build products, letting teams focus on their applications instead of infrastructure coordination.

DEVOPS

EFFICIENCY

DEVOPS

DEVOPS
info@entigo.com | (+372) 600 6130 | Veerenni 40a, Tallinn, 10138