How I Stopped Restarting My Go Server Every Time I Changed a Config
You know that feeling when you're debugging something small, and suddenly, it turns into a whole production issue? That was me at 2 AM, staring at my terminal, wondering why my Go service wouldn’t pick up a simple config change. Every time I updated a setting, I had to restart the entire server. It wasn’t a big deal locally, but in production? A restart meant downtime, annoyed users, and unnecessary overhead. I knew there had to be a better way. Config Changes Without Restarts At Nife.io, we deal with multi-cloud deployments, where things like rate limits, API keys, and feature toggles change dynamically. The last thing we wanted was to restart services just to apply new configs. So, I started digging and found a way to reload configs on the fly. Here’s how I did it. Step 1: Stop Hardcoding Configs First mistake? Manually parsing JSON and YAML files every time the service started. Instead, I switched to Viper, a Go package that supports multiple formats and simplifies config management. Install Viper go get github.com/spf13/viper Load Configs Dynamically package config import ( "log" "github.com/spf13/viper" ) func LoadConfig() { viper.SetConfigName("config") viper.SetConfigType("toml") viper.AddConfigPath(".") if err := viper.ReadInConfig(); err != nil { log.Fatalf("Error reading config: %v", err) } } No more hardcoding file paths or worrying about whether the format is JSON, TOML, or YAML. Step 2: Auto-Reload Without Restarting Now for the key part. I made Viper watch the config file and automatically apply changes. viper.WatchConfig() viper.OnConfigChange(func(e fsnotify.Event) { log.Println("Config updated:", e.Name) // Apply new settings dynamically }) Now, every time I update config.toml, my app instantly picks up the new values—no restart required. Step 3: Secure Secrets Properly This worked great, but there was still a major issue—I was storing API keys and passwords inside the config file. Not ideal. So, I switched to HashiCorp Vault to securely fetch secrets instead. package vault import ( "log" "github.com/hashicorp/vault/api" ) func GetSecret(path string) (map[string]interface{}, error) { client, err := api.NewClient(&api.Config{Address: "http://127.0.0.1:8200"}) if err != nil { return nil, err } secret, err := client.Logical().Read(path) if err != nil { return nil, err } if secret == nil { return nil, nil } return secret.Data, nil } Now, instead of storing sensitive information inside config.toml, I retrieve it securely from Vault when needed. Step 4: Manually Trigger a Config Reload For additional control, I added an API endpoint that lets me reload configs manually. http.HandleFunc("/reload", func(w http.ResponseWriter, r *http.Request) { LoadConfig() w.Write([]byte("Config reloaded successfully!")) }) Now, if I need to force a config refresh, I just run: curl http://localhost:8080/reload Instant update, no restart needed. How This Helps in the Real World At Nife.io, we manage cloud deployments across multiple providers, which means configs change frequently. By making them dynamic, we: Adjust API rate limits instantly Toggle features without redeploying Rotate credentials securely And most importantly—no more late-night restarts. Final Thoughts If you're still restarting your Go services for config updates, there’s a better way. Try dynamic config reloading and let me know how it works for you. How do you handle configs in your apps? Let’s discuss.

You know that feeling when you're debugging something small, and suddenly, it turns into a whole production issue? That was me at 2 AM, staring at my terminal, wondering why my Go service wouldn’t pick up a simple config change.
Every time I updated a setting, I had to restart the entire server. It wasn’t a big deal locally, but in production? A restart meant downtime, annoyed users, and unnecessary overhead.
I knew there had to be a better way.
Config Changes Without Restarts
At Nife.io, we deal with multi-cloud deployments, where things like rate limits, API keys, and feature toggles change dynamically. The last thing we wanted was to restart services just to apply new configs.
So, I started digging and found a way to reload configs on the fly. Here’s how I did it.
Step 1: Stop Hardcoding Configs
First mistake? Manually parsing JSON and YAML files every time the service started. Instead, I switched to Viper, a Go package that supports multiple formats and simplifies config management.
Install Viper
go get github.com/spf13/viper
Load Configs Dynamically
package config
import (
"log"
"github.com/spf13/viper"
)
func LoadConfig() {
viper.SetConfigName("config")
viper.SetConfigType("toml")
viper.AddConfigPath(".")
if err := viper.ReadInConfig(); err != nil {
log.Fatalf("Error reading config: %v", err)
}
}
No more hardcoding file paths or worrying about whether the format is JSON, TOML, or YAML.
Step 2: Auto-Reload Without Restarting
Now for the key part. I made Viper watch the config file and automatically apply changes.
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
log.Println("Config updated:", e.Name)
// Apply new settings dynamically
})
Now, every time I update config.toml
, my app instantly picks up the new values—no restart required.
Step 3: Secure Secrets Properly
This worked great, but there was still a major issue—I was storing API keys and passwords inside the config file. Not ideal.
So, I switched to HashiCorp Vault to securely fetch secrets instead.
package vault
import (
"log"
"github.com/hashicorp/vault/api"
)
func GetSecret(path string) (map[string]interface{}, error) {
client, err := api.NewClient(&api.Config{Address: "http://127.0.0.1:8200"})
if err != nil {
return nil, err
}
secret, err := client.Logical().Read(path)
if err != nil {
return nil, err
}
if secret == nil {
return nil, nil
}
return secret.Data, nil
}
Now, instead of storing sensitive information inside config.toml
, I retrieve it securely from Vault when needed.
Step 4: Manually Trigger a Config Reload
For additional control, I added an API endpoint that lets me reload configs manually.
http.HandleFunc("/reload", func(w http.ResponseWriter, r *http.Request) {
LoadConfig()
w.Write([]byte("Config reloaded successfully!"))
})
Now, if I need to force a config refresh, I just run:
curl http://localhost:8080/reload
Instant update, no restart needed.
How This Helps in the Real World
At Nife.io, we manage cloud deployments across multiple providers, which means configs change frequently. By making them dynamic, we:
- Adjust API rate limits instantly
- Toggle features without redeploying
- Rotate credentials securely
And most importantly—no more late-night restarts.
Final Thoughts
If you're still restarting your Go services for config updates, there’s a better way. Try dynamic config reloading and let me know how it works for you.
How do you handle configs in your apps? Let’s discuss.