tutorial

Monitoring gRPC-Gateway Services with Vigilmon: External Uptime, Health Checks & Heartbeat Monitoring

Practical guide to adding external uptime monitoring to services that expose a RESTful JSON API via gRPC-Gateway — health endpoints, synthetic HTTP monitors, TLS checks, and independent alerting for your gRPC and HTTP gateway surfaces.

gRPC-Gateway is a protoc plugin that reads gRPC service definitions and generates a reverse-proxy server that translates RESTful JSON API calls into gRPC calls. It lets you serve both a high-performance gRPC interface and a standard HTTP/JSON interface from the same backend — a common pattern for services that need to support browser clients, CLI tools, and internal gRPC consumers simultaneously. Both surfaces need uptime monitoring, but your internal gRPC service mesh observability tools only see traffic that enters the mesh. Vigilmon sits entirely outside your infrastructure and probes your public-facing HTTP endpoints continuously from multiple external locations, alerting you whenever your gRPC-Gateway proxy or the underlying gRPC service it fronts becomes unavailable to real users.

This tutorial covers adding external uptime monitoring to services exposed via gRPC-Gateway.

What You'll Build

  • A standard /healthz endpoint added to your gRPC-Gateway HTTP mux
  • A Vigilmon HTTP monitor that validates the JSON health response
  • A heartbeat monitor for scheduled gRPC smoke tests
  • Alert routing that keeps gRPC service mesh alerts and external uptime alerts distinct

Prerequisites

  • A Go service using gRPC-Gateway (github.com/grpc-ecosystem/grpc-gateway/v2)
  • A public HTTPS endpoint for the gateway proxy
  • A free account at vigilmon.online

Step 1: Add a Health Endpoint to Your gRPC-Gateway Mux

gRPC-Gateway registers routes on a standard http.ServeMux or runtime.ServeMux. Add a /healthz handler directly on the HTTP mux so Vigilmon has an endpoint to probe that exercises both the proxy and the upstream gRPC connection.

// internal/gateway/health.go
package gateway

import (
	"context"
	"encoding/json"
	"net/http"
	"os"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/connectivity"
)

type healthResponse struct {
	Status string            `json:"status"`
	Checks map[string]string `json:"checks"`
	Host   string            `json:"host"`
}

// RegisterHealthHandler adds /healthz to mux and probes the gRPC backend conn.
func RegisterHealthHandler(mux *http.ServeMux, grpcConn *grpc.ClientConn) {
	mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
		checks := map[string]string{}
		ok := true

		// Check gRPC backend connectivity state
		state := grpcConn.GetState()
		if state == connectivity.Ready || state == connectivity.Idle {
			checks["grpc_backend"] = "ok"
		} else {
			checks["grpc_backend"] = state.String()
			ok = false
		}

		// Optionally probe a lightweight gRPC health check
		ctx, cancel := context.WithTimeout(r.Context(), 3*time.Second)
		defer cancel()
		_ = ctx // use with grpc_health_v1 if wired up

		host, _ := os.Hostname()
		resp := healthResponse{
			Status: func() string {
				if ok {
					return "ok"
				}
				return "degraded"
			}(),
			Checks: checks,
			Host:   host,
		}

		w.Header().Set("Content-Type", "application/json")
		if !ok {
			w.WriteHeader(http.StatusServiceUnavailable)
		}
		json.NewEncoder(w).Encode(resp)
	})
}

Wire the handler into your gateway setup:

// cmd/server/main.go
package main

import (
	"context"
	"log"
	"net/http"

	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"

	pb "yourmodule/gen/proto/v1"
	"yourmodule/internal/gateway"
)

func main() {
	grpcAddr := "localhost:9090"

	conn, err := grpc.NewClient(grpcAddr,
		grpc.WithTransportCredentials(insecure.NewCredentials()),
	)
	if err != nil {
		log.Fatalf("failed to dial gRPC backend: %v", err)
	}
	defer conn.Close()

	gwMux := runtime.NewServeMux()
	if err := pb.RegisterYourServiceHandlerClient(context.Background(), gwMux, pb.NewYourServiceClient(conn)); err != nil {
		log.Fatalf("failed to register gateway: %v", err)
	}

	httpMux := http.NewServeMux()
	gateway.RegisterHealthHandler(httpMux, conn)
	httpMux.Handle("/", gwMux)

	log.Println("gRPC-Gateway listening on :8080")
	if err := http.ListenAndServe(":8080", httpMux); err != nil {
		log.Fatalf("server error: %v", err)
	}
}

Verify the endpoint:

curl -s https://api.example.com/healthz | jq .
# {"status":"ok","checks":{"grpc_backend":"ok"},"host":"pod-abc123"}

Step 2: Configure Vigilmon HTTP Monitor

  1. Log in to VigilmonAdd Monitor → HTTP.
  2. URL: https://api.example.com/healthz
  3. Check interval: 60 seconds.
  4. Response timeout: 10 seconds.
  5. Expected status code: 200.
  6. JSON body assertion:
    • Path: status
    • Expected value: ok
  7. Click Save.

Vigilmon probes from multiple external locations concurrently and requires a quorum of probe agents to agree on a failure before generating an alert — this eliminates false positives from regional routing anomalies that don't reflect actual service availability.

Monitor both gateway and gRPC surfaces separately

| Surface | URL | Protocol | Interval | |---|---|---|---| | HTTP/JSON gateway | https://api.example.com/healthz | HTTP | 60 s | | gRPC reflection endpoint | https://api.example.com/grpc.health.v1.Health/Check | HTTP | 60 s | | TLS certificate | api.example.com:443 | SSL | Continuous |

gRPC-Gateway serves gRPC on the same port when using a combined HTTP/2 mux. Vigilmon's HTTP monitor covers both surfaces through the gateway proxy.


Step 3: Heartbeat Monitor for gRPC Smoke Tests

gRPC services are often tested with tools like grpcurl or evans in scheduled smoke test pipelines. If those jobs stop running — because a cron job fails silently or a runner goes offline — you lose visibility into whether your gRPC health check passes. Vigilmon heartbeat monitors catch missed runs.

#!/bin/bash
# scripts/grpc-smoke-test.sh
set -euo pipefail

GRPC_HOST="${GRPC_HOST:-api.example.com:443}"

echo "Running gRPC smoke tests..."

# Test reflection availability
grpcurl -proto proto/v1/your_service.proto \
  "${GRPC_HOST}" \
  grpc.health.v1.Health/Check

# Test a real service method
grpcurl -proto proto/v1/your_service.proto \
  -d '{"id": "health-check-probe"}' \
  "${GRPC_HOST}" \
  yourpackage.v1.YourService/GetItem

echo "Smoke tests passed — sending heartbeat"
curl -s -X POST "${VIGILMON_HEARTBEAT_URL}" --max-time 5

GitHub Actions integration

# .github/workflows/grpc-smoke-tests.yml
name: gRPC Smoke Tests

on:
  schedule:
    - cron: '*/15 * * * *'  # Every 15 minutes

jobs:
  smoke:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install grpcurl
        run: |
          curl -LO https://github.com/fullstorydev/grpcurl/releases/download/v1.9.1/grpcurl_1.9.1_linux_amd64.tar.gz
          tar xf grpcurl_1.9.1_linux_amd64.tar.gz
          sudo mv grpcurl /usr/local/bin/

      - name: Run gRPC smoke tests
        env:
          GRPC_HOST: ${{ vars.GRPC_HOST }}
        run: bash scripts/grpc-smoke-test.sh

      - name: Send Vigilmon heartbeat
        if: success()
        run: curl -X POST "${{ secrets.VIGILMON_HEARTBEAT_URL }}"

In Vigilmon, create a Heartbeat monitor with a grace period of 30 minutes — longer than the 15-minute run interval — so brief runner delays don't generate false alerts.


Step 4: TLS and HTTP/2 Certificate Monitoring

gRPC-Gateway typically serves over TLS with HTTP/2. If the certificate expires, all gRPC-over-HTTP/2 clients and the JSON gateway break simultaneously. Add a Vigilmon TLS monitor:

In Vigilmon → Add Monitor → SSL/TLS certificate:

  • Host: api.example.com
  • Port: 443
  • Alert before expiry: 30 days

For services behind a load balancer that terminates TLS, monitor the load balancer hostname, not the individual pod or VM.


Step 5: Alert Routing Strategy

gRPC-Gateway services typically have two alert streams: internal mesh telemetry (Prometheus, Jaeger traces, service mesh health) and external uptime alerts (Vigilmon). Keep them routed differently.

| Failure mode | Source | Route to | |---|---|---| | gRPC backend error rate spike | Prometheus / service mesh | Slack #grpc-service-alerts + on-call | | HTTP gateway endpoint down | Vigilmon | PagerDuty + Slack #prod-alerts | | Smoke test job missed | Vigilmon | Slack #api-ops-critical | | TLS certificate expiring | Vigilmon | DevOps team + ticket | | gRPC latency P99 degradation | Internal metrics | SRE on-call |

Configure Vigilmon alert channels under Alerts → Add channel:

  • Email: on-call distribution list.
  • PagerDuty webhook: primary on-call rotation for production outages.
  • Slack webhook: a #vigilmon-alerts channel for the ops team.

Step 6: Public Status Page

gRPC service mesh dashboards are internal. Your API consumers — whether they use the gRPC or JSON surface — benefit from a public status page that shows availability without requiring access to your internal tooling.

  1. In Vigilmon → Status Pages → Create.
  2. Add your gateway HTTP health monitor and smoke test heartbeat.
  3. Configure a custom domain (status.example.com) or use the Vigilmon subdomain.
  4. Add a status badge to your API documentation:
<img src="https://vigilmon.online/badge/<monitor-id>.svg" alt="API Status" />

What Vigilmon Adds to a gRPC-Gateway Deployment

| Capability | gRPC-Gateway | Vigilmon | |---|---|---| | REST-to-gRPC transcoding | Yes | No | | Proto-based service definition | Yes | No | | HTTP/2 multiplexing | Yes | No | | Internal service mesh integration | Yes | No | | External synthetic HTTP probing | No | Yes | | Multi-location quorum-based checks | No | Yes | | TLS/SSL certificate expiry alerts | No | Yes | | Scheduled job heartbeat monitoring | No | Yes | | Public status page | No | Yes | | Independent of internal mesh health | No | Yes |


gRPC-Gateway bridges gRPC services to HTTP/JSON consumers efficiently. Vigilmon bridges the gap between internal service mesh observability and the external user perspective — continuously probing your gateway endpoints from outside your infrastructure to catch availability failures the moment they occur.

Add external uptime monitoring to your gRPC-Gateway services today — register free at vigilmon.online.

Monitor your app with Vigilmon

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

Start free →