Docs Graceful Shutdown

Graceful Shutdown

In production systems, processes do not live forever.

They are stopped and restarted due to:

  • Deployments
  • Scaling events
  • Configuration changes
  • Node failures
  • Manual operations

A graceful shutdown ensures that ongoing requests are allowed to complete, while new requests are rejected, preventing partial responses and data corruption.

Plumego does not handle shutdown automatically.
This is intentional.

Graceful shutdown is a process-level concern, not a framework feature.


What Happens Without Graceful Shutdown

Without proper shutdown handling:

  • In-flight requests are terminated abruptly
  • Clients receive connection errors
  • Partial writes may occur
  • Background operations may be cut off
  • Logs become misleading
  • Data consistency may be compromised

These failures are intermittent and difficult to reproduce.


Design Principles

A correct graceful shutdown strategy must ensure:

  1. Stop accepting new requests
  2. Allow in-flight requests to finish
  3. Respect timeouts
  4. Release resources cleanly
  5. Exit the process predictably

Plumego aligns naturally with Go’s standard shutdown mechanisms.


Using net/http Shutdown Semantics

Plumego runs on top of Go’s net/http.Server.

Go provides built-in support for graceful shutdown via:

server.Shutdown(ctx)

This method:

  • Stops accepting new connections
  • Waits for active requests to complete
  • Honors context cancellation and deadlines

Plumego relies on this mechanism.


Step 1: Control the HTTP Server Explicitly

Instead of calling app.Run() directly, create and manage the server yourself.

Conceptual example:

app := plumego.New()

// register routes and middleware

server := &http.Server{
	Addr:    ":8080",
	Handler: app,
}

This gives you full control over the server lifecycle.


Step 2: Listen for Shutdown Signals

Most production environments send termination signals:

  • SIGINT (Ctrl+C)
  • SIGTERM (container shutdown)

Set up a signal listener:

ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()

This context will be canceled when a shutdown signal is received.


Step 3: Start the Server in a Goroutine

Run the HTTP server asynchronously:

go func() {
	if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
		log.Fatalf("server error: %v", err)
	}
}()

This allows the main goroutine to wait for shutdown signals.


Step 4: Initiate Graceful Shutdown

When the shutdown signal arrives:

<-ctx.Done()

shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

if err := server.Shutdown(shutdownCtx); err != nil {
	log.Printf("graceful shutdown failed: %v", err)
}

Key points:

  • Use a timeout to avoid hanging indefinitely
  • Log shutdown failures
  • Allow in-flight requests to complete within the timeout

Step 5: Clean Up Resources

After the server shuts down:

  • Close database connections
  • Flush logs
  • Stop background workers
  • Release external resources

This cleanup should respect the same shutdown context where possible.


Interaction with Middleware

Middleware should be written with shutdown in mind:

  • Long-running operations should respect ctx.Done()
  • Background goroutines should be cancellable
  • Blocking operations should have timeouts

Because Plumego embeds context.Context, cancellation propagates naturally.


Handling Long-Lived Connections

Certain features complicate graceful shutdown:

  • WebSocket connections
  • Streaming responses
  • Long polling

Strategies include:

  • Closing connections explicitly
  • Sending shutdown notifications to clients
  • Enforcing connection timeouts

These require explicit handling, not framework magic.


Graceful Shutdown in Containers

In containerized environments (Docker, Kubernetes):

  • Ensure SIGTERM is handled
  • Configure termination grace periods
  • Align shutdown timeout with platform settings

Mismatch between application and platform timeouts leads to forced termination.


Common Mistakes

Calling os.Exit Directly

This bypasses deferred cleanup and prevents graceful shutdown.

Avoid it.


Ignoring Context Cancellation

Long-running operations that ignore cancellation will delay or block shutdown.


Infinite Shutdown Timeouts

Always set an upper bound.

A stuck shutdown is worse than a forced one.


Testing Graceful Shutdown

You should test shutdown behavior by:

  • Sending requests
  • Triggering shutdown signals
  • Verifying responses complete
  • Ensuring no new requests are accepted

This catches issues that unit tests cannot.


Summary

In Plumego:

  • Graceful shutdown is explicit
  • Built on Go’s standard HTTP server
  • Integrated via context cancellation
  • Essential for production reliability

Graceful shutdown is not an optimization.
It is a correctness requirement.


Next

With graceful shutdown implemented, you are ready to handle external interactions safely.

Next guide:

→ Webhook Server

This explains how to receive and validate external callbacks reliably.