Standardizes Go service initialization using the startup library for metrics, tracing, and configuration.

Install

mkdir -p .claude/skills/startup && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/19259" && unzip -o skill.zip -d .claude/skills/startup && rm skill.zip

Installs to .claude/skills/startup

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Use this skill for Go service bootstrapping with flachnetz/startup library. Covers options composition, auto-initialization, Postgres, Kafka, HTTP server, tracing, and environment patterns.
189 chars✓ has a “when” trigger
Advanced

Key capabilities

  • Parse CLI flags with validation via struct tags
  • Auto-initialize components via `Initialize()` methods
  • Provide built-in metrics for connection pools and HTTP
  • Offer an admin panel with health checks and Prometheus
  • Handle graceful shutdown

How it works

It provides a unified approach to Go service bootstrapping by parsing CLI flags, auto-initializing components, and offering built-in metrics. This simplifies the setup of services like Postgres and Kafka.

Inputs & outputs

You give it
Go service code requiring bootstrapping and component initialization
You get back
Bootstrapped Go service with configured CLI flags, initialized components, and observability features

When to use startup

  • Bootstrap a new Go service with Postgres support
  • Add HTTP server and tracing to a Go application
  • Define configuration options using struct tags
  • Implement graceful shutdown for a service

About this skill

Startup Library

Reference for building Go services with the flachnetz/startup library.

Overview

The startup library provides a unified approach to service bootstrapping:

  • CLI flag parsing with validation via struct tags
  • Auto-initialization of components via Initialize() methods
  • Built-in metrics for connection pools and HTTP
  • Admin panel with health checks, pprof, and Prometheus
  • Graceful shutdown handling
go get github.com/flachnetz/startup/v2

Core Pattern: Options Composition

Embed *Options structs to compose your service configuration:

package main

import (
    "github.com/flachnetz/startup/v2"
    "github.com/flachnetz/startup/v2/startup_base"
    "github.com/flachnetz/startup/v2/startup_http"
    "github.com/flachnetz/startup/v2/startup_postgres"
)

type Options struct {
    Base     startup_base.BaseOptions
    Postgres startup_postgres.PostgresOptions
    HTTP     startup_http.HTTPOptions
}

func main() {
    var opts Options

    // Parse CLI flags + auto-initialize all modules
    startup.MustParseCommandLine(&opts)

    // Modules are now ready to use
    db := opts.Postgres.Connection()
    defer db.Close()

    opts.HTTP.Serve(startup_http.Config{
        Name:    "my-service",
        Routing: setupRoutes,
    })
}

How It Works

  1. Each embedded struct defines CLI flags via struct tags
  2. MustParseCommandLine parses flags and validates with go-playground/validator
  3. For each struct with an Initialize() method, startup calls it automatically
  4. Initialize methods can depend on previously initialized modules

Generated CLI Flags

./my-service --help

# Base options
--verbose           Enable debug logging
--log-json          JSON log format
--environment       development|staging|production

# Postgres options
--postgres          Connection URL (default: postgres://postgres:postgres@localhost:5432)
--postgres-pool     Pool size (default: 8)
--postgres-lifetime Connection max lifetime (default: 10m)

# HTTP options
--http-address      Listen address (default: :3080)
--http-tls-cert     TLS certificate file
--http-tls-key      TLS private key file

PostgresOptions

Provides PostgreSQL connections with pgx/sqlx and automatic pool metrics.

Basic Usage

type Options struct {
    Postgres startup_postgres.PostgresOptions
}

func main() {
    var opts Options
    startup.MustParseCommandLine(&opts)

    // Get *sqlx.DB with connection pool
    db := opts.Postgres.Connection()
    defer db.Close()

    // Use sqlx patterns
    var users []User
    err := db.Select(&users, "SELECT * FROM users WHERE active = $1", true)
}

Connection Pool Settings

./service \
  --postgres="postgres://user:pass@host:5432/db?sslmode=require" \
  --postgres-pool=16 \
  --postgres-lifetime=15m

Pool metrics are automatically registered with go-metrics:

  • db.pool.idle - idle connections
  • db.pool.inuse - in-use connections
  • db.pool.open - total open connections

Query Logging

Enable query logging for debugging:

./service --enable-query-logging

Migrations with sql-migrate

Use the Initializer pattern to run migrations on startup:

type Options struct {
    Postgres startup_postgres.PostgresOptions
}

func main() {
    var opts Options

    // Set initializer before parsing
    opts.Postgres.Inputs.Initializer = startup_postgres.Migration(
        "schema_migrations",  // Migration table name
        "sql",                // Directory with .sql files
    )

    startup.MustParseCommandLine(&opts)

    db := opts.Postgres.Connection()
    // Migrations have already run
}

Or use DefaultMigration which searches for sql/ directory:

opts.Postgres.Inputs.Initializer = startup_postgres.DefaultMigration("schema_migrations")

Migration files use sql-migrate format:

-- +migrate Up
CREATE TABLE users (
    id BIGSERIAL PRIMARY KEY,
    email TEXT NOT NULL UNIQUE,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- +migrate Down
DROP TABLE users;

Error Helpers

import "github.com/flachnetz/startup/v2/startup_postgres"

_, err := db.Exec("INSERT INTO users (email) VALUES ($1)", email)
if startup_postgres.ErrIsUniqueViolation(err) {
    return errors.New("email already exists")
}
if startup_postgres.ErrIsForeignKeyViolation(err) {
    return errors.New("referenced record not found")
}

Schema Creation

If your connection URL includes search_path, the schema is auto-created:

--postgres="postgres://user:pass@host:5432/db?search_path=myapp"
# Creates: CREATE SCHEMA IF NOT EXISTS myapp

KafkaOptions

Provides Kafka consumer setup with confluent-kafka-go.

Basic Consumer

import (
    "github.com/confluentinc/confluent-kafka-go/v2/kafka"
    "github.com/flachnetz/startup/v2/startup_kafka"
)

type Options struct {
    Kafka startup_kafka.KafkaOptions
}

func main() {
    var opts Options
    startup.MustParseCommandLine(&opts)

    consumer := opts.Kafka.NewConsumer(kafka.ConfigMap{
        // Override defaults here
        "session.timeout.ms": 30000,
    })
    defer consumer.Close()

    consumer.Subscribe("my-topic", nil)

    for {
        msg, err := consumer.ReadMessage(-1)
        if err != nil {
            log.Printf("Consumer error: %v", err)
            continue
        }
        processMessage(msg)
        consumer.CommitMessage(msg)
    }
}

CLI Flags

./service \
  --kafka-address=kafka1:9092 \
  --kafka-address=kafka2:9092 \
  --kafka-consumer-group=my-service \
  --kafka-offset-reset=smallest \
  --kafka-security-protocol=ssl

Random Consumer Group

For testing or one-off consumers:

--kafka-consumer-group=RANDOM
# Generates: golang-<timestamp>

Custom Properties

Pass rdkafka properties directly:

--kafka-property="session.timeout.ms=30000"
--kafka-property="max.poll.interval.ms=300000"

HTTPOptions

Provides HTTP server with admin panel, Prometheus, and graceful shutdown.

Basic Server

import (
    "net/http"

    "github.com/flachnetz/startup/v2/startup_http"
    "github.com/julienschmidt/httprouter"
)

type Options struct {
    HTTP startup_http.HTTPOptions
}

func main() {
    var opts Options
    startup.MustParseCommandLine(&opts)

    opts.HTTP.Serve(startup_http.Config{
        Name: "my-service",
        Routing: func(router *httprouter.Router) http.Handler {
            router.GET("/api/users", listUsers)
            router.POST("/api/users", createUser)
            return router
        },
    })
}

func listUsers(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
    // Handler logic
}

Built-in Admin Panel

The admin panel is automatically available at /admin:

EndpointDescription
/adminAdmin dashboard
/admin/pingHealth check (no auth)
/admin/metricsPrometheus metrics
/admin/gcTrigger garbage collection
/admin/pprof/*Go pprof endpoints
/admin/heapHeap dump
/admin/log/levelGET/POST log level

Admin Authentication

./service \
  --http-admin-username=admin \
  --http-admin-password=secret

# Disable auth (development only)
./service --http-disable-admin-auth

TLS Support

./service \
  --http-tls-cert=/path/to/cert.pem \
  --http-tls-key=/path/to/key.pem

Access Logging

# Log to stdout (default)
./service

# Log to file
./service --http-access-log=/var/log/access.log

# Disable access log
./service --http-access-log=/dev/null

# Include admin routes in access log
./service --http-access-log-admin-route

Custom Admin Handlers

import "github.com/flachnetz/go-admin"

opts.HTTP.Serve(startup_http.Config{
    Name: "my-service",
    AdminHandlers: []admin.RouteConfig{
        admin.Describe("Custom status", admin.WithHandlerFunc(
            "", "status",
            func(w http.ResponseWriter, r *http.Request) {
                w.Write([]byte("OK"))
            },
        )),
    },
    Routing: setupRoutes,
})

Graceful Shutdown

The server handles SIGINT and SIGTERM automatically:

  1. Stop accepting new connections
  2. Wait for in-flight requests to complete
  3. Shutdown cleanly

Custom shutdown handler:

opts.HTTP.Serve(startup_http.Config{
    RegisterSignalHandlerForServer: func(server *http.Server) <-chan struct{} {
        // Custom shutdown logic
        return myShutdownChannel
    },
})

TracingOptions

Integrates Zipkin tracing via OpenTracing.

import "github.com/flachnetz/startup/v2/startup_tracing"

type Options struct {
    Tracing startup_tracing.TracingOptions
}

func main() {
    var opts Options
    opts.Tracing.Inputs.ServiceName = "my-service"

    startup.MustParseCommandLine(&opts)
    // Global OpenTracing tracer is now configured
}

Enable tracing:

./service --zipkin=http://zipkin:9411/

Environment Helpers

BaseOptions provides environment detection:

import "github.com/flachnetz/startup/v2/startup_base"

func main() {
    // Set via --environment flag or STAGE env var
    if startup_base.IsDevelopment() {
        // Enable dev-only features
    }

    if startup_base.IsProduction() {
        // Production-specific config
    }

    env := startup_base.GetEnvironment()
    // Returns: "development", "staging", "production", etc.
}

Environment detection:

  • IsDevelopment(): "development" or "dev"
  • IsTesting(): "testing" or "test"
  • IsStaging(): "staging" or "stage"
  • IsProduction(): "production", "prod", or "live"

Error Handling Patterns

PanicOnError

Use during initialization - panics halt startup on failure:

config, err := loadConfig()
startup_base.PanicOnError(err, "Failed to load config")

FatalOnError

Use for fatal errors that should log and exit:

db, err := connect()
startup_

---

*Content truncated.*

When not to use it

  • When building services in languages other than Go
  • When manual setup with raw libraries (e.g., pgx/sqlx/kafka) is preferred
  • When a different bootstrapping approach is desired

Prerequisites

github.com/flachnetz/startup/v2

Limitations

  • Specific to Go services using the `flachnetz/startup` library
  • Requires embedding `*Options` structs for configuration
  • Initializer methods can depend on previously initialized modules

How it compares

This skill offers a structured framework for Go service bootstrapping with integrated features like CLI parsing and auto-initialization, which is more complete than manual setup using individual libraries.

Compared to similar skills

startup side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
startup (this skill)03moReviewAdvanced
springboot-patterns114moNo flagsIntermediate
backend-development173moNo flagsIntermediate
backend-patterns01moNo flagsAdvanced

Try saying

Example prompts that trigger this skill in your AI assistant.

Search skills

Search the agent skills registry