Table of Contents
Introduction
If simple string values need to be passed to a Java application and they do not contain sensitive data, then environment variables may be an option.
Use Case
Non-sensitive data needs to be looked up during the deployment, and is therefore, not a good fit for a configuration file.
- Infrastructure as code (Terraform, Ansible, etc.) needs to query a back-end to retrieve:
- Database or other back-end endpoint that doesn’t follow some predefined domain name format consistently,
- Settings that change outside our application’s deployments, like:
- Testing users / applications ID
- Availability status of optional back-ends
- Configuration file path or name
- Etc.
Anti-Patterns
Complex Structures
Environment variables must contain Strings. Complex structures are not well suited to environment variables. For example, a YAML file might be a better fit.
Too Many Environment Variables
Having lots of environment variables can make finding issues more difficult. Especially if more than one environment variable can produce similar undesirable behavior in an application, spotting which environment variable is to blame may be difficult.
This problem can be doubly hard if the way the environment variables are being defined doesn’t prevent duplicate declarations.
Not Part if Infrastructure as Code / SCM
For both troubleshooting perspective and disaster recovery needs, if environment variables are not created & updated using infrastructure as code deployments, then they should be used with caution.
Setting up environment variable values for an environment should not require tribal knowledge or reference outdated documentation. If all servers in a cluster went down, how quickly could your team recover the expected names and values?
Sensitive Values
How are the environment variables being sent to your application, are their values:
- Present in the source code (infrastructure or application repos)
- Available to anyone logged into the server?
- Displayed unmasked in the server configuration?
- Logged during process startup?
Something else to ask yourself:
- Could a disgruntled team member gain access to resources or mask their own malicious actions using the environment variable values? How many people have access to this secret?
Use caution when storing sensitive values as environment variables. Look into alternative, secret-specific storage mechanisms.
For example, GitHub Actions has separate handling for secrets used during those builds/deployments.
Testing Considerations
Java assumes that the environment will be setup before the process is started and remain consistent while running the process.
While it is possible (although in a hacky-way) to vary environment variable values during testing, it is not as common and can be a bit problematic:
- Test performance impacts, as modifying environment variables during a test means that is not thread safe and cannot be run in parallel with other tests,
- The environment variable value update within a running JVM may not take effect immediately or at all, which may lead to flaky tests, even if not run in parallel.
Instead, consider configuration files, which are often marshaled into plain old Java objects (POJOs) and can often vary between tests in a parallel execution.
Also, you can write unit tests that confirm the business logic for the values within your configuration files, so that testing the configuration file values can shift left – into the build.
I am not going into how the hacky-approach may update Java environment variables, as it really isn’t expected for a running Java process.
Java Syntax
To retrieve an environment variable in Java, simply:
String javaHome = System.getenv("JAVA_HOME");
This retrieves the JAVA_HOME environment variable.
public static String getenv(String name)
Gets the value of the specified environment variable. An environment variable is a system-dependent external named value.
If a security manager exists, its
checkPermissionmethod is called with aRuntimePermission("getenv."+name)permission. This may result in aSecurityExceptionbeing thrown. If no exception is thrown the value of the variablenameis returned.System properties and environment variables are both conceptually mappings between names and values. Both mechanisms can be used to pass user-defined information to a Java process. Environment variables have a more global effect, because they are visible to all descendants of the process which defines them, not just the immediate Java subprocess. They can have subtly different semantics, such as case insensitivity, on different operating systems. For these reasons, environment variables are more likely to have unintended side effects. It is best to use system properties where possible. Environment variables should be used when a global effect is desired, or when an external system interface requires an environment variable (such as
PATH).On UNIX systems the alphabetic case of
nameis typically significant, while on Microsoft Windows systems it is typically not. For example, the expressionSystem.getenv("FOO").equals(System.getenv("foo"))is likely to be true on Microsoft Windows.Parameters:
name– the name of the environment variableReturns: the string value of the variable, or
nullif the variable is not defined in the system environmentThrows:
NullPointerException– ifnameisnull
SecurityException– if a security manager exists and itscheckPermissionmethod doesn’t allow access to the environment variablenameSee Also:
https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/System.html#getenv(java.lang.String)
The Javadoc above talks about how environment variables are expected to be globally available. Be aware that any processes that are spawned also receive a copy of the environment variables.
Checking State
In addition to the possible exceptions above, remember to sanity check the value of the variable after it is retrieved.
- Is it null or populated?
- Does it match the expected format/valid values?
- etc.
Summary
Environment variables can be used for simple, non-sensitive configuration in Java. However, they should still be part of version control, such as via infrastructure as code. Over use, lack of version control, poor documentation or accidentally overwriting environment variables can lead to issues while troubleshooting undesirable behavior or in disaster recovery scenarios.

Leave a comment