# 用 systemd 托管一个自写服务 ## 场景 脚本或程序要开机自启、挂了自动拉起、日志能查。别再写 `nohup &` 了。 ## 前置条件 - 程序有确定的启动命令和运行目录 - 用独立用户跑,不要用 root - 知道它会写哪些目录 ## 配置 ```bash # 建用户 useradd -r -s /usr/sbin/nologin appuser # 单元文件 cat >/etc/systemd/system/myapp.service <<'EOF' [Unit] Description=My App After=network-online.target Wants=network-online.target [Service] Type=simple User=appuser Group=appuser WorkingDirectory=/opt/myapp Environment="TZ=Asia/Shanghai" EnvironmentFile=-/opt/myapp/.env ExecStart=/opt/myapp/venv/bin/python /opt/myapp/main.py Restart=always RestartSec=3 LimitNOFILE=65535 StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable --now myapp ``` ## 验证 ```bash systemctl status myapp journalctl -u myapp -n 100 --no-pager journalctl -u myapp -f systemctl restart myapp ``` 杀进程测试自动拉起: ```bash pkill -f main.py && sleep 4 && systemctl is-active myapp ``` ## 回滚 ```bash systemctl disable --now myapp rm /etc/systemd/system/myapp.service systemctl daemon-reload ``` ## 坑 - **改了 unit 文件必须 `daemon-reload`**,否则 `start` 还是旧配置。 - `Type=forking` 要配 PIDFile,配错会一直 `activating`。 - `EnvironmentFile` 里的值不要加引号外层再加引号,会原样传进去。 - 程序自己要写日志文件时,注意目录属主是 `appuser`。 - `Restart=always` 遇到配置错误会无限重启,看 `journalctl` 里的报错再修。 - 不要用 `systemctl edit` 之后忘记 `daemon-reload`,这个坑踩过不止一次。