在 systemd 的 unit 配置文件(如 `.service` 文件)中,`[Service]` 部分的 `WorkingDirectory` 指令是一个关键配置项,它定义了服务进程运行时的工作目录。以下是详细解释和配置指南:
---
### 一、`WorkingDirectory` 的作用
#### 1. **定义执行上下文**
- 设置服务进程启动时的工作目录(相当于在 shell 中先执行 `cd` 命令)
- 影响相对路径的解析(如 `./file.txt` 会基于此目录)
#### 2. **影响文件访问**
- 服务读取/写入的文件默认位于此目录
- 日志文件、临时文件等会在此目录生成(除非指定绝对路径)
#### 3. **模块加载路径**
- 对于 Node.js/Python 等解释型语言,影响 `node_modules` 或 `sys.path` 的查找
- 决定 `require()` 或 `import` 的基准路径
#### 4. **安全性**
- 限制服务可访问的文件系统范围
- 结合 `ProtectSystem` 等指令实现沙盒隔离
#### 5. **依赖管理**
- 确保配置文件(如 `.env`)能被正确找到
- 避免因路径错误导致的启动失败
---
### 二、配置语法和规则
#### 基础配置
```ini
[Service]
WorkingDirectory=/path/to/directory
```
#### 配置规则:
1. **必须使用绝对路径**(不能用 `~` 或相对路径)
2. **目录必须存在**(否则服务启动失败)
3. **运行用户需有访问权限**(否则报 `Permission denied`)
4. 支持变量扩展(如 `%h` 表示用户家目录)
---
### 三、配置示例
#### 示例 1:基本用法
```ini
[Service]
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/node app.js
```
此时 `app.js` 会从 `/opt/myapp/app.js` 加载
#### 示例 2:使用变量
```ini
[Service]
User=appuser
WorkingDirectory=/home/%u/app # %u 会被替换为用户名 (appuser)
ExecStart=/usr/bin/npm start
```
#### 示例 3:Node.js 项目
```ini
[Service]
WorkingDirectory=/srv/node-red
ExecStart=/usr/bin/node-red
Restart=always
```
#### 示例 4:Python 应用
```ini
[Service]
WorkingDirectory=/var/lib/myapp
ExecStart=/usr/bin/python3 main.py
```
---
### 四、验证配置的方法
#### 1. 检查服务状态
```bash
systemctl status your-service.service
```
若目录不存在会显示:
`error: chdir(/path) failed: No such file or directory`
#### 2. 查看进程工作目录
```bash
# 获取主进程 PID
sudo systemctl show your-service --property MainPID
# 查看实际工作目录
sudo ls -l /proc/<PID>/cwd
```
#### 3. 测试目录权限
```bash
# 切换到服务用户测试
sudo -u service-user ls /path/to/directory
```
---
### 五、常见问题解决方案
#### 问题 1:目录不存在
```bash
sudo mkdir -p /opt/myapp
sudo chown service-user:service-group /opt/myapp
```
#### 问题 2:权限不足
```bash
# 递归修改所有权
sudo chown -R service-user:service-group /opt/myapp
# 添加执行权限
sudo chmod 755 /opt/myapp
```
#### 问题 3:路径中有变量未展开
```ini
# 错误配置
WorkingDirectory=~/app # 不支持 ~
# 正确配置
WorkingDirectory=/home/%u/app
```
#### 问题 4:依赖文件找不到
```ini
[Service]
# 添加环境变量(如 Node.js 项目)
Environment="NODE_PATH=/opt/myapp/node_modules"
WorkingDirectory=/opt/myapp
```
---
### 六、最佳实践建议
1. **避免使用 `/root` 或 `/home`**
```ini
# 推荐
WorkingDirectory=/opt/myapp
# 不推荐
WorkingDirectory=/root/myapp
```
2. **配合用户权限**
```ini
[Service]
User=appuser
Group=appgroup
WorkingDirectory=/opt/myapp # 确保 appuser 有权限
```
3. **结合安全限制**
```ini
WorkingDirectory=/opt/myapp
ProtectHome=true # 屏蔽 /home、/root、/run/user
ProtectSystem=strict # 只允许写 WorkingDirectory
ReadWritePaths=/opt/myapp/data
```
4. **多目录项目**
```ini
WorkingDirectory=/opt/main-app
# 通过环境变量指定子目录
Environment="CONFIG_DIR=/opt/main-app/config"
```
5. **动态路径(高级)**
```ini
# 使用 ExecStartPre 创建目录
ExecStartPre=/bin/mkdir -p /run/myapp
WorkingDirectory=/run/myapp
```
---
### 七、与其他指令的关联
| 指令 | 配合作用 | 示例 |
|------|----------|------|
| `User` | 指定运行用户 | `User=node-red` |
| `Environment` | 设置环境变量 | `Environment="PORT=1880"` |
| `ProtectHome` | 限制访问用户目录 | `ProtectHome=true` |
| `ReadWritePaths` | 允许写入的路径 | `ReadWritePaths=/var/log/myapp` |
| `ExecStartPre` | 启动前命令 | 创建目录/文件 |
---
### 八、完整示例:Node-RED 服务
```ini
[Unit]
Description=Node-RED Service
After=network.target
[Service]
User=node-red
Group=node-red
WorkingDirectory=/opt/node-red # 核心配置
Environment="NODE_OPTIONS=--max_old_space_size=256"
ExecStart=/usr/bin/node-red
Restart=on-failure
RestartSec=10s
# 安全设置
NoNewPrivileges=yes
ProtectSystem=strict
PrivateTmp=yes
ReadWritePaths=/opt/node-red/data
[Install]
WantedBy=multi-user.target
```
**配置步骤**:
```bash
# 创建目录并授权
sudo mkdir -p /opt/node-red/data
sudo useradd -r -s /bin/false node-red
sudo chown -R node-red:node-red /opt/node-red
# 部署配置
sudo cp node-red.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl start node-red
```
---
### 九、注意事项
1. **路径结尾不要加 `/`**
`WorkingDirectory=/opt/app`(正确)
`WorkingDirectory=/opt/app/`(可能导致路径解析问题)
2. **符号链接处理**
systemd 会解析符号链接到实际路径(使用 `readlink -f` 查看真实路径)
3. **临时目录场景**
若使用 `/tmp` 需设置:
`PrivateTmp=true`(为服务创建私有 /tmp)
通过合理配置 `WorkingDirectory`,可以确保服务在预期的文件系统上下文中稳定运行,避免因路径问题导致的启动失败或运行时错误。