Working with Environment Variables in Java

October 11, 2021
Written by
Reviewed by

Environment variables are a great way to configure Java applications without having to explicitly store settings in code, such as for database and caching servers, or for third-party APIs.

Keeping such settings outside of the code has several distinct advantages:

  • Avoids the need to update and recompile code when settings change
  • Helps prevent exposing sensitive credentials, such as usernames and passwords, and deployment tokens
  • You can deploy the same code in multiple environments

In this short article, I'm going to show you some of the ways of working with environment variables in Java.

How to access environment variables in Java

One of the most common ways is to use System.getenv(), which accepts an optional String argument. Based on whether a String argument is passed, a different value is returned from the method. Specifically:

If a String is passed and it matches a key in the internal environment Map, then its value is returned. If a matching key is not found, null is returned. If a String argument is not passed, a read-only java.util.Map containing all environment variables and their values is returned. The Map's keys are the environment variable names and its values are the values.

Keep in mind that different platforms operate in different ways, e.g., on UNIX, Linux, and macOS, environment variables are case-sensitive, whereas on Microsoft Windows they are not.

Below, you can see an example of how to use the method to retrieve the Linux SHELL environment variable (which contains the user's shell).

package com.settermjd.twilio.envvars;

public class Main {
    public static void main(String[] args) {
        System.out.println(
            String.format("The current shell is: %s.", System.getenv("SHELL"))
        );
    }
}

If you want to supply a default value, call System.genenv() as follows:

System.getenv().getOrDefault("SHELL", "/bin/bash")

How to set environment variables

As well as reading environment variables, it's helpful to know how to set them. I won't go into too much detail, instead sticking to the essentials. However, if you'd like to learn about them in greater depth, Dominik Kundel has written a detailed blog post about them.

UNIX, Linux, and macOS

On UNIX, Linux, and macOS, environment variables can be set in three core ways.

Available to the current session and all child processes

You can initialise environment variables so that they're available to the current session, both the current one and any started in the current session, as well as any processes started in the current session, by using the builtin export command, as in the example below.

export USER_ID=1

Setting an environment variable this way isn't permanent. If you want to permanently set an environment variable, you need to set it in either the system-wide startup file, /etc/profile, or one of the user-specific startup files, i.e., ~/.bash_profile, ~/.bash_login, and ~/.profile.

Available to the current session

You can initialise an environment variable so that it's available to the current session but not any child processes, as in the example below, by not using the export command.

USER_ID=1

Available to a specific process

Finally, you can initialise an environment variable so that it's available only to a specific process. This is helpful when only that process needs the variable. You initialise it as in the example below, when starting the desired process.

USER_ID=1 retrieveUserDetails

Microsoft Windows

Setting environment variables is a little different in Microsoft Windows. You can either set them via the Control Panel, or you can set them in the command prompt or PowerShell console. There are examples of the latter two below.

# Set USER_ID in the Windows Command Prompt
set USER_ID=1

# Set USER_ID in the Windows PowerShell console
$Env:USER_ID = 1

Using .env files

While setting environment variables this way can be very effective, it can also become cumbersome rather quickly. For example, by setting them in the operating system's shell, there is no concrete list of the variables which the application needs, nor is there information available about what a variable is for or what data type it must be.

Given that, among other reasons, The 12-Factor App recommended a strict separation of config from code. This practice quickly took hold throughout the developer community commonly through the use of .env (dotenv) files. These are plain text files that store a list of key/value pairs, defining the environment variables required for an application to work, as in the example below.

USER_ID=1
TWILIO_AUTH_TOKEN=1234567890987654321

To simplify getting started on a project, a further common practice emerged, that of including a .env.example file in a project which contains all of the keys—but without their values. When a new developer started working on the application, they would copy the file, naming it .env and set values for each key applicable to their local development environment.

Reading .env files

Assuming that the example above was the .env file for our project, we could use a package such as dotenv-java to make the variables available to our application.

If you're developing in Kotlin, you can use dotenv-kotlin.

You can see an example of using the package in the code example below.

hl_lines="3,4,8,9,12"
package com.settermjd.twilio.envvars;

import io.github.cdimascio.dotenv.Dotenv;
import io.github.cdimascio.dotenv.DotenvException;

public class Main {
    public static void main(String[] args) {
        Dotenv dotenv = null;
        dotenv = Dotenv.configure().load();
        System.out.println(String.format(
            "Hello World. Shell is: %s. Name is: %s",
            System.getenv("SHELL"),
            dotenv.get("NAME")
        ));
    }
}

The code initialises a new Dotenv object, dotenv, and calls Dotenv.configure().load() to read in the environment variables in .env, located in the project's root directory. Following that, dotenv.get() is used to retrieve the value of the String object passed to it. If a matching key is not found, the method returns null.

 

You can supply a default value as the second argument to the method if desired.

A note about .env file security

It's important to bear in mind that if sensitive data is stored in .env, that it must not be stored under version control. If that happens, then all of the security benefits of using dotenv files is lost. That's why it's common to exclude them from version control, such as by adding .env (and variations of the filename) to a project's .gitignore file when using Git.

That's how to work with environment variables in Java

I hope this article helped you understand how to use environment variables in your Java projects.

Do you have any other ways to work with environment variables? I’d love to know!

Matthew Setter is a PHP Editor in the Twilio Voices team and a polyglot developer. He’s also the author of Mezzio Essentials and Docker Essentials. When he’s not writing PHP code, he’s editing great PHP articles here at Twilio. You can find him at msetter@twilio.com; he's also settermjd on Twitter and GitHub.