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.

Mar 11, 2025 - 23:28
 0
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.