go
56 lines · 7 steps
Liveness vs readiness health checks in Gin
A Gin handler that separates 'is the process alive' from 'can it actually serve traffic' by pinging its dependencies.
Explained by
highlit
1package handlers
2
3import (
4 "context"
5 "net/http"
6 "time"
7
8 "github.com/gin-gonic/gin"
9 "github.com/redis/go-redis/v9"
10 "gorm.io/gorm"
11)
12
13type HealthHandler struct {
14 DB *gorm.DB
15 Redis *redis.Client
16}
17
18func RegisterHealthRoutes(r *gin.Engine, h *HealthHandler) {
19 group := r.Group("/health")
20 {
21 group.GET("/live", h.Liveness)
22 group.GET("/ready", h.Readiness)
23 }
24}
25
26func (h *HealthHandler) Liveness(c *gin.Context) {
27 c.JSON(http.StatusOK, gin.H{"status": "ok"})
28}
29
30func (h *HealthHandler) Readiness(c *gin.Context) {
31 ctx, cancel := context.WithTimeout(c.Request.Context(), 2*time.Second)
32 defer cancel()
33
34 checks := gin.H{"database": "ok", "redis": "ok"}
35 ready := true
36
37 if sqlDB, err := h.DB.DB(); err != nil || sqlDB.PingContext(ctx) != nil {
38 checks["database"] = "unavailable"
39 ready = false
40 }
41
42 if err := h.Redis.Ping(ctx).Err(); err != nil {
43 checks["redis"] = "unavailable"
44 ready = false
45 }
46
47 status := http.StatusOK
48 if !ready {
49 status = http.StatusServiceUnavailable
50 }
51
52 c.JSON(status, gin.H{
53 "ready": ready,
54 "checks": checks,
55 })
56}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Liveness answers whether the process is running while readiness answers whether it can serve requests, and they deserve separate endpoints.
- 2Bounding dependency checks with a context timeout stops a hung database or cache from making the probe itself hang.
- 3Returning 503 when any dependency is down lets orchestrators route traffic away instead of failing user requests.
Related explainers
go
package security import ( "crypto/hmac"
Signing and verifying payloads with HMAC in Go
hmac
cryptography
authentication
Intermediate
5 steps
typescript
import { Injectable, NotFoundException, ConflictException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository, DeepPartial } from 'typeorm'; import { User } from './entities/user.entity';
Building a CRUD service in NestJS
crud
dependency-injection
repository-pattern
Intermediate
7 steps
go
package handlers import ( "net/http"
Custom query validation in Gin
validation
query-binding
deduplication
Intermediate
8 steps
typescript
import { Controller, Get, Param, Query, Redirect, NotFoundException } from '@nestjs/common'; import { LinksService } from './links.service'; @Controller('links')
Handling HTTP redirects in NestJS
redirects
routing
decorators
Intermediate
7 steps
python
import secrets from datetime import datetime, timedelta, timezone from fastapi import APIRouter, Cookie, Depends, HTTPException, Response, status
Cookie session auth in FastAPI
session-authentication
cookies
dependency-injection
Intermediate
8 steps
go
package storage import ( "context"
A SQLite-backed order store in Go
database/sql
sqlite
error-wrapping
Intermediate
7 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/liveness-vs-readiness-health-checks-in-gin-explained-go-924e/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.