Best Practices for Sourcing Environment Variables in Cron Jobs for Java Applications


9 views

When running scripts through cron, you immediately encounter a fundamental difference from interactive shell sessions - cron jobs execute in a minimal environment without loading your normal shell initialization files like .bashrc or .bash_profile. This becomes particularly problematic when your scripts depend on environment variables defined in these files.

The two solutions you've considered each have significant drawbacks:

# Approach 1: Inline sourcing in crontab
* * * * * /bin/bash -c '. /home/user/env && /path/to/script'

# Approach 2: Source in every script
#!/bin/bash
source ~/user.env
# rest of script

The first approach makes your crontab messy and harder to maintain. The second creates duplication and makes it difficult to change environment variable locations later.

Option 1: Centralized Environment Loader

Create a dedicated environment loader script that all your cron jobs can use:

#!/bin/bash
# /usr/local/bin/load_env.sh

# Fail immediately if env file is missing
ENV_FILE="${HOME}/$(whoami).env"
if [ ! -f "${ENV_FILE}" ]; then
    echo "Error: Environment file ${ENV_FILE} not found" >&2
    exit 1
fi

# Source the environment file
set -o allexport
source "${ENV_FILE}"
set +o allexport

Then in your crontab:

* * * * * /usr/local/bin/load_env.sh && /path/to/your/script.sh

Option 2: System-wide Environment File

For server applications, consider using /etc/environment (on systemd systems):

# /etc/environment
JAVA_HOME=/usr/lib/jvm/java-11-openjdk
APP_HOME=/opt/myapp

These variables will be available to all processes, including cron jobs.

Option 3: Wrapper Script Solution

Create a wrapper that maintains your environment:

#!/bin/bash
# /usr/local/bin/run_with_env

if [ $# -lt 1 ]; then
    echo "Usage: $0 command [args...]" >&2
    exit 1
fi

# Load environment
source "${HOME}/$(whoami).env"

# Execute command with original arguments
exec "$@"

Crontab entry:

* * * * * /usr/local/bin/run_with_env /path/to/your/script.sh param1 param2

For Java server apps specifically, you might want to:

  1. Set JAVA_HOME explicitly in your environment file
  2. Include JVM options like memory settings
  3. Handle logging directory paths

Example environment file for Java:

# ~/java_app.env
export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64
export PATH="${JAVA_HOME}/bin:${PATH}"
export APP_OPTS="-Xmx2G -Xms1G -Dlog.dir=/var/log/myapp"

When running Java server applications through cron jobs, we often face environment variable loading issues because cron executes commands in a minimal environment. Unlike interactive shell sessions that load .bashrc or .bash_profile, cron jobs don't inherit these user-specific configurations.

While you could add source ~/user.env at the beginning of every script, this creates maintenance headaches:

# Bad practice: Hardcoding paths in multiple scripts
source /home/myuser/myuser.env
# Rest of script logic...

This approach violates the DRY principle and makes environment management fragile across different deployment environments.

1. Centralized Environment Wrapper

Create a wrapper script that handles environment loading:

#!/bin/bash
# /usr/local/bin/run_with_env
set -e
source "${HOME}/${USER}.env"
exec "$@"

Then in crontab:

* * * * 1-5 /usr/local/bin/run_with_env /home/myuser/scripts/myscript.sh

2. Systemd Service Units (Modern Linux)

For long-running Java services, consider systemd:

# /etc/systemd/system/myservice.service
[Unit]
Description=My Java Service

[Service]
User=myuser
EnvironmentFile=/home/myuser/myuser.env
ExecStart=/home/myuser/scripts/myscript.sh
Restart=on-failure

[Install]
WantedBy=multi-user.target

3. Environment Directory Pattern

Create an env.d directory for modular configuration:

# In your script:
for f in ~/env.d/*.env; do
    [[ -f "$f" ]] && source "$f"
done

For different environments (dev/stage/prod), use symbolic links:

ln -sf /etc/myapp/env/prod.env /home/deploy/.deploy.env
# Then source ~/.deploy.env in scripts

Always:

  • Set proper permissions on env files (600)
  • Never source files from world-writable directories
  • Consider using /etc/environment for system-wide vars

Capture the actual environment:

* * * * * /usr/bin/env > /tmp/cronenv.log 2>&1