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.
- 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
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