Building real software with quality, low cost, and a clear product vision is something many developers talk about — but few actually do. In this post, I’ll show how I built a blog with a Quarkus backend, Firestore as the database, file storage on Google Cloud Storage, and deployed everything using Cloud Run, spending almost zero.
This blog isn’t a weekend project. I designed it as a real product — something I can use as a technical portfolio, an idea lab. Along the way, I faced real-world challenges: clean architecture, testing, cold starts, authentication, cost control, and integration with the frontend. I’ll walk you through how I handled each of them.
If you’re a backend developer who cares about clean code, architecture, and building something real without breaking the bank — this is for you.
Why I Decided to Build This Blog
I'm a senior developer based in Europe, with experience in complex systems using Java, microservices, clean architecture, testing, and cloud infrastructure. Over the years, I’ve often wanted a space to share real project insights — not just isolated code snippets or tutorials.
This blog was born with three clear goals:
- To share real, technical content in a simple and direct way
- To serve as an example for developers who want to learn by building real things
- To prove that you can build a clean, scalable, and low-cost backend using GCP
Choosing the Right Stack – Part 1: Why I Chose Quarkus
Let’s start with the core of the backend: Quarkus.
I’ve worked with Spring Boot for years and it’s a fantastic framework — mature, well-documented, and extremely popular. But for this project, I wanted something different. Something more aligned with cloud-native design, fast startup times, and low memory consumption. That’s where Quarkus stood out.
Quarkus is built with containers and serverless in mind. Unlike traditional Java stacks, it was designed from the ground up to run efficiently in environments like Google Cloud Run, which scale down to zero when idle. That’s a big deal for me, since I want to keep this blog live 24/7 — but only pay when someone’s actually using it.
I also appreciated how easy it was to build native images with GraalVM. Knowing that I can compile my app to a super-fast binary without major changes is a big win.
Another thing I like: Quarkus makes it easy to write clean code. I’m using a layered architecture that separates the domain, application logic, and infrastructure. Quarkus doesn’t get in the way of that. Dependency injection is simple. Startup logs are minimal and clean. It doesn’t force you into heavy annotations or XML configurations.
Of course, the ecosystem isn’t as large as Spring’s — but for this blog, that’s fine. I don’t need 50 different starter projects. I need performance, clarity, and a framework that plays well with modern cloud services — and Quarkus delivers exactly that.
Lastly, testing has been straightforward. I’m using @QuarkusTest for integration tests and I’ve set up emulators, so I can run tests locally without touching the cloud. That alone saves time, money, and gives me confidence that what I build works — before I even deploy it.
Choosing the Right Stack – Part 2: Why I Chose Next.js
For the frontend, I wanted a framework that was modern, fast, and flexible — something that let me build a clean UI and handle content efficiently. Next.js checked all the boxes.
One of the things I like most about Next.js is its hybrid rendering model. It allows me to pre-render static pages like blog posts and homepages while still supporting dynamic rendering when needed — without switching tools. That gives me great performance and good SEO, without the complexity of managing separate systems.
Next.js also makes it easy to write modular, reusable components, which helps keep the codebase clean and easy to maintain. I’m using Tailwind CSS alongside it, which means I can iterate on styles quickly without writing bloated CSS files.
Another win is how well Next.js handles routing and page structure. It’s intuitive, and the project scales naturally as more pages are added. For a blog, this makes a big difference — I can focus on writing content and evolving the design without worrying about plumbing.
At the moment, the blog doesn’t offer public user login or commenting. However, I did build a simple admin view, protected by Firebase Authentication, which is used only by me. Based on the user’s role (via custom claims), the UI conditionally renders certain routes and actions, like creating or editing posts. It’s invisible to regular visitors.
Lastly, it’s important to mention that although I’m using Google Cloud for the backend, the frontend is not deployed on GCP — and I don’t plan to host it there. Deploying to Vercel was a good surprise, since it is quite easy to setup and configure.
Part 3: Google Cloud and Vercel – A Balanced Architecture
This blog project uses a hybrid architecture:
- The backend, written in Quarkus, runs on Google Cloud Run
- The frontend, built with Next.js, is deployed separately using Vercel
Each platform serves its purpose well.
🔹 Why Google Cloud for the Backend
Google Cloud offers everything I need for the backend: serverless compute with Cloud Run, Firestore for database, Cloud Storage for image uploads and Firebase Auth for secure admin access.
The real advantage is how well these services work together. I can store data easily, and everything integrates smoothly using Google’s SDKs and service accounts. It feels like an ecosystem, not just a collection of tools.
With Cloud Run, I get automatic HTTPS, traffic management, scaling to zero, and a clean developer experience. It’s also cost-efficient: I only pay when someone is actually using the backend — perfect for a personal blog.
🔹 Why Vercel for the Frontend
Vercel made deploying the frontend effortless. It connects directly to my GitHub repo, builds the site on push, and gives me a fast, globally distributed site with zero config.
Since my blog is mostly static content and doesn’t require complex server-side logic on the frontend, Vercel is a great fit. It handles all the CI/CD, CDN, and preview deployments automatically.
Using Vercel also keeps things decoupled. The frontend is its own project, deployable and testable independently of the backend. If I want to rebuild or replace one side, I don’t need to touch the other.
Architecture of the Project
One of the most important goals in this project was to keep the architecture simple without compromising good practices. Since the system is small and managed by a single person (that’s me), the main focus was on clarity, separation of concerns, and easy maintenance.
Domain at the Center
On the backend, I followed an approach inspired by Clean Architecture. The domain is at the heart of the system, represented by classes that model exactly what the blog needs — such as the content of a post, tags, author, and publication status. These classes are completely unaware of infrastructure or external frameworks.
For example, the content of a post is modeled as a structure that supports ordered blocks of text and images, with Markdown support. This design gives flexibility on both backend and frontend to render the content exactly as intended.
Use Cases and Services
The business rules are organized into use cases, which coordinate logic between domain objects and external services (like data persistence or authentication). This means the logic for creating, editing, or listing posts is centralized and easily testable.
Decoupled Infrastructure
Under the hood, there’s the infrastructure layer — responsible for integrating with Firestore (Google’s NoSQL database), Firebase Authentication (used only by me to control access), and Google Cloud Storage (to store images for blog posts). These dependencies are isolated and can be replaced easily in the future if needed.
Claim-Based Access Control
Although I’m the only one who logs in to the system, access control still matters. Certain buttons, admin routes, or features are hidden from anonymous users based on custom claims inside the Firebase-issued JWT token — like role: admin
. This allows me to expand the system later, potentially adding new authors with different permissions.
Conclusion
Building this blog has been much more than a technical exercise — it’s a reflection of how I approach software design: balancing simplicity, clarity, and strong architectural principles. I didn’t want to just throw code together until something worked; I wanted to build a solid foundation that could grow over time.
From the backend built with Quarkus to the frontend rendered with Next.js, every decision was intentional. I chose tools that are modern, fast, and fit well together. The use of Google Cloud services gave me reliability and scalability without unnecessary complexity.
More than anything, this blog is a personal space to share knowledge, ideas, and experiments. It’s built to be maintainable, easy to test, and ready to evolve — whether that means new content, new features, or even bigger architectural changes down the road.
If you’re a developer looking to build your own project, I hope this overview gives you some inspiration or at least a starting point. If you’re just curious, thanks for reading this far. And if you ever want to dive deeper into the code or ask questions — my inbox is always open.