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
/healthzendpoint 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
- Log in to Vigilmon → Add Monitor → HTTP.
- URL:
https://api.example.com/healthz - Check interval: 60 seconds.
- Response timeout: 10 seconds.
- Expected status code:
200. - JSON body assertion:
- Path:
status - Expected value:
ok
- Path:
- 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-alertschannel 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.
- In Vigilmon → Status Pages → Create.
- Add your gateway HTTP health monitor and smoke test heartbeat.
- Configure a custom domain (
status.example.com) or use the Vigilmon subdomain. - 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.