EnvironmentFile是systemd加载环境变量的外部配置方式,仅支持纯KEY=VALUE文本解析,不展开变量、不执行shell语法,路径须为绝对且可读,变量不可用于ExecStart直接插值。

EnvironmentFile 是 systemd 读取环境变量的“外部配置入口”,但它不会自动展开变量、不支持嵌套引用、也不继承 shell 的行为。直接在 ExecStart 中用 ${VAR} 是无效的——systemd 只在 Environment= 或 EnvironmentFile= 加载后,才把变量注入进程环境,而 ExecStart 命令行本身**不经过 shell 解析**。
EnvironmentFile 路径必须绝对且可读
systemd 不会帮你补全路径或 fallback 到 home 目录。常见错误是写成 EnvironmentFile=env.conf 或 EnvironmentFile=./env.conf,这会导致启动失败并报 Failed at step EXEC spawning 或静默忽略(加 - 前缀时)。
- ✅ 正确写法:
EnvironmentFile=/etc/myapp/env.conf - ✅ 可选写法(文件不存在也不报错):
EnvironmentFile=-/etc/myapp/env.conf - ❌ 错误写法:
EnvironmentFile=env.conf、EnvironmentFile=~/env.conf、EnvironmentFile=$HOME/env.conf - 确保服务用户(如
User=myapp)对文件有读权限,否则变量不会加载,且日志里可能只显示Cannot read environment file,不提示具体路径问题
EnvironmentFile 中的变量不能在 ExecStart 里用 ${} 直接插值
你不能在 ExecStart 行里写 ExecStart=/usr/bin/java -jar ${JAR_FILE} 并指望它工作——systemd 的命令行解析器不支持这种 shell 风格的变量展开。它只做字面量拼接。
- ✅ 正确做法:用
Environment=显式声明,或确保变量已通过EnvironmentFile加载,并改用sh -c包裹(但需谨慎):
EnvironmentFile=/etc/myapp/env.conf ExecStart=/bin/sh -c '/usr/bin/java -jar "$JAR_FILE" --port "$APP_PORT"'
sh -c 启动的是子 shell,$MAINPID 将指向 sh 进程而非 java 进程,影响 ExecStop 和健康检查Environment=,或让 Java 应用自己读取 System.getenv("JAR_FILE") 等,避免在 systemd 层做逻辑JAVA_HOME 和 PATH 在 EnvironmentFile 中不生效于 java 命令查找
EnvironmentFile 设置的 JAVA_HOME 或 PATH 只影响最终启动的进程环境,**不影响 systemd 自身查找 ExecStart 中的可执行文件路径**。也就是说,即使你在 env.conf 里写了 PATH=/opt/jdk/bin:$PATH,systemd 仍会按系统默认 PATH 查找 java。
- ✅ 正确做法:在
ExecStart中写死 java 路径,例如/usr/lib/jvm/java-11-openjdk-amd64/bin/java - ✅ 或统一用
Environment=JAVA_HOME=...+ExecStart=${JAVA_HOME}/bin/java ...——注意:这个${JAVA_HOME}是 systemd 的变量展开语法,仅适用于Environment=定义的变量,不适用于EnvironmentFile里的内容 - ❌ 错误假设:“我设了 PATH,systemd 就能自动找到 java” → 不成立
真正容易被忽略的一点是:systemd 加载 EnvironmentFile 是纯文本解析,不执行任何 shell 逻辑,所以 export、if、命令替换 $(...)、甚至未引号的空格分隔值都会出错。它只认 KEY=VALUE 行,且 VALUE 中的空格必须用引号包裹或转义。一旦格式不对,整行失效,还很难从日志定位——systemd 默认不打印解析失败详情,得用 systemctl show myapp.service --property=Environment 或 journalctl -u myapp -o cat 结合 strace 观察实际传给进程的环境。


















