tutorial

Uptime Monitoring for Spring Cloud Gateway

Your Spring Cloud Gateway is routing production traffic — but is it actually healthy? Set up external uptime monitoring, route-level checks, and instant alerts in under 30 minutes — free.

Uptime Monitoring for Spring Cloud Gateway

Your Spring Cloud Gateway is the front door to your microservices — every API call passes through it. When it goes down, everything goes down. When a route silently breaks, a whole class of requests starts failing. Monitoring the gateway itself is not enough; you need to watch what it does.

One misconfigured route after a config-server update, a load balancer URI that stops resolving because a service was renamed, a rate-limiter Redis instance that died in the night — these break entire service paths while the gateway process stays alive and healthy-looking. By the end of this guide you'll have external uptime monitoring, route-level synthetic checks, heartbeat monitoring for gateway-adjacent scheduled tasks, and a public status page — all on the free tier.


Why Spring Cloud Gateway deployments go dark

Gateways fail in ways that application-level health misses:

Route failures — the gateway process is healthy but a specific route is returning 502 because the upstream service is down or the URI is unresolvable. Your /health endpoint says UP because it only checks the gateway process, not the routes it serves.

Predicate and filter misconfigurations — a deployment changes a route predicate or adds a new filter. Requests to affected paths start failing silently. The gateway logs No route found or Connection refused per request, but no alert fires because no monitor is watching those specific paths.

Rate limiter state loss — the Redis instance backing your rate limiter goes down. Depending on your deny-empty-key configuration, this either breaks rate-limited routes or opens them completely. Both outcomes are silent from a process-health perspective.


Step 1: Enable Spring Boot Actuator health endpoints

Spring Cloud Gateway builds on Spring Boot Actuator. Add the dependency if you don't have it:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

Configure application.yml to expose the health endpoint with full detail:

management:
  endpoints:
    web:
      exposure:
        include: health, info, gateway
  endpoint:
    health:
      show-details: always
      probes:
        enabled: true
  health:
    livenessState:
      enabled: true
    readinessState:
      enabled: true

This exposes:

  • /actuator/health — overall health with component details
  • /actuator/health/liveness — process alive check
  • /actuator/health/readiness — ready to serve traffic check
  • /actuator/gateway/routes — all registered routes (useful for debugging)

Verify locally:

curl -s http://localhost:8080/actuator/health | jq .

Step 2: Add a custom downstream health indicator

The built-in health endpoint doesn't check whether your upstream services are actually reachable. Add a custom ReactiveHealthIndicator to probe a critical upstream:

import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import java.time.Duration;

@Component("orderService")
public class OrderServiceHealthIndicator implements ReactiveHealthIndicator {

    private final WebClient webClient;

    public OrderServiceHealthIndicator(WebClient.Builder builder) {
        this.webClient = builder
                .baseUrl("http://order-service")
                .build();
    }

    @Override
    public Mono<Health> health() {
        return webClient.get()
                .uri("/actuator/health/readiness")
                .retrieve()
                .toBodilessEntity()
                .timeout(Duration.ofSeconds(3))
                .map(response -> Health.up()
                        .withDetail("status", response.getStatusCode().value())
                        .build())
                .onErrorResume(ex -> Mono.just(
                        Health.down()
                                .withDetail("error", ex.getMessage())
                                .build()
                ));
    }
}

Register as many of these as you have critical upstream services. When any one returns DOWN, the overall /actuator/health responds with HTTP 503.


Step 3: Set up external monitoring with Vigilmon

With /actuator/health/readiness returning accurate 503s on failure, point Vigilmon at it:

  1. Sign up at vigilmon.online — free tier, no credit card
  2. Click New Monitor → HTTP
  3. Enter https://api.yourdomain.com/actuator/health/readiness
  4. Set check interval (5 minutes on free tier)
  5. Save

Because the gateway is the entry point for all services, layer additional monitors for the most critical routes:

| Monitor target | What it catches | |---|---| | /actuator/health/readiness | Gateway process, Redis rate limiter, upstream health indicators | | /actuator/health/liveness | JVM hang, OOM, deadlock | | /api/v1/orders/ping | Order service route end-to-end reachability | | /api/v1/users/ping | User service route end-to-end reachability | | /api/v1/payments/ping | Payment service route end-to-end reachability |

This gives you per-service alerting through the gateway. When the payment route breaks, Vigilmon tells you it's the payment route — not just "the API is down."


Step 4: Add lightweight ping routes for synthetic monitoring

Add a dedicated /ping route to each upstream service and expose them through dedicated gateway routes for synthetic health probing:

spring:
  cloud:
    gateway:
      routes:
        # Synthetic health probe routes — monitored by Vigilmon
        - id: order-service-ping
          uri: lb://order-service
          predicates:
            - Path=/api/v1/orders/ping
          filters:
            - RewritePath=/api/v1/orders/ping, /actuator/health/readiness

        - id: user-service-ping
          uri: lb://user-service
          predicates:
            - Path=/api/v1/users/ping
          filters:
            - RewritePath=/api/v1/users/ping, /actuator/health/readiness

        # Normal business routes
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/api/v1/orders/**

Each /ping route proxies to the upstream service's readiness endpoint. Vigilmon monitoring these routes tests the complete path: DNS → load balancer → gateway routing → service discovery → upstream process.


Step 5: Heartbeat monitoring for gateway-adjacent jobs

Gateway deployments often include scheduled tasks: route config refreshes, circuit breaker state resets, metrics aggregation. Monitor these with heartbeats.

import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component
public class RouteConfigRefreshJob {

    private static final Logger log = LoggerFactory.getLogger(RouteConfigRefreshJob.class);

    private final RouteDefinitionWriter routeWriter;
    private final WebClient webClient;

    @Value("${heartbeat.url:}")
    private String heartbeatUrl;

    public RouteConfigRefreshJob(RouteDefinitionWriter routeWriter, WebClient.Builder builder) {
        this.routeWriter = routeWriter;
        this.webClient = builder.build();
    }

    @Scheduled(fixedDelayString = "${gateway.refresh.interval:PT15M}")
    public void refreshRoutes() {
        try {
            doRefresh();
            pingHeartbeat();
        } catch (Exception e) {
            log.error("Route config refresh failed: {}", e.getMessage(), e);
            // Do NOT ping — let Vigilmon detect the missed beat
        }
    }

    private void doRefresh() {
        // Route refresh logic here
    }

    private void pingHeartbeat() {
        if (heartbeatUrl == null || heartbeatUrl.isBlank()) {
            return;
        }
        webClient.get()
                .uri(heartbeatUrl)
                .retrieve()
                .toBodilessEntity()
                .subscribe(
                        r -> log.debug("Heartbeat ping sent"),
                        e -> log.warn("Heartbeat ping failed: {}", e.getMessage())
                );
    }
}

In Vigilmon:

  1. Click New Monitor → Heartbeat
  2. Set the expected interval to match your refresh schedule plus a grace window
  3. Copy the unique ping URL and set it as HEARTBEAT_URL in your deployment environment

Step 6: Webhook alerts and the uptime badge

Slack and Discord alerts:

  1. In Vigilmon go to Notifications → New Channel
  2. Choose Slack or Discord, paste your webhook URL
  3. Enable it on your monitors

For a gateway you want alerts fast — route failures affect every user. Set your notification channel to alert immediately on the first failure (no retries delay).

Add an uptime badge to your README:

[![API Gateway Uptime](https://vigilmon.online/badge/your-monitor-id.svg)](https://vigilmon.online?utm_source=devto&utm_medium=article&utm_campaign=spring-cloud-gateway-tutorial)

What you've built

| What | How | |---|---| | Gateway process health | /actuator/health/readiness + Vigilmon HTTP monitor | | Upstream service health | Custom ReactiveHealthIndicator per service | | Route-level synthetic checks | /ping routes through gateway + Vigilmon HTTP monitors | | Silent scheduler job detection | Heartbeat ping after successful @Scheduled run | | Instant incident alerts | Slack/Discord webhook notifications | | README status badge | Vigilmon badge embed |

The whole setup runs on Vigilmon's free tier and takes under 30 minutes. Your gateway health endpoint returns accurate 503s when upstreams fail, Vigilmon probes each critical route independently, and your maintenance jobs ping in on every successful run. The next silent route failure gets caught before it becomes a customer escalation.


Monitor your Spring Cloud Gateway free at vigilmon.online — monitors running in under a minute, no credit card required.

Monitor your app with Vigilmon

Free plan — 5 monitors, no credit card required. Up and running in 60 seconds.

Start free →