# How to Implement Graceful Shutdown for a Gin Server in Go

> Implement graceful shutdown for your Gin server in Go. Learn how to manage OS termination signals and drain active connections to prevent dropped requests.

- Repository: [Gin-Gonic/gin](https://github.com/gin-gonic/gin)
- Tags: how-to-guide
- Published: 2026-02-27

---

**To implement graceful shutdown for a Gin server, create an `http.Server` manually with the Gin router as its handler, start it in a goroutine, listen for OS termination signals, and call `server.Shutdown(ctx)` with a timeout context to drain active connections without dropping in-flight requests.**

Gin is a high-performance HTTP web framework that implements the standard `http.Handler` interface. While the convenience method `engine.Run()` provides a quick way to start listening, it blocks the calling goroutine forever and prevents clean termination. Implementing graceful shutdown for a Gin server requires bypassing this helper to gain direct control over the underlying `http.Server` instance, ensuring your Go application can handle `SIGINT` or `SIGTERM` signals without interrupting active HTTP requests.

## Why Avoid `engine.Run()` for Production Services

The `Engine.Run()` method in [[`gin.go`](https://github.com/gin-gonic/gin/blob/main/gin.go)](https://github.com/gin-gonic/gin/blob/master/gin.go#L37-L56) creates an internal `http.Server` and immediately calls `ListenAndServe()`, blocking execution indefinitely. This design prevents the main goroutine from listening to OS signals or orchestrating a controlled shutdown. According to the source code at lines 37-56, this helper is intended for rapid prototyping, not production deployments that require lifecycle management.

## Implementing Graceful Shutdown for a Gin Server (Step-by-Step)

Since Go 1.8, the standard library provides `http.Server.Shutdown()`, which coordinates with the underlying `net.Listener` to stop accepting new connections while allowing existing requests to complete. Gin’s official documentation in [[`docs/doc.md`](https://github.com/gin-gonic/gin/blob/main/docs/doc.md)](https://github.com/gin-gonic/gin/blob/master/docs/doc.md#L51-L55) describes this pattern in the *Graceful shutdown or restart* section.

### Step 1: Create the Gin Router Without Calling `Run()`

Instantiate your router using `gin.Default()` or `gin.New()`, but do not invoke the `Run()` method. This keeps the engine ready to serve HTTP traffic without blocking your main goroutine.

```go
router := gin.Default()
router.GET("/", func(c *gin.Context) {
    c.String(http.StatusOK, "Hello World")
})

```

### Step 2: Instantiate Your Own `http.Server`

Construct an `http.Server` struct manually, assigning the Gin router to the `Handler` field. This gives you a server handle required for the shutdown procedure.

```go
srv := &http.Server{
    Addr:    ":8080",
    Handler: router,
}

```

### Step 3: Start the Server in a Goroutine

Launch `srv.ListenAndServe()` inside its own goroutine. This allows the main goroutine to continue execution and monitor for termination signals, while the server handles incoming traffic concurrently.

```go
go func() {
    if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
        log.Fatalf("listen: %s\n", err)
    }
}()

```

### Step 4: Capture OS Interrupt Signals

Create a channel to receive operating system signals and register it with `signal.Notify` to watch for `SIGINT` and `SIGTERM`. The main goroutine blocks on this channel until an interrupt occurs.

```go
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("Shutting down server...")

```

### Step 5: Create a Context with Timeout

Define a `context.Context` with a deadline that bounds how long you allow existing connections to finish processing. A typical timeout ranges from 5 to 30 seconds depending on your workload characteristics.

```go
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

```

### Step 6: Trigger Graceful Shutdown

Invoke `srv.Shutdown(ctx)`. This method stops the server from accepting new connections, closes idle connections, and waits for active handlers to return or for the context timeout to expire.

```go
if err := srv.Shutdown(ctx); err != nil {
    log.Fatalf("Server forced to shutdown: %v", err)
}

```

### Step 7: Handle Exit

After `Shutdown()` returns, log the termination status and allow the main function to exit cleanly. All in-flight requests that completed within the timeout window will have been served successfully.

```go
log.Println("Server exiting")

```

## Complete Working Example

The following implementation demonstrates the full pattern, including a simulated long-running handler to test the graceful shutdown behavior. This example aligns with the official code snippet found in [[`docs/doc.md`](https://github.com/gin-gonic/gin/blob/main/docs/doc.md)](https://github.com/gin-gonic/gin/blob/master/docs/doc.md#L71-L33).

```go
package main

import (
	"context"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/gin-gonic/gin"
)

func main() {
	// 1️⃣ Create router
	router := gin.Default()
	router.GET("/", func(c *gin.Context) {
		// Simulate work that takes a few seconds
		time.Sleep(5 * time.Second)
		c.String(http.StatusOK, "Welcome Gin Server")
	})

	// 2️⃣ Build an http.Server without calling router.Run()
	srv := &http.Server{
		Addr:    ":8080",
		Handler: router,
	}

	// 3️⃣ Start the server in a goroutine
	go func() {
		if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			log.Fatalf("listen: %s\n", err)
		}
	}()

	// 4️⃣ Listen for OS interrupt signals
	quit := make(chan os.Signal, 1)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
	<-quit // block until signal is received
	log.Println("Shutting down server…")

	// 5️⃣ Create a context with a timeout for the shutdown process
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	// 6️⃣ Trigger graceful shutdown
	if err := srv.Shutdown(ctx); err != nil {
		log.Fatalf("Server forced to shutdown: %v", err)
	}

	log.Println("Server exiting")
}

```

## Alternative Third-Party Approaches

If you prefer to abstract the boilerplate, several community packages provide drop-in replacements for `ListenAndServe` with graceful shutdown capabilities. The Gin documentation in [[`docs/doc.md`](https://github.com/gin-gonic/gin/blob/main/docs/doc.md)](https://github.com/gin-gonic/gin/blob/master/docs/doc.md#L55-L71) mentions these options:

- **`github.com/fvbock/endless`** – Provides `endless.ListenAndServe(":4242", router)` for graceful restarts without dropping connections.
- **`github.com/facebookgo/grace`** – Supports zero-downtime restarts by passing the listener to a new process.
- **`github.com/tylerb/graceful`** – Offers a `graceful` wrapper with configurable timeouts and shutdown hooks.

While these libraries simplify implementation, the native `http.Server.Shutdown` available since Go 1.8 is recommended for most use cases to minimize external dependencies.

## Summary

- **Bypass `engine.Run()`**: The built-in method blocks forever and prevents signal handling; instantiate your own `http.Server` instead.
- **Use `http.Server.Shutdown`**: Available in Go 1.8+, this standard library method safely drains active connections within a bounded timeout.
- **Start in a Goroutine**: Run `ListenAndServe()` concurrently so the main thread can monitor for `SIGINT` or `SIGTERM`.
- **Context Timeout Matters**: Provide a `context.Context` with a deadline to prevent the shutdown process from hanging indefinitely on slow handlers.
- **Reference Implementation**: The complete pattern is documented in [[`docs/doc.md`](https://github.com/gin-gonic/gin/blob/main/docs/doc.md)](https://github.com/gin-gonic/gin/blob/master/docs/doc.md) and demonstrated in the [`gin-examples`](https://github.com/gin-gonic/gin-examples) repository under the `graceful-shutdown` directory.

## Frequently Asked Questions

### What is graceful shutdown in a web server?

Graceful shutdown is the process of terminating a server process without abruptly closing active HTTP connections. When a termination signal is received, the server stops accepting new requests, allows in-flight requests to complete within a defined timeout period, and then closes the underlying listener and connections. This prevents data loss and ensures clients receive complete responses rather than connection reset errors.

### Why can't I use `router.Run()` for graceful shutdown?

The `Run()` method in [[`gin.go`](https://github.com/gin-gonic/gin/blob/main/gin.go)](https://github.com/gin-gonic/gin/blob/master/gin.go#L37-L56) encapsulates server creation and immediately invokes `http.Server.ListenAndServe()`, which blocks the calling goroutine indefinitely. Because it does not return control to the caller, you cannot simultaneously listen for OS signals or invoke `Shutdown()`. You must bypass this convenience method and manage the `http.Server` lifecycle manually to implement graceful shutdown for a Gin server.

### How long should the shutdown timeout be?

The appropriate timeout depends on your application's request latency characteristics. Common production values range from **5 to 30 seconds**. Set the timeout too short, and active requests may be terminated prematurely; set it too long, and the orchestration layer (like Kubernetes or Docker) may force-kill the container before shutdown completes. The timeout is passed via `context.WithTimeout()` and enforced by `server.Shutdown(ctx)`.

### Does Gin support zero-downtime restarts?

Gin itself does not implement zero-downtime (hot) restart functionality, but it is compatible with libraries that provide this capability. The documentation mentions `github.com/fvbock/endless` and `github.com/facebookgo/grace` as solutions that manage file descriptor passing to new processes, allowing you to upgrade your Gin application without dropping existing TCP connections. For simple graceful shutdown without restart functionality, the standard library's `http.Server.Shutdown` is sufficient.