From Local Chaos to Cloud Success

28 Oct 2025 . Tech . Comments
#SpringBoot #Microservices #Java #SoftwareEngineering #Architecture

From Local Chaos to Cloud Success

The Challenge: Taking a Real Microservices System to the Cloud

When I built my flagship Spring Boot project, a production-grade e-commerce platform with six microservices, I knew the biggest challenge wasn’t just the code.
It was getting the entire system deployed, connected, and observable in the cloud.

I have tried on paper all the known cloud providers like AWS, GCP, Azure. Each one had powers, but also bureaucracy: IAM roles, VPCs, Terraform pipelines, YAML jungles, let alone the high cost in some of the cases.

So I went with a platform that aligned with how I like to work: fast iteration, clean deployments, and full control from my IDE and my github.
That platform was Railway.


Why Railway Made Sense

Railway is like “Heroku reimagined for modern developers.”
You connect a GitHub repo and Railway handles containerization, SSL, scaling, and environment variables automatically.

For my microservices architecture, this meant I could deploy six services independently. In other words no Kubernetes, no frustration.


🧩 The Architecture

I wanted this project to mirror what real companies run in production with independent, event-driven microservices communicating securely.

So what we got …

Six Microservices

  • API Gateway — Spring Cloud Gateway managing routes, JWT auth, rate limiting, and resilience patterns
  • User Service — Handles authentication and identity with Keycloak integration
  • Order Service — Publishes events to Kafka for order processing
  • Payment Service — Implements idempotent transactions and Resilience4j
  • Inventory Service — Redis caching and product management
  • Streaming Service — Real-time updates via WebFlux and Server-Sent Events

Tech Stack

  • Java 21 + Spring Boot 3.5
  • Railway-managed PostgreSQL
  • Redis for caching
  • Kafka template-based event publishing
  • Keycloak for OAuth2/JWT security
  • Prometheus + Grafana for metrics

The Real Deployment Challenges

1. Environment Variable Chaos

Each service required its own database credentials, Kafka setup, Redis host, and Keycloak configuration.
Initially, it was chaos. But is this so bad ? The actual answer is no. That’s why we got environments and thus environmental variables.

So I created per-service application-railway.yml profiles and used Railway’s environment variable injection.
This gave me clean separation, zero hardcoded secrets, and simple environment management.

spring:
  data:
    redis:
      host: ${REDIS_HOST}
      port: ${REDIS_PORT}
      password: ${REDIS_PASSWORD:}

2. Service Communication Without a Service Mesh

Railway runs each service in its own container, meaning no localhost discovery and no Kubernetes DNS. I used Spring Cloud Gateway as the single entry point.
So each service’s Railway URL is stored in environment variables, and the Gateway routes traffic while validating JWTs via Keycloak.


3. Shared Database Instead of “One DB per Service”

The pure microservices approach says one database per service, but Railway’s model encouraged simplicity AND keen the cost low. (Remember this is a demo project)

So I used a single shared PostgreSQL schema, meaning that all services share one instance.

Why it worked:

  • Easier migrations
  • Lower resource usage
  • Logical isolation with table prefixes
  • Still maintained service-level boundaries in code

It’s not dogmatic microservices! It’s pragmatic microservices that ship.


4. Event-Driven Design with Kafka Template

Railway doesn’t offer native Kafka, but I set up a Kafka template service running in my environment.
All microservices use Spring Kafka’s KafkaTemplate and @KafkaListener for event flow.

Result:
I kept the event-driven architecture intact, connecting Orders → Payments → Inventory → Streaming in real time.

@Service
public class OrderEventProducer {
    private final KafkaTemplate<String, String> kafkaTemplate;

    public void publishOrder(String orderJson) {
        kafkaTemplate.send("orders", orderJson);
    }
}

5. Monitoring Without Heavyweight Tools

Prometheus + Grafana are great, but running them on limited Railway resources wasn’t ideal.

So rather than upgrading a tier, I used Spring Actuator and Railway’s built-in metrics for lightweight monitoring.
Focus shifted from infrastructure to application health and business KPIs.


✨ Breakthrough Moments

  • ✅ First End-to-End Success:
    All services deployed, Kafka events flowing, Gateway routing correctly. Everything just worked or that what I suppose.

  • ⚡ Performance Wins:
    Redis caching + optimized HikariCP pooling delivered sub-second response times.

  • 🔒 Security Simplified:
    Keycloak integration made OAuth2/JWT authentication stateless and scalable across all services.


Technical Deep Dive Highlights

  • Database Strategy: Single schema, logical isolation, low-latency connection pooling
  • Kafka Integration: Event-driven communication using Spring Kafka template
  • Caching: Cache-aside pattern with Redis TTL invalidation
  • Security: Stateless JWT validation with Keycloak realm integration
  • Deployment: Multi-service Docker setup orchestrated by railway.toml

The Result

✅ Six microservices deployed on Railway
⚡ Sub-second response times
🔐 Fully secured with OAuth2/JWT
📦 Event-driven communication with Kafka
🩺 Lightweight monitoring with Actuator
☁️ Scalable, production-grade infrastructure


What This Project Demonstrates

For developers, this project highlights real-world, senior-level engineering skills or at least tries to:

  • Architectural Design: Microservices that actually deploy and communicate
  • Cloud Pragmatism: Balancing best practices with real constraints
  • Performance Optimization: Caching, connection pooling, async events
  • Security Implementation: OAuth2 + JWT with Keycloak
  • DevOps Literacy: Containerization, CI/CD, multi-service orchestration

This isn’t theory — it’s production-grade architecture deployed for real.


Looking Ahead

Next steps include:

  • Service mesh experimentation (Istio-like patterns on Railway)
  • Distributed tracing and APM integration
  • Schema separation via Liquibase migrations

But the biggest takeaway?
You don’t need Kubernetes to run serious microservices.
With the right design and a platform like Railway, you can ship production systems faster — and smarter.


In a world obsessed with cloud complexity, sometimes the best engineering decision is the one that lets you focus on building great software.


Me

Marios Gavriil is an awesome person. He lives between Jigglypuffs and Deadpools figures and loves unicorns. In his spare time composing music or experimenting new recipes in the kitchen