Vault Config Loader

A complete application that authenticates to HashiCorp Vault (auto-detecting Kubernetes vs AppRole environment), reads secrets from KV v2, and populates a typed config struct for the application to use.

Input: Vault address and auth credentials (AppRole role/secret IDs or Kubernetes service account). Output: A populated AppConfig struct with database, Redis, and API credentials ready for use.

package main

import (
	"context"
	"fmt"
	"os"

	vault "github.com/hashicorp/vault/api"
)

type AppConfig struct {
	DBHost     string
	DBUser     string
	DBPassword string
	DBName     string
	RedisURL   string
	APIKey     string
}

func loginAppRole(client *vault.Client, roleID, secretID string) error {
	data := map[string]interface{}{
		"role_id":   roleID,
		"secret_id": secretID,
	}

	resp, err := client.Logical().Write("auth/approle/login", data)
	if err != nil {
		return fmt.Errorf("approle login: %w", err)
	}

	client.SetToken(resp.Auth.ClientToken)
	return nil
}

func loginKubernetes(client *vault.Client, role string) error {
	jwt, err := os.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/token")
	if err != nil {
		return fmt.Errorf("reading SA token: %w", err)
	}

	data := map[string]interface{}{
		"jwt":  string(jwt),
		"role": role,
	}

	resp, err := client.Logical().Write("auth/kubernetes/login", data)
	if err != nil {
		return fmt.Errorf("k8s auth: %w", err)
	}

	client.SetToken(resp.Auth.ClientToken)
	return nil
}

func loadConfig(ctx context.Context) (*AppConfig, error) {
	// Initialize client
	config := vault.DefaultConfig()
	config.Address = os.Getenv("VAULT_ADDR")

	client, err := vault.NewClient(config)
	if err != nil {
		return nil, err
	}

	// Authenticate with AppRole (CI/CD) or Kubernetes (pods)
	if os.Getenv("KUBERNETES_SERVICE_HOST") != "" {
		err = loginKubernetes(client, "myapp")
	} else {
		err = loginAppRole(client, os.Getenv("VAULT_ROLE_ID"), os.Getenv("VAULT_SECRET_ID"))
	}
	if err != nil {
		return nil, fmt.Errorf("vault auth: %w", err)
	}

	// Read static secrets
	dbSecrets, err := client.KVv2("secret").Get(ctx, "myapp/db")
	if err != nil {
		return nil, fmt.Errorf("read db secrets: %w", err)
	}

	redisSecrets, err := client.KVv2("secret").Get(ctx, "myapp/redis")
	if err != nil {
		return nil, fmt.Errorf("read redis secrets: %w", err)
	}

	// Build config struct
	cfg := &AppConfig{
		DBHost:     dbSecrets.Data["host"].(string),
		DBUser:     dbSecrets.Data["username"].(string),
		DBPassword: dbSecrets.Data["password"].(string),
		DBName:     dbSecrets.Data["dbname"].(string),
		RedisURL:   redisSecrets.Data["url"].(string),
		APIKey:     redisSecrets.Data["api_key"].(string),
	}

	return cfg, nil
}

func main() {
	ctx := context.Background()

	cfg, err := loadConfig(ctx)
	if err != nil {
		fmt.Fprintf(os.Stderr, "config load failed: %v\n", err)
		os.Exit(1)
	}

	fmt.Printf("Connected to %s@%s/%s\n", cfg.DBUser, cfg.DBHost, cfg.DBName)
	// Start application with cfg...
}

💻 Run locally

Copy the code above and run it on your machine

© 2026 ByteLearn.dev. Free courses for developers. · Privacy