Golang Environment variables [One STOP Tutorial]


GO

Getting started with golang environment variables

Environment variables are the best way to set configuration values for our software application. They can be defined at the system level i.e development, testing, staging, and production levels. These configuration values are made for database configs, third-party URLs, APIs URLs, etc. The environment variable is essential for developing and deploying the application.

When I started with shell programming language, reading variables from files was relatively very easy. You just source the file with the environment variables and just like that you could access all those variables. While in golang we have to rely on some modules to read environment variables from a file.

 

What will you learn in this article?

By the end of this article, you should be familiar with below topics.

  • How to declare environment variable using a file
  • How to read environment variables from a file
  • How to declare a fallback value if the variable is not defined or emoty
  • How to read environment variable from system
  • How to check if environment variable is defined and non-empty

 

3 Methods to access Environment Variables in golang

The environment variable is important when containerizing and deploying the application into a staging and production environment. It easily overrides the default configurations when deploying an app either into the cloud, Kubernetes, or Docker environment.

In Go, we can manage this environments variables using three packages. Namely:-

  • Viper package
  • godotenv package
  • os package

 

Setup Lab Environment

Create Project module

Let me setup my lab environment for the demonstration. Before we continue on how to create project, get detail steps from our previous article How to create modules in Go
We will create a project directory GoLinuxCloud-env and two files main.go and app.env

$ mkdir GoLinuxCloud-env
$ cd GoLinuxCloud-env 
$ touch main.go && touch app.env

main.go holds our code and app.env holds our environment variables.

 

Initialize the GoLinuxCloud module of the program

Navigate inside the directory and execute on the terminal

$ go mod init GoLinuxCloud-env

the above command creates two files nested go.mod and go.sum to hold all program package and there versions such as viper or godotenv packages.

 

Create config file containing environment variables

We will create a sample config file namely app.env to store/hold our default configuration values for our preferred environment.

IMPORTANT NOTE:
You must follow below set of rules while creating the env file or else golang will not be able to recognise the variable values:
  • All variables should be declared on a separate line
  • Case-sensitive variable name should be named using uppercase and words should be separated using underscore i.e DB_HOST
  • The variable value is followed by the name after an equal symbol i.e DB_HOST=localhost
  • The variable value should not be enclosed in a double quotes i.e DB_HOST=”localhost”
  • The comments should not be in line with the variable i.e #depends with database ports mysql,mongodb,postgres etc
    DB_PORT=5432

 

Sample app.env file

# sample app.env
# environment can be test,production,testing
APP_ENV=development
# username
DB_USER=postgres
# secure password
DB_PASS=pass
# app version not set
APP_VERSION=
#listening to any address
SERVER_ADDRESS=0.0.0.0:8080
# host value
DB_HOST=localhost
#depends with database mysql,mongodb,postgres etc
DB_PORT=5432

 

Method-1: Use Viper package to read environment variable from file

It’s an environment management package. It's essential for reading configuration from file and environment variables. It's capable of doing the following functions:-

1.1 Install viper in the project

Read more about Viper package in the material. Form the root of the project copy and paste the below command

$ go get github.com/spf13/viper

This package will be added to the go.mod file in the project as a dependency.

 

1.2 Store the environment variable

We will use struct to store the environment variable from the file into global vars inside our go code.

type Config struct {
	AppEnv        string `mapstructure:"APP_ENV"`
	DBUser        string `mapstructure:"DB_USER"`
	DBPass        string `mapstructure:"DB_PASS"`
	DBHost        string `mapstructure:"DB_HOST"`
	DBPort        string `mapstructure:"DB_PORT"`
	DBDriver      string `mapstructure:"DB_DRIVER"`
	AppVersion    string `mapstructure:"APP_VERSION"`
	ServerAddress string `mapstructure:"SERVER_ADDRESS"`
}

 

1.3 Load the environment file

Next we will load the environment file app.env using func:

func LoadConfig(path string) (config Config, err error) {
	// Read file path
	viper.AddConfigPath(path)
	// set config file and path
	viper.SetConfigName("app")
	viper.SetConfigType("env")
	// watching changes in app.env
	viper.AutomaticEnv()
	// reading the config file
	err = viper.ReadInConfig()
	if err != nil {
		return
	}

	err = viper.Unmarshal(&config)
	return
}

 

Example-1: Access environment variable (Final Code with viper)

Here is the complete piece of golang code to read and print the environment variable:

package main

import (
	"fmt"
	"log"

	"github.com/spf13/viper"
)

// Config stores all configuration of the application.
// The values are read by viper from a config file or environment variable.
type Config struct {
	AppEnv        string `mapstructure:"APP_ENV"`
	DBUser        string `mapstructure:"DB_USER"`
	DBPass        string `mapstructure:"DB_PASS"`
	DBHost        string `mapstructure:"DB_HOST"`
	DBPort        string `mapstructure:"DB_PORT"`
	DBDriver      string `mapstructure:"DB_DRIVER"`
	AppVersion    string `mapstructure:"APP_VERSION"`
	ServerAddress string `mapstructure:"SERVER_ADDRESS"`
}

// LoadConfig reads configuration from file or environment variables.
func LoadConfig(path string) (config Config, err error) {
	// Read file path
	viper.AddConfigPath(path)
	// set config file and path
	viper.SetConfigName("app")
	viper.SetConfigType("env")
	// watching changes in app.env
	viper.AutomaticEnv()
	// reading the config file
	err = viper.ReadInConfig()
	if err != nil {
		return
	}

	err = viper.Unmarshal(&config)
	return
}

func main() {
	// load app.env file data to struct
	config, err := LoadConfig(".")

	// handle errors
	if err != nil {
		log.Fatalf("can't load environment app.env: %v", err)
	}

	fmt.Printf(" -----%s----\n", "Reading Environment variables Using Viper package")
	fmt.Printf(" %s = %v \n", "Application_Environment", config.AppEnv)
	// not defined
	fmt.Printf(" %s = %s \n", "DB_DRIVE", dbDrive)
	fmt.Printf(" %s = %s \n", "Server_Listening_Address", config.ServerAddress)
	fmt.Printf(" %s = %s \n", "Database_User_Name", config.DBUser)
	fmt.Printf(" %s = %s \n", "Database_User_Password", config.DBPass)

}

Output:

$ go run main.go
 ------Reading Environment variables Using Viper package----------
 Application_Environment = development 
 Server_Listening_Address = 0.0.0.0:8080 
 Database_User_Name = postgres 
 Database_User_Password = pass

 

Example-2: Set default values when variable in undefined or empty

There are situations when an environment variable may not be present so in such case rather than failing the code, we can define a fallback default value using viper.SetDefault:

Here is a sample golang code where we have created a function which will assign a default value if the variable is empty or undefined:

package main

import (
	"fmt"

	"github.com/spf13/viper"
)

//Function to read an environment or return a default value
func getEnvValue(key string, defaultValue string) string {

	// Get file path
	viper.SetConfigFile("app.env")
	//read configs and handle errors
	err := viper.ReadInConfig()
	if err != nil {
		fmt.Println(err)
	}
	value := viper.GetString(key)
	if value != "" {
		return value
	}
	return defaultValue
}
func main() {
	// reading environments variable using the viper
	appEnv := getEnvValue("APP_ENV", "defaultEnvtesting")
	// not set in our app.env
	appVersion := getEnvValue("APP_VERSION", "1")
	dbPass := getEnvValue("DB_PASS", "1234")
	dbUser := getEnvValue("DB_USER", "goLinux_DB")
	serverAddress := getEnvValue("SERVER_ADDRESS", "127.0.0.1:8080")

	fmt.Printf(" ------%s-----\n", "Reading Environment variables Using Viper package")
	fmt.Printf(" %s = %s \n", "Application_Environment", appEnv)
	fmt.Printf(" %s = %s \n", "Application_Version", appVersion)
	fmt.Printf(" %s = %s \n", "Server_Listening_Address", serverAddress)
	fmt.Printf(" %s = %s \n", "Database_User_Name", dbUser)
	fmt.Printf(" %s = %s \n", "Database_User_Password", dbPass)

}

Output:

$ go run main.go
 ---Reading Environment variables Using Viper package--------
 Application_Environment = development 
 Application_Version = 1 
 Server_Listening_Address = 0.0.0.0:8080 
 Database_User_Name = postgres 
 Database_User_Password = pass

 

Method-2: Use GoDotEnv package to read environment variable from file

2.1 Install godotenv package

GoDotEnv package works similar to viper package above, however it loads the app.env file using godotenv.Load() function which accepts a file name input and returns value within the context of application.

 

2.2 Install godotenv package in the project

Read more about the godotenv package.

All our packages are stored into go.mod so godotenv will be added there. The package provide us with Load() method which allow us to pass the file name i.e godotenv.Load("app.env"), in some case if we use .env and is located at the root directory of the project then it can load that file without being passed as argument. i.e godotenv.Load().

 

2.3 Importance of godoten package

It support management of multiple environments at a once. i.e _ = godotenv.Load("app.env", "k8s_app.env")

It supports YAML file type input:
// app.YAML
APP_ENV: Development
SERVER_ADDRESS: 0.0.0.0:8080

Install godotenv package using below command

$ go get github.com/joho/godotenv

 

Example of using godotenv in Go

// main.go
package main

import (
	"fmt"
	"log"
	"os"

	"github.com/joho/godotenv"
)

// Function to read an environment or return a default value
func getEnvValue(key string, defaultValue string) string {
	if value, ok := os.LookupEnv(key); ok && value != "" {
		return value
	}
	return defaultValue
}

func main() {
	// load app.env file
	err := godotenv.Load("app.env")
	//handle errors
	if err != nil {
		log.Fatalf("can't load environment app.env: %v", err)
	}

	// reading environments variable from the app context
	appEnv := getEnvValue("APP_ENV", "defaultEnvtesting")

	// not defined in our app.env
	appVersion := getEnvValue("APP_VERSION", "1")
	dbPass := getEnvValue("DB_PASS", "1234")

	// DB_NAME not defined in app env
	dbName := getEnvValue("DB_NAME", "goLinux_DB")
	dbUser := getEnvValue("DB_USER", "goLinux_DB")
	serverAddress := getEnvValue("SERVER_ADDRESS", "127.0.0.1:8080")

	fmt.Printf(" ----%s---\n", "Reading Environment variables Using GoDotEnv package ")
	fmt.Printf(" %s = %s \n", "Application_Environment", appEnv)
	fmt.Printf(" %s = %s \n", "Application_Version", appVersion)
	fmt.Printf(" %s = %s \n", "Server_Listening_Address", serverAddress)
	fmt.Printf(" %s = %s \n", "Database_User_Name", dbUser)
	fmt.Printf(" %s = %s \n", "Database_User_Password", dbPass)
	fmt.Printf(" %s = %s \n", "Database_Name", dbName)

}

Output:

$ go run main.go   
              
 -----Reading Environment variables Using GoDotEnv package-----
 Application_Environment = development 
 Application_Version = 1 
 Server_Listening_Address = 0.0.0.0:8080 
 Database_User_Name = postgres 
 Database_User_Password = pass 
 Database_Name = goLinux_DB

 

Method-3: Use os package to set or read system environment variables

3.1 Set environment variable using os.Setenv()

os.Setenv(key,value) accepts two inputs a key and value to set the environment variable. Here is the syntax to use os.Setenv():

err := os.Setenv(key, value)
if err != nil {
	fmt.Printf("error will setting the environment value: %s", err)
}

For example to set an environment variable COLOUR=BLUE, we can use:

err := os.Setenv(COLOUR, BLUE)
if err != nil {
	fmt.Printf("error will setting the environment value: %s", err)
}

 

3.2 Read system environment variable using os.Getenv()

The os.Getenv() accepts single key argument to fetch the key variable value. For example to access the value of PATH variable from the system:

// main.go
package main

import (
	"fmt"
	"os"
)

func main() {
	fmt.Println("supported paths in my shell: ", os.Getenv("PATH"))
}

 

3.3 Read system environment variable using os.LookupEnv()

The downside of os.Getenv is that is just returns the key with no error. Now you may add an extra check whether the key exists using another if condition but os.LookEnv() also returns a boolean which can help you know whether the key value was fetched or not.

// main.go
package main

import (
	"fmt"
	"log"
	"os"
)

func main() {

	path, ok := os.LookupEnv("PATH123")
	if !ok {
		log.Fatalln(path + " variable is not defined")
	}
	fmt.Println(path)
}

 

4.4 Unset an environment variable using os.UnsetEnv()

We can use os.UnsetEnv() to remove or undefine any system environment variable. Now this would unset a variable temporarily for the current terminal only.

// main.go
package main

import (
	"fmt"
	"os"
)

func main() {
	// Before unsetting the variable
	out, _ := os.LookupEnv("MYUSER")
	fmt.Println("BEFORE: " + out)

	// Unset the variable
	os.Unsetenv("MYUSER")

	// After unsetting the variable
	out, _ = os.LookupEnv("MYUSER")
	fmt.Println("AFTER: " + out)

}

Output:

$ go run main.go 
BEFORE: Admin
AFTER:

 

Summary

In this article we have learned on how we can manage environment variable using viper,godotenv and os packages, with appropriet examples provide, you can learn more in the links provided in the reference area.

 

References

Viper
GodotEnv

 

Related Keywords: golang setup environment variables, golang environment variables, golang get environment variables, go environment variables, golang set environment variable, golang read environment variables, golang get env variable, get env variable golang, os.getenv golang, go os getenv, go run with environment variables, golang os.getenv, env golang, golang unset variable, go environment, go env file, golang env file

Deepak Prasad

Deepak Prasad

He is the founder of GoLinuxCloud and brings over a decade of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels in various domains, from development to DevOps, Networking, and Security, ensuring robust and efficient solutions for diverse projects. You can connect with him on his LinkedIn profile.

Can't find what you're searching for? Let us assist you.

Enter your query below, and we'll provide instant results tailored to your needs.

If my articles on GoLinuxCloud has helped you, kindly consider buying me a coffee as a token of appreciation.

Buy GoLinuxCloud a Coffee

For any other feedbacks or questions you can send mail to admin@golinuxcloud.com

Thank You for your support!!

Leave a Comment