Your first app in STACKIT
Expand the table of contents
My first mistake: Using Kubernetes for a URL shortener
What it’s like to actually work with STACKIT
Managed services: The deal nobody talks about
The rough edges and why I can live with them
Try if for yourself: Set up your own instance in under 15 minutes
Conclusion
At first glance, a cloud platform often looks like a list of services: PostgreSQL, Redis, Kubernetes, Container Registry, Terraform provider. It’s all there, it’s all familiar, and it all sounds like a quick start. But whether a platform is really any good isn’t revealed by the list of features, but rather the moment you want to run a real application on it.
That’s exactly where this article begins.
STACKIT positions itself as a European cloud with a clear promise: infrastructure based in Germany, proximity to the GDPR, and greater digital sovereignty. This is particularly relevant for businesses with stringent compliance requirements. However, for me, this argument alone isn’t enough to decide whether I would host an application there. Data sovereignty is important, but it doesn’t answer the practical questions that matter in day-to-day operations.
How well can the infrastructure be automated? How seamlessly do the individual components work together? Where do unexpected hurdles arise? What costs are involved, even for a small app? And does the platform feel like a tool you’d want to entrust with production workloads?
To find out exactly that, I built a simple but complete application on STACKIT: a URL shortener with a Node.js backend, PostgreSQL as the database, Redis as the cache, Kubernetes as the runtime environment, and a public URL. Small enough to remain straightforward. Realistic enough to highlight the typical issues faced by a production application.
This post is therefore not about a glossy tour of a cloud portal. It’s about the real journey from scratch to a running application: authentication, Terraform, managed services, Kubernetes, container registry, costs, quality and the areas where the process isn’t quite as smooth as one might wish.
At the end, there’s an honest assessment based on three criteria: capabilities, costs and quality. What works well? Where does STACKIT excel? Where are there still gaps? And for what sort of application is it really worth using?
Let’s start where many people get stuck on their first attempt: even before the first resource is created.
The first hurdle is authentication
This is where many people get stuck on their first attempt, and it isn’t highlighted particularly prominently in any marketing text: before Terraform can create even a single resource, it needs an identity that allows it to access STACKIT.
STACKIT uses service accounts for this purpose. These are technical user accounts that operate separately from your personal account. It is precisely these kinds of accounts that are used for automation: rather than a person clicking in the portal, Terraform acts on behalf of this service account.
Here’s how to do it: In the portal, go to IAM → Service Accounts to create an account, then download the JSON key file. This is your private key, which you should never commit to Git. Next, assign a role to the account under IAM → Access. You then export the path to the key as an environment variable:
export STACKIT_SERVICE_ACCOUNT_KEY_PATH=/pfad/zu/ihrem-sa-key.json
This is precisely the variable that the official Terraform provider automatically looks for when running `terraform apply`. If it is set correctly and the service account has the appropriate permissions, Terraform can communicate with STACKIT and create resources.
This is exactly where the pitfall lies: if the service account is missing, the role is incorrect or the path to the key file is set wrong, Terraform fails before it even gets to the first resource. The error messages can quickly seem cryptic, especially if you’re using STACKIT for the first time. This is exactly where I, as a complete novice, experienced my first moment of frustration.
The good news is that once authentication has been set up properly, this problem disappears. After that, it runs in the background and is no longer an issue during further deployments.
My first mistake: Using Kubernetes for a URL shortener
The app is deliberately simple: you enter a long URL, receive a short code in return, and the request is redirected. Three layers, as with almost any production app:
- PostgreSQL for data persistence,
- Redis as a cache,
- a Node.js backend for HTTP traffic.
Browser (HTMX) ──► Node.js Backend ──► PostgreSQL (managed, persistent)
└─► Redis (managed, Cache)
But: take a look at what was required to achieve this:
- a node pool with autoscaling limits,
- a namespace,
- deployments with requests and limits, and
- a Kubeconfig file that the Terraform provider first has to fetch.
resource "stackit_ske_cluster" "app_cluster" {
project_id = var.project_id
name = "url-shortener-cluster"
kubernetes_version = "1.30"
node_pools = [{
name = "default"
machine_type = "c1.2"
os_name = "flatcar"
minimum = 1
maximum = 2
}]
}
That’s why, at a certain point, the honest question arose: do I really need a whole orchestration setup for a service that, at its core, simply runs a single container?
The answer for my scenario is: no. This was overkill.
What I actually wanted was a simple service along the lines of ‘take this container and run it’ – hassle-free, without any cluster management.
In an enterprise environment, the situation is different: there, the K8s overhead pays for itself quickly across many services, teams and scaling requirements. For a single, small piece of software, it rarely does.
And this isn’t just a question of convenience, but also a question of cost. A cluster means at least one worker node running continuously – a VM that’s billed round the clock, regardless of whether my URL shortener receives ten or ten thousand requests a day. For a task that a lean, usage-based container service would handle more cost-effectively, you’re effectively paying for the base load of an orchestration platform as well.
A lightweight alternative – a pure container runtime along the lines of ‘Azure Container Instance’ – is currently missing from STACKIT. And that brings us to the pattern that runs through the whole platform experience, and which I honestly think is a good thing: better to have a smaller selection of services that work properly than a huge portfolio that doesn’t really deliver anywhere. That is precisely the gap, but one I can live with as long as what’s already there is solid.
Which brings us to the next question: how good does this platform actually feel when you’re really working with it?
What it’s like to actually work with STACKIT
For me, that’s the crucial question: not where the data is stored, but how mature the tool is that I use every day. And STACKIT has delivered on that front.
Managing a cloud with Terraform won’t be an achievement in 2026 – it’ll be the norm. The real question is: how good is the provider? And the official STACKIT provider is well-maintained, clean and covers the resources you actually need: Redis, Postgres, clusters, deployments, secrets, etc.
This is precisely where other providers fall short: half-baked providers, missing resources, outdated versions. STACKIT behaves exactly as you’d expect from Terraform.
Nothing surprising – and that’s exactly the point. The provider doesn’t disrupt the normal Terraform workflow at any stage; you don’t have to learn any quirks and, with one exception (I’ll get to that in a moment), you don’t have to build workarounds for missing resources.
On top of that, the documentation has been completely overhauled and is now really good.
My conclusion at this stage: the platform doesn’t feel like a beta promise, but like a tool you can entrust with a production workload. That’s why I would use STACKIT.
Managed services: The deal nobody talks about
I’ve used both Redis and Postgres as managed services, rather than running them myself. The operational benefits are real and undisputed: no OS patching, no replication setup, no in-house backup system. That’s exactly what managed services are for, and I’m not questioning that here.
But – and this is where I take issue with the usual enthusiasm – every managed service is also a hidden dependency. Even when the underlying product is open source, such as Redis or Postgres. You’re not just leasing the functionality; you’re leasing the way it’s operated, configured and integrated.
Hence the worst-case scenario you should consider: what if, at some point, a managed service no longer fits into the structure of your platform – for instance, if you want or need to run it yourself in Kubernetes? In that case, you’ll have to realign your architecture and platform.
This isn’t an argument against managed services. It’s an argument for treating the decision as what it is: a trade-off analysis. Advantages on the one hand, lock-in and loss of control on the other. Anyone who only sees the advantages has only done half the maths.
For this stack? Definitely the right choice. For yours? Do the maths. And speaking of maths, that’s precisely the next point that’s almost always missing from ‘first app’ tutorials.
The rough edges and why I can live with them
Not everything went smoothly, and I don’t want to sugar-coat anything.
The Container Registry is still in beta. There is simply no Terraform resource for creating a Registry project programmatically. That’s exactly the sort of gap that pulls you out of the ‘all-in-one apply’ workflow.
But the workaround is neat. You create the Registry project once in the portal and then automate the build and push using the null provider, which injects arbitrary shell commands into the Terraform graph:
resource "null_resource" "push_backend" {
triggers = { image = local.backend_image }
provisioner "local-exec" {
command = <<EOT
docker build -t ${local.backend_image} ../backend
docker push ${local.backend_image}
EOT
}
}
triggers even enables simple build caching: if the image tag doesn’t change, Terraform skips that step. The result: `docker build` and `docker push` run in the same run as the rest of the infrastructure. It’s a shame, of course, that the registry is in beta and doesn’t yet support Terraform. But it’s acceptable. There’s a workaround, and it keeps the whole process running smoothly.
Getting started was a bit of a struggle. When I was completely new to STACKIT, there were definitely some frustrating moments. But once you’ve had a bit of a play around, those disappear, and after that everything ran smoothly. That’s an onboarding issue, not a platform issue.
The deployment speed is okay: The initial setup takes up to 10 minutes, driven mainly by the Kubernetes cluster, which takes a few minutes to initialise. Not lightning-fast, but perfectly fine for one-off provisioning.
Bottom line: these are the rough edges of a platform that’s still growing, not ones that really cause problems in day-to-day use. That’s exactly why I come back to the same point in my conclusion.
Try it for yourself: Set up your own instance in under 15 minutes
The full code is openly available on GitHub; you can set up the entire stack yourself. It even runs locally without a STACKIT account:
git clone https://github.com/florianlenz96/STACKIT-URL-Shortener
cd STACKIT-URL-Shortener
docker compose up
# → http://localhost:8080
To deploy to the cloud, you need to carry out three preparatory steps, and yes, these are precisely the two pitfalls mentioned above:
1. Create a service account in the portal with the `project.owner` role and download the JSON key.
2. Create a container registry – this is a one-off step in the portal; there is no Terraform resource for this – and log in:
docker login registry.onstackit.de
# Username: <service-account-email>
# Password: <service-account-token>
3. Set the Auth variable:
export STACKIT_SERVICE_ACCOUNT_KEY_PATH=/pfad/zu/sa-key.json
After that, the rest is all done in one go:
cd terraform
cp terraform.tfvars.example terraform.tfvars # Werte prüfen und anpassen
terraform init
terraform plan
terraform apply
Terraform builds and pushes the Docker images, provisions SKE, PostgreSQL Flex and Redis, and deploys the backend and frontend to the cluster.
If you’re interested: I’ve also recorded the whole process as a YouTube-Video.
Conclusion
If there’s just one thing to take away: don’t judge STACKIT on the basis of its ‘sovereignty’ pitch, but on its capabilities, costs and quality.
Data sovereignty is a nice bonus, not a reason to buy. What matters is that the platform is robust: a well-maintained Terraform provider, updated documentation, and an infrastructure model that resolves dependencies itself rather than making you write scripts.
Here are the three main takeaways in brief:
- Capabilities: What’s currently available is solid, but Kubernetes was overkill for a small app, and a lightweight container runtime is still missing. Better to have a few services that deliver than many that don’t.
- Costs: The biggest expense for a small app is the cluster baseline cost; you should factor this in carefully.
- Quality: The provider, documentation and IaC integration all feel production-ready. Managed services are a trade-off here – there’s no free lunch, as is the case with all providers; they offer huge operational benefits, but lock-in is inevitable, even with open-source products.
Reality has exceeded my expectations where I least expected it: in terms of sheer maturity. Not that everything can be managed with Terraform – I take that for granted – but rather how comprehensively the provider covers the resources and how good the revised documentation has become. And even that one beta shortcoming, the registry, can be neatly patched with the null provider, rather than throwing the whole process off course.
Notes:
You can find the complete code, including the Terraform modules, in this GitHub repository. If you’re working through this yourself, check at least these four points before going live:
- Remove ACLs from 0.0.0.0/0,
- Set Postgres tp replicas = 2 for failover,
- Tag images with Git-SHA instead of ‘latest’, and
- intergrate ‘terraform apply’ into a CI/CD pipeline rather than running it from a laptop.
Here you will find more information about STACKIT.
If you’re interested in further background information or serverless architectures, then book an appointment with Florian Lenz. As a strategic partner, he’ll be happy to support you every step of the way – from consultation and analysis through to design, architecture and operational implementation.
Would you like to support Florian Lenz or discuss STACKIT? Then please feel free to share this post within your network.
Florian Lenz has published two more posts on the t2informatik Blog:

Florian Lenz
Florian Lenz is the founder of neocentric GmbH, a certified Microsoft trainer and conference speaker. He has a great deal of expertise in Azure, organises meetups and runs a YouTube channel on Azure Serverless. He founded neocentric with the aim of strengthening companies in their efficiency, cost reduction and competitiveness. His specialisation in serverless solutions enables him to turn innovative ideas into leading products. He likes to do this in an authentic, down-to-earth and practical way.
In the t2informatik Blog, we publish articles for people in organisations. For these people, we develop and modernise software. Pragmatic. ✔️ Personal. ✔️ Professional. ✔️ Click here to find out more.

