A shared shutdown primitive for graceful application lifecycle management in Go
Squad helps you orchestrate goroutines, manage graceful shutdowns, and handle application lifecycle events with minimal boilerplate.
Features β’ Installation β’ Quick Start β’ Documentation β’ Examples
- About
- Features
- Installation
- Quick Start
- Usage
- API Reference
- Examples
- Project Structure
- Development
- Testing
- FAQ
- Contributing
- License
- Author
Squad is a lightweight Go package that provides a shared shutdown primitive for managing application lifecycle. It allows you to:
- Coordinate multiple goroutines that must start and stop together
- Handle graceful shutdowns with configurable timeouts and grace periods
- Manage HTTP servers with proper shutdown sequences
- Run consumer workers (e.g., message queues, event streams) with graceful stop
- Execute bootstrap and cleanup functions for subsystems
- Automatically handle OS signals (SIGINT, SIGTERM, SIGHUP, SIGQUIT)
Squad is particularly useful for:
- Microservices and API servers
- Background workers and job processors
- Event-driven applications with multiple consumers
- Any Go application requiring coordinated startup/shutdown
The package is designed to be Kubernetes-aware, with default grace periods aligned with Pod termination lifecycle.
- β‘ Zero dependencies - Uses only standard library and
github.com/moeryomenko/synx - π Coordinated lifecycle - If one goroutine exits, others are signaled to stop
- π‘οΈ Graceful shutdowns - Configurable grace periods and shutdown timeouts
- π HTTP server support - Built-in wrapper for
http.Serverwith proper shutdown - π¨ Consumer pattern - Graceful stop for message/event consumers without interrupting active handlers
- π― Signal handling - Automatic SIGINT/SIGTERM/SIGHUP/SIGQUIT handling
- π Bootstrap/cleanup hooks - Run initialization and cleanup functions
- β±οΈ Kubernetes-friendly - Default 30s grace period matches k8s pod termination
- π§ͺ Well tested - Comprehensive test coverage
- π¦ Simple API - Minimal boilerplate, intuitive usage
go get github.com/moeryomenko/squadRequirements:
- Go 1.25 or higher
package main
import (
"context"
"log"
"github.com/moeryomenko/squad"
)
func main() {
// Create a new squad with signal handler
s, err := squad.New(squad.WithSignalHandler())
if err != nil {
log.Fatal(err)
}
// Run your application logic
s.Run(func(ctx context.Context) error {
// Your code here
<-ctx.Done()
return nil
})
// Wait for shutdown
if err := s.Wait(); err != nil {
log.Printf("shutdown error: %v", err)
}
}Create a simple service with signal handling:
s, err := squad.New(squad.WithSignalHandler())
if err != nil {
log.Fatal(err)
}
s.Run(func(ctx context.Context) error {
// Your background work
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return nil
case <-ticker.C:
log.Println("tick")
}
}
})
s.Wait()Launch an HTTP server with graceful shutdown:
s, err := squad.New(squad.WithSignalHandler())
if err != nil {
log.Fatal(err)
}
http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
s.RunServer(&http.Server{Addr: ":8080"})
s.Wait()Run consumer workers that stop gracefully without interrupting active handlers:
s, err := squad.New(squad.WithSignalHandler())
if err != nil {
log.Fatal(err)
}
s.RunConsumer(func(consumeCtx, handleCtx context.Context) error {
// consumeCtx: cancelled when shutdown starts
// handleCtx: never cancelled, allows active handlers to complete
for {
select {
case <-consumeCtx.Done():
return nil
default:
msg := receiveMessage() // Your message source
go processMessage(handleCtx, msg)
}
}
})
s.Wait()Run tasks with cleanup functions:
s, err := squad.New(squad.WithSignalHandler(
squad.WithGracefulPeriod(30 * time.Second),
squad.WithShutdownTimeout(5 * time.Second),
))
if err != nil {
log.Fatal(err)
}
s.RunGracefully(
// Background function
func(ctx context.Context) error {
// Your work
<-ctx.Done()
return nil
},
// Cleanup function (runs on shutdown)
func(ctx context.Context) error {
log.Println("cleaning up...")
return closeResources(ctx)
},
)
s.Wait()Initialize and cleanup subsystems:
s, err := squad.New(
squad.WithSignalHandler(),
squad.WithBootstrap(
func(ctx context.Context) error {
return initDatabase(ctx)
},
func(ctx context.Context) error {
return connectToCache(ctx)
},
),
squad.WithCloses(
func(ctx context.Context) error {
return closeDatabase(ctx)
},
),
squad.WithSubsystem(
func(ctx context.Context) error { return openMessageQueue(ctx) },
func(ctx context.Context) error { return closeMessageQueue(ctx) },
),
)
if err != nil {
log.Fatal("initialization failed:", err)
}
// Your application code...
s.Wait()Squad- Main struct for coordinating goroutinesConsumerLoop- Function signature for consumer workers
New(opts ...Option) (*Squad, error)- Create a new Squad instance
WithSignalHandler(...ShutdownOpt)- Add OS signal handlingWithGracefulPeriod(duration)- Set graceful shutdown period (default: 30s)WithShutdownTimeout(duration)- Set shutdown timeout (default: 2s)WithShutdownInGracePeriod(duration)- Set both graceful and shutdown timeoutWithBootstrap(...func)- Add initialization functionsWithCloses(...func)- Add cleanup functionsWithSubsystem(init, close)- Add init+cleanup pair for a subsystem
Run(fn)- Run a function in the squadRunGracefully(backgroundFn, onDown)- Run with cleanup functionRunServer(*http.Server)- Run HTTP server with graceful shutdownRunConsumer(ConsumerLoop)- Run consumer workerWait() error- Block until all squad members exit
See the example/ directory for complete examples:
- simple.go - HTTP server with signal handling and graceful shutdown
Run the example:
go run example/simple.gosquad/
βββ squad.go # Core Squad implementation
βββ consumers.go # Consumer worker helpers
βββ options.go # Configuration options
βββ squad_test.go # Unit tests
βββ example/ # Example applications
β βββ simple.go
βββ tools/ # Development tools
βββ go.mod # Go module definition
βββ Makefile # Build automation
βββ LICENSE-APACHE # Apache 2.0 license
βββ LICENSE-MIT # MIT license
βββ README.md # This file
- Go 1.25+
- golangci-lint (for linting)
- go-mod-upgrade (for dependency management)
# Run tests
make test
# Run tests with race detector
RACE_DETECTOR=1 make test
# View coverage report
make cover
# Run linter
make lint
# Update dependencies
make mod
# View all commands
make helpSquad includes comprehensive unit tests covering:
- Basic lifecycle management
- Bootstrap initialization
- Background task failures
- Shutdown failures and timeouts
- Error propagation
- Concurrent operations
Run tests:
go test ./...
# With race detector
go test -race ./...
# With coverage
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.outSquad uses synx.CtxGroup which recovers from panics and converts them to errors. The error will be returned from Wait().
- Graceful period: Total time allowed for the entire shutdown process
- Shutdown timeout: Time reserved for executing cleanup functions
The signal handler waits gracefulPeriod - shutdownTimeout before canceling the squad context.
Yes! Simply don't use WithSignalHandler(). You'll need to manage context cancellation yourself.
RunConsumer provides two contexts:
consumeContext: Cancelled on shutdown (stop accepting new work)handleContext: Never cancelled (allow in-flight work to complete)
This enables graceful consumer shutdown without interrupting active message handlers.
Yes, Squad is designed to be used concurrently. Internal state is protected by mutexes.
30 seconds, matching Kubernetes pod termination grace period. This ensures compatibility with k8s deployments.
Contributions are welcome! Please feel free to submit issues, fork the repository, and send pull requests.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Add tests for new functionality
- Run tests and linter (
make test lint) - Commit your changes
- Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is dual-licensed under your choice of:
- MIT License - See LICENSE-MIT
- Apache License 2.0 - See LICENSE-APACHE
You may use this project under the terms of either license.
Maxim Eryomenko
- GitHub: @moeryomenko
- Email: moeryomenko@gmail.com
If you find Squad useful, please consider giving it a βοΈ!
Made with β€οΈ by Maxim Eryomenko