Stop Rewriting Go Service Boilerplate: Ship Production Go APIs Faster with nurago

Learn how nurago helps you ship production Go APIs faster with reusable bootstrap, observability, and service lifecycle components.


tecnickcom/nurago pipeline

Every backend team says the same thing when starting a new service:

“This one will be lean.”

Then the infrastructure checklist arrives.

Configuration loading. Structured logging. Metrics. Retries. Health checks. Graceful shutdown. Validation. Cache layers. Cloud clients. Test utilities.

None of it is core business logic. All of it is essential in production.

After watching that pattern repeat across teams and projects, I consolidated the building blocks I kept rewriting into one open-source project: nurago.

What that buys, concretely:

Bootstrap a reliable Go service lifecycle without rewriting the same infrastructure code for every new service.


The problem: service lifecycle code is easy to get wrong

Backend production incidents rarely start in the core algorithms. They start in operational failures and missing guardrails:

  • Services that do not shut down gracefully
  • Missing logs or inconsistent structure that slow diagnosis
  • Missing or late metrics that delay detection
  • Retry and timeout behaviour implemented inconsistently
  • Utility packages that drift between repositories

In Go, this often turns into custom glue code in every new project.

It works, until the third or fourth service.


Why nurago exists

nurago is not a framework. It is a production-oriented collection of modular Go packages for common backend concerns, plus a working REST API example showing how to compose them.

What it optimises for:

  • Reuse stable, tested components
  • Stay aligned with open standards and common conventions
  • Keep APIs idiomatic and composable
  • Reduce dependency sprawl and custom one-off helpers
  • Ship faster with safer defaults

Project links:


What makes nurago different

Most libraries solve one narrow task. Three things make this collection useful in practice.

1. Breadth without framework lock-in

nurago provides dozens of packages across key backend categories:

  • Infrastructure and service lifecycle
  • Observability and operations
  • Data, storage, and caching
  • Security, privacy, and validation
  • Cloud integrations and messaging
  • Data processing, utilities, and testing

You can adopt one package at a time. There is no architecture to buy into, no base type to embed, and no main to hand over.

2. High standards and OSS discipline

Quality and security badges are publicly visible in the repository README.

tecnickcom/nurago quality badges

nurago is actively maintained with a quality-first approach:

  • Code Quality: A 100% unit test coverage policy enforced in continuous integration (CI), plus strict linting with golangci-lint
  • Security: CodeQL analysis and dependency review in CI to catch vulnerabilities early
  • Transparency: Public coverage, build status, and quality signals visible to all users
  • Process: Rigorous code review, contribution guidelines, and strong engineering hygiene
  • Recognition: Open Source Security Foundation (OpenSSF) Best Practices badge achieved

For teams adopting open-source software (OSS) in production, these signals matter as much as the features themselves.

3. A production-oriented example service

The examples/service project is a working service rather than a demo. It covers config, logging, metrics, docs, packaging, testing, and deployment-friendly workflows.

You can also scaffold a new service with:

make project CONFIG=project.cfg

cd target/github.com/test/dummy/

This scaffolds a ready-to-use project with runtime wiring, HTTP exposure, operational assets, and generated output clearly separated, so you can extend the example without mixing application code, deployment assets, and generated artefacts in the same place. For a detailed breakdown of the project structure and key files, see examples/service/README.md.

Top features

  • Three-server runtime layout across monitoring, private, and public HTTP endpoints, so operational traffic is isolated from internal and external APIs from day one.
  • Configuration-first bootstrap in internal/cli, where startup, logging, metrics, health checks, and graceful shutdown are wired in one place rather than scattered through main.
  • Operational assets shipped with the codebase in resources, keeping local development, integration tests, packaging, container builds, and service-manager integration reproducible.
  • Generated documentation and artefacts kept separate in doc and target, so docs and build outputs regenerate without polluting source packages.

Real challenges from the journey

Building reusable infrastructure packages is harder than writing project-local helpers.

Main trade-offs I had to handle:

  • Keeping APIs flexible without becoming generic to the point of ambiguity
  • Preserving backward compatibility while evolving internals
  • Balancing “do one thing well” package boundaries with discoverability
  • Supporting real-world operational needs without turning into a framework

These constraints shaped the package design philosophy: small, focused modules, explicit options, practical defaults.


Solve one real problem: reliable bootstrap and graceful shutdown

pkg/bootstrap is the package that gets underestimated most often. It centralises the standard service lifecycle:

  1. Create and propagate context.Context
  2. Initialise logger and metrics
  3. Bind application components
  4. Listen for OS signals (SIGTERM, SIGINT)
  5. Broadcast shutdown events
  6. Wait for dependents with timeout bounds

This removes repetitive startup/shutdown orchestration from each service built on it.

A basic integration example

package main

import (
	"context"
	"log"
	"log/slog"
	"sync"
	"time"

	"github.com/tecnickcom/nurago/pkg/bootstrap"
	"github.com/tecnickcom/nurago/pkg/logutil"
	"github.com/tecnickcom/nurago/pkg/metrics"
)

func bind(ctx context.Context, l *slog.Logger, m metrics.Client) error {
	// register HTTP handlers, DB clients, workers, consumers, etc.
	return nil
}

func main() {
	shutdownWG := &sync.WaitGroup{}
	shutdownCh := make(chan struct{})

	err := bootstrap.Bootstrap(
		bind,
		bootstrap.WithLogConfig(logutil.DefaultConfig()),
		bootstrap.WithShutdownTimeout(30*time.Second),
		bootstrap.WithShutdownWaitGroup(shutdownWG),
		bootstrap.WithShutdownSignalChan(shutdownCh),
	)
	if err != nil {
		log.Fatal(err)
	}
}

The gain is less about line count and more about predictable behaviour under pressure: controlled shutdown, consistent observability hooks, and fewer edge-case bugs in the paths that only run during a deploy or an incident.

For an advanced, runnable implementation, see:


Developer contribution quick start

If you want to contribute to nurago, start with this fast onboarding path:

  1. Clone the repository.
  2. Run the full local quality pipeline.
  3. Explore the service example.
  4. Scaffold your own project.
git clone https://github.com/tecnickcom/nurago.git
cd nurago
make x

If you prefer a Docker-based workflow:

make dbuild

After setup, pick one high-value contribution path:

  • Add focused docs and examples for under-explained packages
  • Propose performance benchmarks for hot-path utilities
  • Expand integration examples in examples/service

Review contribution guidelines in the repository before opening a PR.


Where to start

If your team keeps rebuilding the same Go service primitives, that repeated work is a tax on every new project.

nurago removes it with tested, modular packages and a reference service you can build from.

If this fits your stack:

For the long form, Building a Production REST API in Go is a twenty-part guide that builds a complete service from a bound socket to a deployment manifest, using the examples/service reference implementation as its starting point.

Contributions are welcome.