[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-4637":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":9,"language":10,"languages":9,"totalLinesOfCode":9,"stars":11,"forks":12,"watchers":13,"openIssues":14,"contributorsCount":15,"subscribersCount":15,"size":15,"stars1d":16,"stars7d":17,"stars30d":18,"stars90d":15,"forks30d":15,"starsTrendScore":19,"compositeScore":20,"rankGlobal":9,"rankLanguage":9,"license":21,"archived":22,"fork":22,"defaultBranch":23,"hasWiki":24,"hasPages":22,"topics":25,"createdAt":9,"pushedAt":9,"updatedAt":26,"readmeContent":27,"aiSummary":28,"trendingCount":15,"starSnapshotCount":15,"syncStatus":29,"lastSyncTime":30,"discoverSource":31},4637,"viper","spf13\u002Fviper","spf13","Go configuration with fangs",null,"Go",30297,2105,249,7,0,3,16,74,13,44.97,"MIT License",false,"master",true,[],"2026-06-12 02:01:02","> ## Viper v2 Feedback\n>\n> Viper is heading towards v2 and we would love to hear what _**you**_ would\n> like to see in it. Share your thoughts here:\n> https:\u002F\u002Fforms.gle\u002FR6faU74qPRPAzchZ9\n>\n> **Thank you!**\n\n![viper logo](https:\u002F\u002Fgithub.com\u002Fuser-attachments\u002Fassets\u002Facae9193-2974-41f3-808d-2d433f5ada5e)\n\n[![Mentioned in Awesome Go](https:\u002F\u002Fawesome.re\u002Fmentioned-badge-flat.svg)](https:\u002F\u002Fgithub.com\u002Favelino\u002Fawesome-go#configuration)\n[![run on repl.it](https:\u002F\u002Frepl.it\u002Fbadge\u002Fgithub\u002Fsagikazarmark\u002FViper-example)](https:\u002F\u002Frepl.it\u002F@sagikazarmark\u002FViper-example#main.go)\n\n[![GitHub Workflow Status](https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Factions\u002Fworkflow\u002Fstatus\u002Fspf13\u002Fviper\u002Fci.yaml?branch=master&style=flat-square)](https:\u002F\u002Fgithub.com\u002Fspf13\u002Fviper\u002Factions?query=workflow%3ACI)\n[![Join the chat at https:\u002F\u002Fgitter.im\u002Fspf13\u002Fviper](https:\u002F\u002Fbadges.gitter.im\u002FJoin%20Chat.svg)](https:\u002F\u002Fgitter.im\u002Fspf13\u002Fviper?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)\n[![Go Report Card](https:\u002F\u002Fgoreportcard.com\u002Fbadge\u002Fgithub.com\u002Fspf13\u002Fviper?style=flat-square)](https:\u002F\u002Fgoreportcard.com\u002Freport\u002Fgithub.com\u002Fspf13\u002Fviper)\n![Go Version](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fgo%20version-%3E=1.23-61CFDD.svg?style=flat-square)\n[![PkgGoDev](https:\u002F\u002Fpkg.go.dev\u002Fbadge\u002Fmod\u002Fgithub.com\u002Fspf13\u002Fviper)](https:\u002F\u002Fpkg.go.dev\u002Fmod\u002Fgithub.com\u002Fspf13\u002Fviper)\n\n**Go configuration with fangs!**\n\nMany Go projects are built using Viper including:\n\n* [Hugo](http:\u002F\u002Fgohugo.io)\n* [EMC RexRay](http:\u002F\u002Frexray.readthedocs.org\u002Fen\u002Fstable\u002F)\n* [Imgur’s Incus](https:\u002F\u002Fgithub.com\u002FImgur\u002Fincus)\n* [Nanobox](https:\u002F\u002Fgithub.com\u002Fnanobox-io\u002Fnanobox)\u002F[Nanopack](https:\u002F\u002Fgithub.com\u002Fnanopack)\n* [Docker Notary](https:\u002F\u002Fgithub.com\u002Fdocker\u002FNotary)\n* [BloomApi](https:\u002F\u002Fwww.bloomapi.com\u002F)\n* [doctl](https:\u002F\u002Fgithub.com\u002Fdigitalocean\u002Fdoctl)\n* [Clairctl](https:\u002F\u002Fgithub.com\u002Fjgsqware\u002Fclairctl)\n* [Mercure](https:\u002F\u002Fmercure.rocks)\n* [Meshery](https:\u002F\u002Fgithub.com\u002Fmeshery\u002Fmeshery)\n* [Bearer](https:\u002F\u002Fgithub.com\u002Fbearer\u002Fbearer)\n* [Coder](https:\u002F\u002Fgithub.com\u002Fcoder\u002Fcoder)\n* [Vitess](https:\u002F\u002Fvitess.io\u002F)\n\n\n## Install\n\n```shell\ngo get github.com\u002Fspf13\u002Fviper\n```\n\n> **NOTE** Viper uses [Go Modules](https:\u002F\u002Fgo.dev\u002Fwiki\u002FModules) to manage dependencies.\n\n\n## Why use Viper?\n\nViper is a complete configuration solution for Go applications including\n[12-Factor apps](https:\u002F\u002F12factor.net\u002F#the_twelve_factors). It is designed to\nwork within any application, and can handle all types of configuration needs\nand formats. It supports:\n\n* setting defaults\n* setting explicit values\n* reading config files\n* dynamic discovery of config files across multiple locations\n* reading from environment variables\n* reading from remote systems (e.g. Etcd or Consul)\n* reading from command line flags\n* reading from buffers\n* live watching and updating configuration\n* aliasing configuration keys for easy refactoring\n\nViper can be thought of as a registry for all of your applications'\nconfiguration needs.\n\n\n## Putting Values in Viper\n\nViper can read from multiple configuration sources and merges them together\ninto one set of configuration keys and values.\n\nViper uses the following precedence for merging:\n\n * explicit call to `Set`\n * flags\n * environment variables\n * config files\n * external key\u002Fvalue stores\n * defaults\n\n> **NOTE** Viper configuration keys are case insensitive.\n\n### Reading Config Files\n\nViper requires minimal configuration to load config files. Viper currently supports:\n\n* JSON\n* TOML\n* YAML\n* INI\n* envfile\n* Java Propeties\n\nA single Viper instance only supports a single configuration file, but multiple\npaths may be searched for one.\n\nHere is an example of how to use Viper to search for and read a configuration\nfile. At least one path should be provided where a configuration file is\nexpected.\n\n```go\n\u002F\u002F Name of the config file without an extension (Viper will intuit the type\n\u002F\u002F from an extension on the actual file)\nviper.SetConfigName(\"config\")\n\n\u002F\u002F Add search paths to find the file\nviper.AddConfigPath(\"\u002Fetc\u002Fappname\u002F\")\nviper.AddConfigPath(\"$HOME\u002F.appname\")\nviper.AddConfigPath(\".\")\n\n\u002F\u002F Find and read the config file\nerr := viper.ReadInConfig()\n\n\u002F\u002F Handle errors\nif err != nil {\n\tpanic(fmt.Errorf(\"fatal error config file: %w\", err))\n}\n```\n\nYou can handle the specific case where no config file is found.\n\n```go\nvar fileLookupError viper.FileLookupError\nif err := viper.ReadInConfig(); err != nil {\n    if errors.As(err, &fileLookupError) {\n        \u002F\u002F Indicates an explicitly set config file is not found (such as with\n        \u002F\u002F using `viper.SetConfigFile`) or that no config file was found in\n        \u002F\u002F any search path (such as when using `viper.AddConfigPath`)\n    } else {\n        \u002F\u002F Config file was found but another error was produced\n    }\n}\n\n\u002F\u002F Config file found and successfully parsed\n```\n\n> **NOTE (since 1.6)** You can also have a file without an extension and\n> specify the format programmatically, which is useful for files that naturally\n> have no extension (e.g., `.bashrc`).\n\n### Writing Config Files\n\nAt times you may want to store all configuration modifications made during run\ntime.\n\n```go\n\u002F\u002F Writes current config to the path set by `AddConfigPath` and `SetConfigName`\nviper.WriteConfig()\nviper.SafeWriteConfig() \u002F\u002F Like the above, but will error if the config file exists\n\n\u002F\u002F Writes current config to a specific place\nviper.WriteConfigAs(\"\u002Fpath\u002Fto\u002Fmy\u002F.config\")\n\n\u002F\u002F Will error since it has already been written\nviper.SafeWriteConfigAs(\"\u002Fpath\u002Fto\u002Fmy\u002F.config\")\n\nviper.SafeWriteConfigAs(\"\u002Fpath\u002Fto\u002Fmy\u002F.other_config\")\n```\n\nAs a rule of the thumb, methods prefixed with `Safe` won't overwrite any\nexisting file, while other methods will.\n\n### Watching and Re-reading Config Files\n\nGone are the days of needing to restart a server to have a config take\neffect--Viper powered applications can read an update to a config file while\nrunning and not miss a beat.\n\nIt's also possible to provide a function for Viper to run each time a change\noccurs.\n\n```go\n\u002F\u002F All config paths must be defined prior to calling `WatchConfig()`\nviper.AddConfigPath(\"$HOME\u002F.appname\")\n\nviper.OnConfigChange(func(e fsnotify.Event) {\n\tfmt.Println(\"Config file changed:\", e.Name)\n})\n\nviper.WatchConfig()\n```\n\n### Reading Config from `io.Reader`\n\nViper predefines many configuration sources but you can also implement your own\nrequired configuration source.\n\n```go\nviper.SetConfigType(\"yaml\")\n\nvar yamlExample = []byte(`\nhacker: true\nhobbies:\n- skateboarding\n- snowboarding\n- go\nname: steve\n`)\n\nviper.ReadConfig(bytes.NewBuffer(yamlExample))\n\nviper.Get(\"name\") \u002F\u002F \"steve\"\n```\n\n### Setting Defaults\n\nA good configuration system will support default values, which are used if a\nkey hasn't been set in some other way.\n\nExamples:\n\n```go\nviper.SetDefault(\"ContentDir\", \"content\")\nviper.SetDefault(\"LayoutDir\", \"layouts\")\nviper.SetDefault(\"Taxonomies\", map[string]string{\"tag\": \"tags\", \"category\": \"categories\"})\n```\n\n### Setting Overrides\n\nViper allows explict setting of configuration, such as from your own\napplication logic.\n\n```go\nviper.Set(\"verbose\", true)\nviper.Set(\"host.port\", 5899) \u002F\u002F Set an embedded key\n```\n\n### Registering and Using Aliases\n\nAliases permit a single value to be referenced by multiple keys\n\n```go\nviper.RegisterAlias(\"loud\", \"Verbose\")\n\nviper.Set(\"verbose\", true) \u002F\u002F Same result as next line\nviper.Set(\"loud\", true)    \u002F\u002F Same result as prior line\n\nviper.GetBool(\"loud\")    \u002F\u002F true\nviper.GetBool(\"verbose\") \u002F\u002F true\n```\n\n### Working with Environment Variables\n\nViper has full support for environment variables.\n\n> **NOTE** Unlike other configuration sources, environment variables are case\n> sensitive.\n\n```go\n\u002F\u002F Tells Viper to use this prefix when reading environment variables\nviper.SetEnvPrefix(\"spf\")\n\n\u002F\u002F Viper will look for \"SPF_ID\", automatically uppercasing the prefix and key\nviper.BindEnv(\"id\")\n\n\u002F\u002F Alternatively, we can search for any environment variable prefixed and load\n\u002F\u002F them in\nviper.AutomaticEnv()\n\nos.Setenv(\"SPF_ID\", \"13\")\n\nid := viper.Get(\"id\") \u002F\u002F 13\n```\n\n* By default, empty environment variables are considered unset and will fall back to\n  the next configuration source, unless `AllowEmptyEnv` is used.\n* Viper does not \"cache\" environment variables--the value will be read each\n  time it is accessed.\n* `SetEnvKeyReplacer` and `EnvKeyReplacer` allow you to rewrite environment\n  variable keys, which is useful to merge SCREAMING_SNAKE_CASE environment\n  variables with kebab-cased configuration values from other sources.\n\n### Working with Flags\n\nViper has the ability to bind to flags. Specifically, Viper supports\n[pflag](https:\u002F\u002Fgithub.com\u002Fspf13\u002Fpflag\u002F) as used in the\n[Cobra](https:\u002F\u002Fgithub.com\u002Fspf13\u002Fcobra) library.\n\nLike environment variables, the value is not set when the binding method is\ncalled, but when it is accessed.\n\nFor individual flags, the `BindPFlag` method provides this functionality.\n\n```go\nserverCmd.Flags().Int(\"port\", 1138, \"Port to run Application server on\")\n\nviper.BindPFlag(\"port\", serverCmd.Flags().Lookup(\"port\"))\n```\n\nYou can also bind an existing set of pflags.\n\n```go\npflag.Int(\"flagname\", 1234, \"help message for flagname\")\npflag.Parse()\n\nviper.BindPFlags(pflag.CommandLine)\n\ni := viper.GetInt(\"flagname\") \u002F\u002F Retrieve values from viper instead of pflag\n```\n\nThe standard library [flag](https:\u002F\u002Fgolang.org\u002Fpkg\u002Fflag\u002F) package is not\ndirectly supported, but may be parsed through pflag.\n\n```go\npackage main\n\nimport (\n\t\"flag\"\n\n\t\"github.com\u002Fspf13\u002Fpflag\"\n)\n\nfunc main() {\n\t\u002F\u002F Using standard library \"flag\" package\n\tflag.Int(\"flagname\", 1234, \"help message for flagname\")\n\n    \u002F\u002F Pass standard library flags to pflag\n\tpflag.CommandLine.AddGoFlagSet(flag.CommandLine)\n\tpflag.Parse()\n\n    \u002F\u002F Viper takes over\n\tviper.BindPFlags(pflag.CommandLine)\n}\n```\n\nUse of pflag may be avoided entirely by implementing the `FlagValue` and\n`FlagValueSet` interfaces.\n\n```go\n\u002F\u002F Implementing FlagValue\n\ntype myFlag struct {}\nfunc (f myFlag) HasChanged() bool { return false }\nfunc (f myFlag) Name() string { return \"my-flag-name\" }\nfunc (f myFlag) ValueString() string { return \"my-flag-value\" }\nfunc (f myFlag) ValueType() string { return \"string\" }\n\nviper.BindFlagValue(\"my-flag-name\", myFlag{})\n\n\u002F\u002F Implementing FlagValueSet\n\ntype myFlagSet struct {\n\tflags []myFlag\n}\nfunc (f myFlagSet) VisitAll(fn func(FlagValue)) {\n\tfor _, flag := range flags {\n\t\tfn(flag)\n\t}\n}\n\nfSet := myFlagSet{\n\tflags: []myFlag{myFlag{}, myFlag{}},\n}\nviper.BindFlagValues(\"my-flags\", fSet)\n```\n\n### Remote Key\u002FValue Store Support\n\nTo enable remote support in Viper, do a blank import of the `viper\u002Fremote`\npackage.\n\n```go\nimport _ \"github.com\u002Fspf13\u002Fviper\u002Fremote\"\n```\n\nViper supports the following remote key\u002Fvalue stores. Examples for each are\nprovided below.\n\n* Etcd and Etcd3\n* Consul\n* Firestore\n* NATS\n\nViper will read a config string retrieved from a path in a key\u002Fvalue store.\n\nViper supports multiple hosts separated by `;`. For example:\n`http:\u002F\u002F127.0.0.1:4001;http:\u002F\u002F127.0.0.1:4002`.\n\n#### Encryption\n\nViper uses [crypt](https:\u002F\u002Fgithub.com\u002Fsagikazarmark\u002Fcrypt) to retrieve\nconfiguration from the key\u002Fvalue store, which means that you can store your\nconfiguration values encrypted and have them automatically decrypted if you\nhave the correct GPG keyring. Encryption is optional.\n\nCrypt has a command-line helper that you can use to put configurations in your\nkey\u002Fvalue store.\n\n```bash\n$ go get github.com\u002Fsagikazarmark\u002Fcrypt\u002Fbin\u002Fcrypt\n$ crypt set -plaintext \u002Fconfig\u002Fhugo.json \u002FUsers\u002Fhugo\u002Fsettings\u002Fconfig.json\n$ crypt get -plaintext \u002Fconfig\u002Fhugo.json\n```\n\nSee the Crypt documentation for examples of how to set encrypted values, or\nhow to use Consul.\n\n### Remote Key\u002FValue Store Examples (Unencrypted)\n\n#### etcd\n\n```go\nviper.AddRemoteProvider(\"etcd\", \"http:\u002F\u002F127.0.0.1:4001\",\"\u002Fconfig\u002Fhugo.json\")\nviper.SetConfigType(\"json\") \u002F\u002F because there is no file extension in a stream of bytes, supported extensions are \"json\", \"toml\", \"yaml\", \"yml\", \"properties\", \"props\", \"prop\", \"env\", \"dotenv\"\nerr := viper.ReadRemoteConfig()\n```\n\n#### etcd3\n\n```go\nviper.AddRemoteProvider(\"etcd3\", \"http:\u002F\u002F127.0.0.1:4001\",\"\u002Fconfig\u002Fhugo.json\")\nviper.SetConfigType(\"json\") \u002F\u002F because there is no file extension in a stream of bytes, supported extensions are \"json\", \"toml\", \"yaml\", \"yml\", \"properties\", \"props\", \"prop\", \"env\", \"dotenv\"\nerr := viper.ReadRemoteConfig()\n```\n\n#### Consul\n\nGiven a Consul key `MY_CONSUL_KEY` with the value:\n\n```json\n{\n    \"port\": 8080,\n    \"hostname\": \"myhostname.com\"\n}\n```\n\n```go\nviper.AddRemoteProvider(\"consul\", \"localhost:8500\", \"MY_CONSUL_KEY\")\nviper.SetConfigType(\"json\") \u002F\u002F Need to explicitly set this to json\nerr := viper.ReadRemoteConfig()\n\nfmt.Println(viper.Get(\"port\")) \u002F\u002F 8080\n```\n\n#### Firestore\n\n```go\nviper.AddRemoteProvider(\"firestore\", \"google-cloud-project-id\", \"collection\u002Fdocument\")\nviper.SetConfigType(\"json\") \u002F\u002F Config's format: \"json\", \"toml\", \"yaml\", \"yml\"\nerr := viper.ReadRemoteConfig()\n```\n\nOf course, you're allowed to use `SecureRemoteProvider` also.\n\n#### NATS\n\n```go\nviper.AddRemoteProvider(\"nats\", \"nats:\u002F\u002F127.0.0.1:4222\", \"myapp.config\")\nviper.SetConfigType(\"json\")\nerr := viper.ReadRemoteConfig()\n```\n\n### Remote Key\u002FValue Store Examples (Encrypted)\n\n```go\nviper.AddSecureRemoteProvider(\"etcd\",\"http:\u002F\u002F127.0.0.1:4001\",\"\u002Fconfig\u002Fhugo.json\",\"\u002Fetc\u002Fsecrets\u002Fmykeyring.gpg\")\nviper.SetConfigType(\"json\") \u002F\u002F because there is no file extension in a stream of bytes,  supported extensions are \"json\", \"toml\", \"yaml\", \"yml\", \"properties\", \"props\", \"prop\", \"env\", \"dotenv\"\nerr := viper.ReadRemoteConfig()\n```\n\n### Watching Key\u002FValue Store Changes\n\n```go\n\u002F\u002F Alternatively, you can create a new viper instance\nvar runtime_viper = viper.New()\n\nruntime_viper.AddRemoteProvider(\"etcd\", \"http:\u002F\u002F127.0.0.1:4001\", \"\u002Fconfig\u002Fhugo.yml\")\nruntime_viper.SetConfigType(\"yaml\") \u002F\u002F because there is no file extension in a stream of bytes, supported extensions are \"json\", \"toml\", \"yaml\", \"yml\", \"properties\", \"props\", \"prop\", \"env\", \"dotenv\"\n\n\u002F\u002F Read from remote config the first time\nerr := runtime_viper.ReadRemoteConfig()\n\n\u002F\u002F Unmarshal config\nruntime_viper.Unmarshal(&runtime_conf)\n\n\u002F\u002F Open a goroutine to watch remote changes forever\ngo func(){\n\tfor {\n\t\ttime.Sleep(time.Second * 5) \u002F\u002F delay after each request\n\n\t\t\u002F\u002F Currently, only tested with Etcd support\n\t\terr := runtime_viper.WatchRemoteConfig()\n\t\tif err != nil {\n\t\t\tlog.Errorf(\"unable to read remote config: %v\", err)\n\t\t\tcontinue\n\t\t}\n\n\t\t\u002F\u002F Unmarshal new config into our runtime config struct\n\t\truntime_viper.Unmarshal(&runtime_conf)\n\t}\n}()\n```\n\n## Getting Values From Viper\n\nThe simplest way to retrieve configuration values from Viper is to use `Get*`\nfunctions. `Get` will return an any type, but specific types may be retrieved\nwith `Get\u003CType>` functions.\n\nNote that each `Get*` function will return a zero value if it’s key is not\nfound. To check if a key exists, use the `IsSet` method.\n\nNested keys use `.` as a delimiter and numbers for array indexes. Given the\nfollowing configuration:\n\n```jsonc\n{\n    \"datastore\": {\n        \"metric\": {\n            \"host\": \"127.0.0.1\",\n            \"ports\": [\n                5799,\n                6029\n            ]\n        }\n    }\n}\n\n```\n\n```go\nGetString(\"datastore.metric.host\") \u002F\u002F \"127.0.0.1\"\nGetInt(\"host.ports.1\") \u002F\u002F 6029\n```\n\n> **NOTE** Viper _does not_ deep merge configuration values. Complex values\n> that are overridden will be entirely replaced.\n\nIf there exists a key that matches the delimited key path, its value will be\nreturned instead.\n\n```jsonc\n{\n    \"datastore.metric.host\": \"0.0.0.0\",\n    \"datastore\": {\n        \"metric\": {\n            \"host\": \"127.0.0.1\"\n        }\n    }\n}\n```\n\n```go\nGetString(\"datastore.metric.host\") \u002F\u002F \"0.0.0.0\"\n```\n\n### Configuration Subsets\n\nIt's often useful to extract a subset of configuration (e.g., when developing a\nreusable module which should accept specific sections of configuration).\n\n```yaml\ncache:\n  cache1:\n    item-size: 64\n    max-items: 100\n  cache2:\n    item-size: 80\n    max-items: 200\n```\n\n```go\nfunc NewCache(v *Viper) *Cache {\n\treturn &Cache{\n\t\tItemSize: v.GetInt(\"item-size\"),\n\t\tMaxItems: v.GetInt(\"max-items\"),\n\t}\n}\n\ncache1Config := viper.Sub(\"cache.cache1\")\n\nif cache1Config == nil {\n    \u002F\u002F Sub returns nil if the key cannot be found\n\tpanic(\"cache configuration not found\")\n}\n\ncache1 := NewCache(cache1Config)\n```\n\n### Unmarshaling\n\nYou also have the option of unmarshaling configuration to a struct, map, etc.,\nusing `Unmarshal*` methods.\n\n```go\ntype config struct {\n\tPort int\n\tName string\n\tPathMap string `mapstructure:\"path_map\"`\n}\n\nvar C config\n\nerr := viper.Unmarshal(&C)\nif err != nil {\n\tt.Fatalf(\"unable to decode into struct, %v\", err)\n}\n```\n\nIf you want to unmarshal configuration where the keys themselves contain `.`\n(the default key delimiter), you can change the delimiter.\n\n```go\nv := viper.NewWithOptions(viper.KeyDelimiter(\"::\"))\n\nv.SetDefault(\"chart::values\", map[string]any{\n\t\"ingress\": map[string]any{\n\t\t\"annotations\": map[string]any{\n\t\t\t\"traefik.frontend.rule.type\":                 \"PathPrefix\",\n\t\t\t\"traefik.ingress.kubernetes.io\u002Fssl-redirect\": \"true\",\n\t\t},\n\t},\n})\n\ntype config struct {\n\tChart struct{\n\t\tValues map[string]any\n\t}\n}\n\nvar C config\n\nv.Unmarshal(&C)\n```\n\nViper also supports unmarshaling into embedded structs.\n\n```go\n\u002F*\nExample config:\n\nmodule:\n    enabled: true\n    token: 89h3f98hbwf987h3f98wenf89ehf\n*\u002F\ntype config struct {\n\tModule struct {\n\t\tEnabled bool\n\n\t\tmoduleConfig `mapstructure:\",squash\"`\n\t}\n}\n\ntype moduleConfig struct {\n\tToken string\n}\n\nvar C config\n\nerr := viper.Unmarshal(&C)\nif err != nil {\n\tt.Fatalf(\"unable to decode into struct, %v\", err)\n}\n```\n\nViper uses\n[github.com\u002Fgo-viper\u002Fmapstructure](https:\u002F\u002Fgithub.com\u002Fgo-viper\u002Fmapstructure)\nunder the hood for unmarshaling values which uses `mapstructure` tags, by\ndefault.\n\n### Marshalling to String\n\nYou may need to marshal all the settings held in Viper into a string. You can\nuse your favorite format's marshaller with the config returned by\n`AllSettings`.\n\n```go\nimport (\n\tyaml \"go.yaml.in\u002Fyaml\u002Fv3\"\n)\n\nfunc yamlStringSettings() string {\n\tc := viper.AllSettings()\n\tbs, err := yaml.Marshal(c)\n\tif err != nil {\n\t\tlog.Fatalf(\"unable to marshal config to YAML: %v\", err)\n\t}\n\treturn string(bs)\n}\n```\n\n### Decoding Custom Formats\n\nA frequently requested feature is adding more value formats and decoders (for\nexample; parsing character delimited strings into slices. This is already\navailable in Viper using mapstructure decode hooks.\n\nRead more in [this blog\npost](https:\u002F\u002Fsagikazarmark.hu\u002Fblog\u002Fdecoding-custom-formats-with-viper\u002F).\n\n\n## FAQ\n\n### Why is it called “Viper”?\n\nViper is designed to be a\n[companion](http:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FViper_(G.I._Joe)) to\n[Cobra](https:\u002F\u002Fgithub.com\u002Fspf13\u002Fcobra). While both can operate completely\nindependently, together they make a powerful pair to handle much of your\napplication foundation needs.\n\n### I found a bug or want a feature, should I file an issue or a PR?\n\nYes, but there are two things to be aware of.\n\n1.  The Viper project is currently prioritizing backwards compatibility and\n    stability over features.\n2.  Features may be deferred until Viper 2 forms.\n\n### Can multiple Viper instances be used?\n\n**tl;dr:** Yes.\n\nEach will have its own unique configuration and can read from a different\nconfiguration source. All of the functions that the Viper package supports are\nmirrored as methods on a Viper instance.\n\n```go\nx := viper.New()\ny := viper.New()\n\nx.SetDefault(\"ContentDir\", \"content\")\ny.SetDefault(\"ContentDir\", \"foobar\")\n```\n\n### Should Viper be a global singleton or passed around?\n\nThe best practice is to initialize a Viper instance and pass that around when\nnecessary.\n\nViper comes with a global instance (singleton) out of the box. Although it\nmakes setting up configuration easy, using it is generally discouraged as it\nmakes testing harder and can lead to unexpected behavior.\n\nThe global instance may be deprecated in the future. See\n[#1855](https:\u002F\u002Fgithub.com\u002Fspf13\u002Fviper\u002Fissues\u002F1855) for more details.\n\n### Does Viper support case sensitive keys?\n\n**tl;dr:** No.\n\nViper merges configuration from various sources, many of which are either case\ninsensitive or use different casing than other sources (e.g., env vars). In\norder to provide the best experience when using multiple sources, all keys are\nmade case insensitive.\n\nThere has been several attempts to implement case sensitivity, but\nunfortunately it's not trivial. We might take a stab at implementing it in\n[Viper v2](https:\u002F\u002Fgithub.com\u002Fspf13\u002Fviper\u002Fissues\u002F772), but despite the initial\nnoise, it does not seem to be requested that much.\n\nYou can vote for case sensitivity by filling out this feedback form:\nhttps:\u002F\u002Fforms.gle\u002FR6faU74qPRPAzchZ9.\n\n### Is it safe to concurrently read and write to a Viper instance?\n\nNo, you will need to synchronize access to Viper yourself (for example by using\nthe `sync` package). Concurrent reads and writes can cause a panic.\n\n\n## Troubleshooting\n\nSee [TROUBLESHOOTING.md](TROUBLESHOOTING.md).\n\n\n## Development\n\n**For an optimal developer experience, it is recommended to install\n[Nix](https:\u002F\u002Fnixos.org\u002Fdownload.html) and\n[direnv](https:\u002F\u002Fdirenv.net\u002Fdocs\u002Finstallation.html).**\n\n_Alternatively, install [Go](https:\u002F\u002Fgo.dev\u002Fdl\u002F) on your computer then run\n`make deps` to install the rest of the dependencies._\n\nRun the test suite:\n\n```shell\nmake test\n```\n\nRun linters:\n\n```shell\nmake lint # pass -j option to run them in parallel\n```\n\nSome linter violations can automatically be fixed:\n\n```shell\nmake fmt\n```\n\n\n## License\n\nThe project is licensed under the [MIT License](LICENSE).\n","Viper 是一个用于 Go 语言应用程序的配置管理库。它支持多种配置源，包括默认值、显式设置、配置文件、环境变量、远程系统（如 Etcd 或 Consul）、命令行标志和缓冲区。Viper 的核心功能包括动态发现多个位置的配置文件、实时监控和更新配置以及配置键别名，便于重构。此外，Viper 还支持 12-Factor 应用程序的最佳实践，使其成为构建复杂且灵活的 Go 应用程序的理想选择。适用于需要统一管理和访问不同来源配置信息的各种场景，特别是那些对配置灵活性和可维护性有较高要求的应用。",2,"2026-06-11 02:59:57","top_language"]