Zero-dependency Go library for production-ready startup banners with build metadata, runtime information, and structured health checks.
goStartyUpy gives every Go service a Spring Boot–style startup banner — instantly showing build metadata, runtime info, and dependency health in one glance.
🎨 6 Banner Styles — Spring, Classic, Box, Mini, Block, or your own ASCII art
🔧 Build Metadata — Version, commit, branch, build time — injected via -ldflags
🖥️ Runtime Info — Go version, OS/Arch, PID — captured automatically
✅ Health Checks — SQL, TCP, HTTP, Redis — parallel or sequential, with timeout
📦 Zero Dependencies — Pure stdlib. Adds nothing to your go.sum
🛡️ Panic-Safe & Deterministic — Stable output, no side effects, all errors caught
| Feature | Description |
|---|---|
| 6 Banner Styles | spring (default), classic, box, mini, block, or custom ASCII art via the Banner field |
| Build Metadata | Version, BuildTime, Commit, Branch, Dirty — injected at compile time via -ldflags |
| Runtime Info | Go version, OS/Arch, PID — automatically captured at runtime |
| 4 Built-in Checks | SQLPingCheck, TCPDialCheck, HTTPGetCheck, RedisPingCheck — all without external dependencies |
| Custom Checks | checks.New(), checks.Bool(), checks.NewGroup() — or implement the Check interface |
| Parallel & Sequential | Runner supports both modes with configurable per-check timeout |
| Environment Detection | Automatic via the GO_STARTYUPY_ENV environment variable when not explicitly set |
| ANSI Colors | Optional via Color: true — plain text without escape sequences by default |
| ASCII-Only Mode | ASCIIOnly: true replaces Unicode box-drawing characters with plain ASCII (+, -, |) |
| Banner Width | BannerWidth truncates each line to a maximum width |
| Deterministic | Stable output order, no randomness, no side effects |
| Panic-Safe | All errors are caught and returned as structured Result objects |
go get github.com/keksclan/goStartyUpyPrerequisite: Go 1.24 or newer.
The module has no transitive dependencies. After go get, your go.sum will contain only the single entry for goStartyUpy itself.
import (
"github.com/keksclan/goStartyUpy/banner" // Banner rendering, Options, BuildInfo
"github.com/keksclan/goStartyUpy/checks" // Health checks, Runner, Check interface
"github.com/keksclan/goStartyUpy/configcheck" // Configuration validation for goConfy structs
"github.com/keksclan/goStartyUpy/version" // Module version (e.g., "0.2.0")
)banner— Main package. ContainsOptions,BuildInfo,Render(),RenderWithChecks(), all banner functions, and the build metadata variables.checks— Health check system. Contains theCheckinterface,Runner, all built-in checks, and helper constructors.configcheck— Configuration validation. Validates that all required fields in a goConfy config struct are populated.version— Exposes the module version as theModuleVersionconstant.
package main
import (
"fmt"
"github.com/keksclan/goStartyUpy/banner"
)
func main() {
opts := banner.Options{
ServiceName: "my-service",
}
info := banner.CurrentBuildInfo()
fmt.Print(banner.Render(opts, info))
}This produces a Spring Boot–style ASCII art wordmark with all detected build/runtime metadata.
package main
import (
"context"
"fmt"
"os"
"github.com/keksclan/goStartyUpy/banner"
"github.com/keksclan/goStartyUpy/checks"
)
func main() {
opts := banner.Options{
ServiceName: "order-service",
Environment: "production",
Extra: map[string]string{
"HTTP": ":8080",
"gRPC": ":9090",
},
}
info := banner.CurrentBuildInfo()
runner := checks.DefaultRunner() // 2s timeout, parallel
results := runner.Run(context.Background(),
checks.New("env-DATABASE_URL", func(ctx context.Context) error {
if os.Getenv("DATABASE_URL") == "" {
return fmt.Errorf("DATABASE_URL is not set")
}
return nil
}),
checks.TCPDialCheck{Address: "localhost:5432", Label: "postgres-tcp"},
checks.HTTPGetCheck{URL: "http://localhost:8080/healthz", Label: "self-http"},
)
fmt.Print(banner.RenderWithChecks(opts, info, results))
}make build PKG=./cmd/myservice BIN=bin/myserviceOr directly:
go build -ldflags "$(./scripts/ldflags.sh)" ./cmd/myservice/The banner package provides five link-time variables that are injected at compile time via -ldflags. These values automatically appear in the rendered banner.
| Variable | Type | Description | Default |
|---|---|---|---|
banner.Version |
string |
Semantic version or git describe output (e.g., v1.2.3, v1.2.3-4-gabcdef1) |
"dev" |
banner.BuildTime |
string |
UTC build timestamp in RFC 3339 format (e.g., 2026-03-05T18:00:00+01:00) |
"unknown" |
banner.Commit |
string |
Short Git commit hash (e.g., abcdef1) |
"unknown" |
banner.Branch |
string |
Git branch used for the build (e.g., master, feature/foo) |
"unknown" |
banner.Dirty |
string |
"true" if the working tree had uncommitted changes at build time, otherwise "false" |
"false" |
How does this work?
Go's linker allows overriding variable values at compile time. The -X flags set the package variables directly in the compiled binary without modifying source code. CurrentBuildInfo() then reads these values at runtime.
The included Makefile collects Git metadata automatically via scripts/ldflags.sh:
make build-example # Compiles the example binary with all metadata
make run-example # Compiles and runs the example
make test # Runs all unit tests (go test ./...)
make lint # go vet + gofmt check on all packages
make clean # Removes build artifacts (bin/)For your own service:
make build PKG=./cmd/myservice BIN=bin/myserviceIf you prefer not to use the Makefile, you can set the ldflags directly:
VERSION=$(git describe --tags --always --dirty)
COMMIT=$(git rev-parse --short HEAD)
BRANCH=$(git rev-parse --abbrev-ref HEAD)
BUILD_TIME=$(date -Iseconds)
DIRTY=$(git diff --quiet && echo "false" || echo "true")
go build -ldflags "\
-X 'github.com/keksclan/goStartyUpy/banner.Version=${VERSION}' \
-X 'github.com/keksclan/goStartyUpy/banner.BuildTime=${BUILD_TIME}' \
-X 'github.com/keksclan/goStartyUpy/banner.Commit=${COMMIT}' \
-X 'github.com/keksclan/goStartyUpy/banner.Branch=${BRANCH}' \
-X 'github.com/keksclan/goStartyUpy/banner.Dirty=${DIRTY}'" \
./cmd/myservice/The POSIX-sh-compatible script outputs the complete ldflags string — ideal for CI/CD pipelines or other build systems:
# Standard usage:
LDFLAGS="$(./scripts/ldflags.sh)" go build -ldflags "$LDFLAGS" ./cmd/myservice
# Override module path (if your import path differs):
MODULE=github.com/my/repo ./scripts/ldflags.shThe library supports 6 different banner styles, controlled via Options.BannerStyle. If Options.Banner is empty, the style determines the automatically generated banner. If Options.Banner is set, it is used directly (raw mode) and BannerStyle is ignored.
Style Overview:
| Style | BannerStyle Value |
Height | Font Technique | Direct Function |
|---|---|---|---|---|
| Spring (Default) | "spring" or "" |
5 lines | Underscores / Pipes / Slashes | SpringLikeBanner(name, asciiOnly) |
| Classic | "classic" |
5 lines | Slashes / Backslashes / Underscores | ClassicLikeBanner(name, asciiOnly) |
| Box | "box" |
3 lines | Unicode box-drawing characters (or ASCII) | BoxBanner(name, asciiOnly) |
| Mini | "mini" |
3 lines | Compact ASCII glyphs | MiniBanner(name, asciiOnly) |
| Block | "block" |
5 lines | Thick # characters |
BlockBanner(name, asciiOnly) |
| Custom (Raw) | — | any | Custom ASCII art | — |
Character Support (All Built-in Fonts):
Each built-in font supports the same characters: A–Z, 0–9, -, _, and space. The ServiceName is automatically converted to uppercase. Unsupported characters are replaced by a ? fallback glyph. Spaces are normalized to -.
BannerStyle: "spring" (or empty, since "spring" is the default) produces a large ASCII art wordmark inspired by the Spring Boot startup banner. The font uses underscores (_), pipes (|), and slashes (/, \).
Below the wordmark is the tagline :: goStartyUpy :: with an optional environment suffix (only when detected via GO_STARTYUPY_ENV, see Environment Detection).
opts := banner.Options{
ServiceName: "my-svc",
// BannerStyle defaults to "spring"
}Example Output:
__ __ __ __ ____ __ __ ____
| \/ | \ \ / / / ___| \ \ / / / ___|
| |\/| | \ V / _____ \___ \ \ \ / / | |
| | | | | | |_____| ___) | \ V / | |___
|_| |_| |_| |____/ \_/ \____|
:: goStartyUpy ::
Direct call (without Options/Render()):
art := banner.SpringLikeBanner("my-svc", false)
fmt.Println(art)BannerStyle: "classic" produces a banner with a slash/backslash/underscore font style reminiscent of traditional Java framework startup banners. Below the wordmark, two configurable taglines are printed:
| Tagline | Default Value | Example |
|---|---|---|
Tagline1 |
"<ServiceName> <Version>" |
"my-service v1.2.3" |
Tagline2 |
"Build: <BuildTime> Commit: <Commit> [Branch] [Dirty]" |
"Build: 2026-03-05 Commit: abcdef1 Branch: master" |
Both taglines can be overridden via Options.Tagline1 and Options.Tagline2:
opts := banner.Options{
ServiceName: "my-svc",
BannerStyle: "classic",
Tagline1: "My Service v2.0.0",
Tagline2: "Powered by goStartyUpy",
}ShowDetails Option: Controls whether the key/value info block (Service, Version, Go version, etc.) is displayed in classic mode. This is a *bool pointer. By default, details are shown (nil = true). Set explicitly to false to hide them:
hide := false
opts := banner.Options{
ServiceName: "my-svc",
BannerStyle: "classic",
ShowDetails: &hide, // Details block will not be printed
}Direct call:
art := banner.ClassicLikeBanner("my-svc", false)
fmt.Println(art)BannerStyle: "box" produces the classic box banner using Unicode box-drawing characters (┌, ─, ┐, │, └, ┘). The service name is centered inside the box.
opts := banner.Options{
ServiceName: "my-service",
BannerStyle: "box",
}Output:
┌───────────────────────────┐
│ MY-SERVICE │
└───────────────────────────┘
ASCII-Only Mode: Set Options.ASCIIOnly = true to replace Unicode box-drawing characters with plain ASCII (+, -, |):
+---------------------------+
| MY-SERVICE |
+---------------------------+
Direct call:
art := banner.BoxBanner("my-service", true) // true = ASCII-only
fmt.Println(art)BannerStyle: "mini" produces a compact 3-line ASCII art wordmark. Ideal for narrow terminals or logs with limited vertical space.
opts := banner.Options{
ServiceName: "go",
BannerStyle: "mini",
}Example Output ("GO"):
__ _
| _ | |
|__||_|
Direct call:
art := banner.MiniBanner("my-svc", false)
fmt.Println(art)BannerStyle: "block" produces a thick 5-line ASCII art wordmark where each letter is built from # characters. Highly visible even in noisy log output.
opts := banner.Options{
ServiceName: "go",
BannerStyle: "block",
}Example Output ("GO"):
#### ###
# # #
# ## # #
# # # #
#### ###
Direct call:
art := banner.BlockBanner("my-svc", false)
fmt.Println(art)To use your own ASCII art, simply set Options.Banner. The value is used directly without any processing. BannerStyle is ignored in this case.
opts := banner.Options{
ServiceName: "my-service",
Banner: `
╔═══════════════════════════════════╗
║ ★ MY AWESOME SERVICE ★ ║
╚═══════════════════════════════════╝`,
}Tip: You can use tools like patorjk.com/software/taag to generate custom ASCII art fonts and insert them as the Banner string.
Set Options.BannerWidth to a positive integer to hard-truncate each banner line to that maximum width. A value of 0 (default) means no restriction.
opts := banner.Options{
ServiceName: "my-very-long-service-name",
BannerWidth: 60, // Each line is truncated after 60 characters
}This is useful when the generated banner is too wide for your terminal or logging system.
goStartyUpy supports automatic environment detection via the GO_STARTYUPY_ENV environment variable. The behavior is as follows:
| Scenario | Options.Environment |
GO_STARTYUPY_ENV |
Result in Banner |
|---|---|---|---|
| Explicitly set | "production" |
any | No suffix displayed |
| Detected from env var | "" (empty) |
"staging" |
Suffix (staging) is displayed |
| Nothing set | "" (empty) |
not set / empty | No suffix displayed |
Rule: The environment suffix (e.g., (staging), (dev)) appears in the banner header only when the value originates from the GO_STARTYUPY_ENV environment variable. If Options.Environment is explicitly set in code, no suffix is displayed — the value is used internally but not shown in the banner.
Why this design?
- Explicitly set values in code are known to the developer — no visual hint needed.
- Values from environment variables may be unexpected (e.g., incorrect configuration in a CI/CD pipeline) — a visual hint in the banner helps with debugging.
Example with environment variable:
export GO_STARTYUPY_ENV=staging
go run ./cmd/myservice/The banner then shows:
:: goStartyUpy :: (staging)
Example without environment variable (explicit):
opts := banner.Options{
ServiceName: "my-service",
Environment: "production", // Explicit — no suffix in the banner
}The banner.Options struct controls all aspects of banner rendering. Each field is documented with its type, default value, and description:
| Field | Type | Default | Description |
|---|---|---|---|
ServiceName |
string |
"" |
Name of the service. Displayed in the banner and the info section. If empty, "SERVICE" is used as a fallback. |
Environment |
string |
"" |
Runtime environment (e.g., "production", "staging"). If empty, GO_STARTYUPY_ENV is checked. Appears in the info section. |
Banner |
string |
"" |
Custom ASCII art. If set, automatic banner generation is skipped and this text is used directly. |
BannerStyle |
string |
"spring" |
Controls the automatically generated banner style: "spring", "classic", "box", "mini", "block". Ignored when Banner is set. |
BannerWidth |
int |
0 |
Maximum width per banner line. 0 = no restriction. Positive values hard-truncate each line. |
Separator |
string |
"═" (Unicode) |
Character for the separator line between the banner and info section. In ASCII-only mode, "=" is used. |
ASCIIOnly |
bool |
false |
When true, all Unicode characters (box-drawing, separator) are replaced with plain ASCII. |
Color |
bool |
false |
When true, the output is colored with ANSI escape sequences. Plain text by default. |
Extra |
map[string]string |
nil |
Additional key/value pairs displayed in the info section (e.g., "HTTP": ":8080"). |
Tagline1 |
string |
"" |
Overrides the first tagline in classic style. If empty, the default is generated. |
Tagline2 |
string |
"" |
Overrides the second tagline in classic style. If empty, the default is generated. |
ShowDetails |
*bool |
nil |
Controls display of the details block in classic style. nil = show, &false = hide. |
Internal Fields (unexported; not part of the public API):
| Field | Type | Description |
|---|---|---|
environmentFromEnv |
bool |
Set internally to true when the environment originates from GO_STARTYUPY_ENV. Controls the suffix display. |
The checks package provides a complete startup check system for verifying dependencies (databases, caches, HTTP services) before accepting traffic.
Every startup check implements the Check interface:
type Check interface {
Name() string // Human-readable name of the check
Run(ctx context.Context) Result // Executes the check, returns Result
}The Result struct contains the outcome:
type Result struct {
Name string // Name of the check
OK bool // true = passed, false = failed
Duration time.Duration // Execution duration
Error string // Error message (empty on success)
}Important: Checks never panic. All panics within check functions are automatically caught and returned as a Result with OK: false and a corresponding error message.
The Runner executes checks with a configurable timeout. It supports both parallel and sequential execution:
runner := checks.Runner{
TimeoutPerCheck: 2 * time.Second, // Timeout per individual check
Parallel: true, // true = parallel, false = sequential
}
results := runner.Run(ctx, check1, check2, check3)| Field | Type | Default | Description |
|---|---|---|---|
TimeoutPerCheck |
time.Duration |
0 |
Timeout per check. 0 = no additional timeout (only the provided context). |
Parallel |
bool |
false |
true = all checks run concurrently in their own goroutines. false = sequential execution in input order. |
DefaultRunner() returns a preconfigured runner with a 2-second timeout and parallel execution:
runner := checks.DefaultRunner()
// Equivalent to:
// runner := checks.Runner{TimeoutPerCheck: 2 * time.Second, Parallel: true}Result Order: Regardless of the execution mode (parallel or sequential), the results are always returned in the same order as the input checks. This makes the output deterministic and testable.
Pings an *sql.DB connection via PingContext. Useful for PostgreSQL, MySQL, SQLite, and any other database/sql-compatible driver.
check := checks.SQLPingCheck{
DB: db, // *sql.DB handle (must not be nil)
NameLabel: "postgres", // Human-readable name
}| Field | Type | Description |
|---|---|---|
DB |
*sql.DB |
The database handle. If nil, the check fails with "sql.DB is nil". |
NameLabel |
string |
Name of the check in the output. |
Checks whether a TCP endpoint is reachable by establishing a connection and immediately closing it. Ideal for databases, caches, or other TCP-based services.
check := checks.TCPDialCheck{
Address: "localhost:5432", // host:port format
Label: "postgres-tcp", // Human-readable name
}| Field | Type | Description |
|---|---|---|
Address |
string |
TCP address in host:port format (e.g., "localhost:5432", "redis:6379"). |
Label |
string |
Name of the check in the output. |
Performs an HTTP GET request and checks whether the status code falls within an expected range. Useful for health endpoints of other services.
check := checks.HTTPGetCheck{
URL: "http://localhost:8080/healthz",
Label: "api-health",
ExpectedStatusMin: 200, // Optional, default: 200
ExpectedStatusMax: 299, // Optional, default: 399
}| Field | Type | Default | Description |
|---|---|---|---|
URL |
string |
— | Full URL to probe (e.g., "http://localhost:8080/healthz"). |
Label |
string |
— | Name of the check in the output. |
ExpectedStatusMin |
int |
200 |
Lower bound (inclusive) of the acceptable status code range. |
ExpectedStatusMax |
int |
399 |
Upper bound (inclusive) of the acceptable status code range. |
Note: The HTTP client does not use its own timeout — it relies on the runner's context deadline so that behavior is consistent across all check types.
Sends a RESP-encoded PING command over a raw TCP connection and expects +PONG as the response. No Redis client or external dependency required — works with any Redis-compatible server.
check := checks.RedisPingCheck{
Address: "localhost:6379", // host:port of the Redis server
Label: "redis-ping", // Human-readable name
}| Field | Type | Description |
|---|---|---|
Address |
string |
TCP address of the Redis server in host:port format. |
Label |
string |
Name of the check in the output. |
Technical Detail: The check sends the RESP array *1\r\n$4\r\nPING\r\n and expects +PONG\r\n. On unexpected responses, the check fails with the actual reply in the error text.
The simplest way to create a custom check. Pass a label string and a function that returns error (nil = passed):
envCheck := checks.New("env-DATABASE_URL", func(ctx context.Context) error {
if os.Getenv("DATABASE_URL") == "" {
return fmt.Errorf("DATABASE_URL is not set")
}
return nil
})If label is empty, "custom" is used as a fallback. If fn is nil, the check always fails with "nil check function".
For checks that return a boolean result plus an optional error:
featureFlag := checks.Bool("feature-flag", func(ctx context.Context) (bool, error) {
return os.Getenv("ENABLE_NEW_UI") == "true", nil
})The check passes only when ok == true and err == nil. If ok == false and err == nil, the error "check returned false" is produced.
Combines multiple checks into a single one. The group passes only when all children pass:
deps := checks.NewGroup("dependencies", checks.GroupOptions{
Parallel: true, // Run children in parallel
TimeoutPerCheck: 3 * time.Second, // Timeout per child check
},
checks.SQLPingCheck{DB: db, NameLabel: "postgres"},
checks.TCPDialCheck{Address: "localhost:6379", Label: "redis-tcp"},
checks.RedisPingCheck{Address: "localhost:6379", Label: "redis-ping"},
)GroupOptions Field |
Type | Default | Description |
|---|---|---|---|
Parallel |
bool |
false |
true = run child checks in parallel. |
TimeoutPerCheck |
time.Duration |
0 |
Timeout per child check. 0 = no additional timeout. |
On failures, the error string contains a compact summary: "2 failing: postgres: connection refused; redis-tcp: dial timeout".
For more complex scenarios, you can implement the Check interface directly:
type MyCustomCheck struct {
// custom fields
}
func (c MyCustomCheck) Name() string { return "my-custom" }
func (c MyCustomCheck) Run(ctx context.Context) checks.Result {
start := time.Now()
// ... your logic ...
return checks.Result{
Name: c.Name(),
OK: true,
Duration: time.Since(start),
}
}| Mode | Runner.Parallel |
Behavior |
|---|---|---|
| Parallel | true |
Each check runs in its own goroutine. The runner waits until all are finished. Fastest overall throughput. |
| Sequential | false |
Checks run one after another in input order. A slow check blocks subsequent ones. |
In both modes, the results are returned in the same order as the input.
The configcheck package provides startup-time configuration validation for structs loaded by goConfy. It inspects the config struct via reflection and reports any required fields that are missing or empty — catching configuration mistakes before the service starts serving traffic.
Missing or empty configuration values often cause cryptic runtime errors (nil pointer dereferences, empty connection strings, silent fallbacks). Running a validation step at startup ensures that all required values are present before any component is initialized.
Validation is optional and off by default. Enable it by passing configcheck.Options{Enabled: true} before printing the startup banner:
package main
import (
"fmt"
"log"
goconfy "github.com/keksclan/goConfy"
"github.com/keksclan/goStartyUpy/banner"
"github.com/keksclan/goStartyUpy/configcheck"
)
type AppConfig struct {
Database struct {
Host string `yaml:"host"`
Port int `yaml:"port"`
Password string `yaml:"password"`
} `yaml:"database"`
Redis struct {
Address string `yaml:"address"`
} `yaml:"redis"`
LogLevel string `yaml:"log_level" required:"false"`
}
func main() {
// 1. Load config via goConfy
cfg, err := goconfy.Load[AppConfig](goconfy.WithFile("config.yml"))
if err != nil {
log.Fatalf("failed to load config: %v", err)
}
// 2. Run config validation
msg, err := configcheck.RunStartupCheck(configcheck.Options{
Enabled: true,
Config: cfg,
})
if err != nil {
fmt.Println(msg)
log.Fatalf("Aborting startup due to configuration errors: %v", err)
}
// 3. Print startup banner
fmt.Print(banner.Render(banner.Options{ServiceName: "my-service"}, banner.CurrentBuildInfo()))
// 4. Continue service initialization...
}The recommended startup sequence when using config validation:
- Load config via goConfy (
goconfy.Load[T]) - Run config validation (
configcheck.RunStartupCheck) - Print startup banner (
banner.Render/banner.RenderWithChecks) - Continue service initialization
By default, all exported struct fields are required. Mark a field as optional with the required:"false" struct tag:
type Config struct {
Host string `yaml:"host"` // required (default)
Port int `yaml:"port"` // required
LogLevel string `yaml:"log_level" required:"false"` // optional
}The validator uses the yaml struct tag to determine the YAML key name. If no yaml tag is present, the Go field name is used. Fields tagged yaml:"-" are skipped entirely.
| Check | Description |
|---|---|
| Zero-value scalars | Empty strings, zero ints/floats, false bools (when required) |
| Nil pointers | Pointer fields that are nil |
| Empty slices/maps | Nil or zero-length slices and maps |
| Nested structs | Recursively validated with dot-separated paths |
| Leaf structs | Types implementing encoding.TextUnmarshaler (e.g., time.Time) are checked as single values |
When validation fails, the output is a clear diagnostic block:
Config validation failed:
Missing configuration keys:
- database.port
- database.password
- redis.address
This helps developers quickly identify and fix configuration problems.
For a one-liner that logs and exits on failure:
configcheck.MustPassStartupCheck(configcheck.Options{
Enabled: true,
Config: cfg,
}, log.Fatalf)A complete example YAML file containing every supported configuration option is available at:
examples/config-validation/config.full.example.yml
Use it as a starting point for your own configuration. The file includes all banner, config-check, and application-level settings with explanatory comments.
Additional example files in the same directory:
| File | Purpose |
|---|---|
config.full.example.yml |
All supported options with comments |
config.minimal.yml |
Smallest valid configuration |
config_invalid.yml |
Intentionally broken config for testing |
Run the example tests to verify all configs:
cd examples/config-validation && go test ./... -v- Enable validation in all environments — it adds negligible overhead and catches drift between config files.
- Use
required:"false"sparingly — only for truly optional fields with sensible zero-value behavior. - Run validation before the banner — so failures are visible immediately, not buried after startup output.
- Use goConfy's strict mode (default) together with configcheck — goConfy rejects unknown YAML keys, configcheck catches missing ones.
- Keep example YAML files in sync — the full example config acts as living documentation; update it when adding new config options.
goStartyUpy strictly distinguishes between two different version values that are independent of each other:
| Value | Package | Purpose | Set by |
|---|---|---|---|
version.ModuleVersion |
version |
Release version of the library itself (e.g., "0.2.0") |
In the source code (version/version.go) |
banner.Version |
banner |
Build version of the service binary (e.g., "v1.2.3") |
-ldflags at compile time |
Why two versions?
ModuleVersiontells you which version of goStartyUpy you are using as a dependency.banner.Versiontells you which version of your own service is currently running.
Both are independent values. Your service can use goStartyUpy@v0.2.0 and still be tagged as v3.7.2.
import "github.com/keksclan/goStartyUpy/version"
fmt.Println("goStartyUpy Library:", version.ModuleVersion) // "0.2.0"CurrentBuildInfo() creates a BuildInfo struct from the package-level link-time variables. This struct is passed to Render() and RenderWithChecks():
info := banner.CurrentBuildInfo()These are the actual fields of the BuildInfo struct, set via -ldflags at build time:
| Field | Type | Source | Description |
|---|---|---|---|
Version |
string |
-ldflags |
Build version of the service (default: "dev") |
BuildTime |
string |
-ldflags |
UTC build timestamp (default: "unknown") |
Commit |
string |
-ldflags |
Short Git commit hash (default: "unknown") |
Branch |
string |
-ldflags |
Git branch (default: "unknown") |
Dirty |
string |
-ldflags |
"true" / "false" for uncommitted changes |
The following values are not fields on BuildInfo. They are collected internally by the renderer at render time from the runtime package and os.Getpid(), and appear in the banner output alongside the struct fields:
| Value | Type | Source | Description |
|---|---|---|---|
GoVersion |
string |
runtime.Version() |
Go version (e.g., "go1.26") |
OS |
string |
runtime.GOOS |
Operating system (e.g., "linux", "darwin") |
Arch |
string |
runtime.GOARCH |
CPU architecture (e.g., "amd64", "arm64") |
PID |
int |
os.Getpid() |
Process ID of the running binary |
The banner package provides two main render functions:
Produces the complete startup banner without checks. Returns the entire banner as a string (ready for fmt.Print):
output := banner.Render(opts, info)
fmt.Print(output)Produces the complete startup banner with check results. The check results are appended as a list at the end of the banner:
output := banner.RenderWithChecks(opts, info, results)
fmt.Print(output)Check Output Format:
Checks:
[OK] postgres (12ms)
[OK] redis-tcp (3ms)
[FAIL] kafka: connection refused (1.2s)
Startup Complete
[OK]= Check passed (green whenColor: true)[FAIL]= Check failed (red whenColor: true), with error message
The following elements are considered public API and are subject to versioning guarantees:
- All exported types, functions, variables, and constants in the
banner,checks, andversionpackages. - The
Checkinterface and its contract. - The fields of the
Options,BuildInfo,Result,Runner, andGroupOptionsstructs. - The fields of the built-in check structs (
SQLPingCheck,TCPDialCheck,HTTPGetCheck,RedisPingCheck).
Not part of the public API (may change without notice):
- All unexported (lowercase) identifiers.
- The
example/directory. - The
scripts/directory. - Internal font data and render helper functions.
This project follows Semantic Versioning 2.0.0:
| Version Part | When? | Example |
|---|---|---|
MAJOR (X.0.0) |
Incompatible API changes (removing/renaming exported symbols, changing function signatures, breaking changes to the Check interface) |
1.0.0 → 2.0.0 |
MINOR (0.X.0) |
New features, backward-compatible (new check types, new Options fields, new helper functions) |
0.1.0 → 0.2.0 |
PATCH (0.0.X) |
Backward-compatible bug fixes and documentation corrections | 0.1.0 → 0.1.1 |
Note: As long as the module is at 0.x.y, the API may change between minor versions. A 1.0.0 release signals a stable API commitment.
- Update version: Set
ModuleVersioninversion/version.goto the new version. - Update CHANGELOG: Move entries from
[Unreleased]into a new version section with date. - Commit:
git add -A git commit -m "release: v0.2.0" - Tag and push:
git tag v0.2.0 git push origin master v0.2.0
- Consumers can pin the version:
go get github.com/keksclan/goStartyUpy@v0.2.0
The example/ directory contains runnable programs for various use cases:
| Example | Description | Run with |
|---|---|---|
example/ |
Full demo: Custom checks, groups, built-in checks | make run-example |
example/simple/ |
Minimal banner without checks | go run ./example/simple/ |
example/basic_start/ |
Simplest possible usage — banner with defaults | go run ./example/basic_start/ |
example/custom_banner/ |
Custom ASCII art as banner | go run ./example/custom_banner/ |
example/env_aware_start/ |
Automatic environment detection via GO_STARTYUPY_ENV |
go run ./example/env_aware_start/ |
example/ascii_only/ |
ASCII-only mode for terminals without Unicode | go run ./example/ascii_only/ |
example/checks_demo/ |
All built-in check types (SQL, TCP, HTTP, Redis) | go run ./example/checks_demo/ |
example/custom_checks/ |
Function-based, boolean, and grouped checks | go run ./example/custom_checks/ |
example/font_preview/ |
Prints the big-font ASCII wordmark for a service name | go run ./example/font_preview/ |
example/config_validation/ |
Configuration validation with configcheck | go run ./example/config_validation/ |
# Simplest start:
go run ./example/simple/
# Full demo with build metadata:
make run-exampleThe following example shows the complete output in box style with build metadata, extra fields, and check results:
┌──────────────────────────────┐
│ ORDER-SERVICE │
└──────────────────────────────┘
════════════════════════════════════════════════════════════
Service : order-service
Environment : staging
Version : v1.2.3
BuildTime : 2026-02-24T09:00:00Z
Commit : abcdef1
Branch : master
Dirty : false
Go : go1.26
OS/Arch : linux/amd64
PID : 12345
HTTP : :8080
Checks:
[OK] postgres (12ms)
[OK] redis-tcp (3ms)
[OK] self-http (8ms)
[OK] redis-ping (2ms)
Startup Complete
Output Structure:
- Banner — ASCII art or box (depending on style)
- Separator — Separator line (
═══...or===...in ASCII mode) - Info Section — Key/value pairs (Service, Environment, Version, BuildTime, Commit, Branch, Dirty, Go, OS/Arch, PID, plus all
Extraentries) - Checks (only with
RenderWithChecks) — Results with[OK]/[FAIL]status and duration - Footer —
"Startup Complete"or check summary
The banner prints exclusively safe, non-secret information (version, addresses, PID, etc.).
Options.Extra or other fields. The caller is responsible for what is printed.
# Run all unit tests:
go test ./...
# Via Makefile (identical):
make test
# Linting (go vet + gofmt):
make lintThe project includes tests for:
- Banner rendering of all 6 styles
- Font rendering and fallback glyphs
- Build metadata snapshot
- Formatting and separator
- Environment detection (explicit, from env var, not set)
- Check runner (parallel and sequential)
- All built-in check types
- FuncCheck, Bool check, Group check
- Panic recovery
- Unicode and edge-case safety
This project is licensed under the MIT License — see LICENSE for the full text.
Summary:
- Open-source usage: Free under the MIT License, no attribution required.
- Commercial/corporate usage: MIT-permissive, no attribution required.
Detailed documentation is available in the docs/ directory:
| Document | Description |
|---|---|
docs/architecture.md |
Module layout, design principles, package responsibilities, data flow |
docs/startup-flow.md |
Step-by-step rendering sequence, check execution phases |
docs/banner-system.md |
Banner styles, font system, name normalization, ASCII/color modes |
This project is used by:
- Keksclan — Creator of goStartyUpy
- Internal microservices — Production startup banners and health checks
- Community projects — Open-source Go services using goStartyUpy for boot diagnostics
➕ Add your project/organization here? Open a pull request and edit USED_BY.md. Rules:
- Sort alphabetically
- 1 line per entry, no marketing
- Format:
- [Name](URL) - Short description [tags]
golang · go-library · banner · startup-banner · cli · microservices · health-check · startup-checks · zero-dependencies · devops · ascii-art · build-metadata · spring-boot-style · production-ready · deterministic