systemdのExecStart=の書き方|絶対パス・引数・リダイレクトで失敗する原因
ExecStart=はシェルへそのまま渡されるコマンド行ではありません。実行ファイル・引数・作業ディレクトリ・シェル構文を分けて考えると、起動失敗を減らせます。
このページで分かること
- ExecStart=の基本的な書き方
- 絶対パスと実行ファイル検索の考え方
- パイプ・リダイレクト・&&がそのまま使えない理由
- status=203/EXECが出たときの確認順
先に結論
ExecStart=では、実行ファイルと引数を明確に指定します。実行ファイルは絶対パスを使うと環境差を減らせます。パイプやリダイレクトなどが必要なら、目的に合うsystemdの設定を使うか、必要性を確認したうえで明示的にシェルを起動します。
ExecStart=を適用前にレビュー
Unitファイルを貼り付けて、実行パスやシェル構文など起動失敗につながる設定を確認できます。
ExecStart=の基本
ExecStart=は、サービス開始時に実行するプログラムと引数を指定します。たとえばPythonアプリを起動するなら、次のように実行ファイルとスクリプトを分けて書きます。
ここで最初の要素は実行するプログラム、その後ろはそのプログラムへ渡す引数です。普段の対話シェルで入力する「コマンド文字列」と完全に同じものとして扱わないことが重要です。
command -v python3やreadlink -fなどで実行ファイルの場所を確認し、サービスの実行ユーザーでもアクセスできるか確認します。実行ファイルは絶対パスが分かりやすい
systemdは単純な実行ファイル名を、コンパイル時に決められた固定の検索パスから解決できる場合があります。そのため「絶対パス以外は必ず失敗する」というわけではありません。
| 例 | 考え方 |
|---|---|
ExecStart=/usr/bin/python3 ... | どの実行ファイルを使うか明確。環境差を避けやすく、運用手順にも残しやすい。 |
ExecStart=python3 ... | 単純なファイル名ならsystemdの固定検索パスで解決される場合がある。ただしログインシェルのPATHやaliasを使う仕組みではない。 |
./appやbin/appのようにスラッシュを含む相対指定を、普段のシェルと同じ感覚で使わないようにします。実行場所やファイル配置を明示した方が安全です。引数と引用符は「シェルと同じ」と決めつけない
スペースを含む1つの引数は引用符でまとめられます。ただし、systemdのコマンド行解析には独自の規則があり、シェルの展開すべてが使えるわけではありません。
環境変数を使う場合も、対話シェルの設定ファイルやaliasは読み込まれません。必要な環境はEnvironment=やEnvironmentFile=などで明示する方が追跡しやすくなります。
パイプ・リダイレクト・&&はそのままシェル構文にならない
次のような書き方を「シェルで実行される」と考えるのは危険です。
|、>、>>、&&などを解釈するシェルが自動で挟まるわけではありません。
StandardOutput=・journal、事前処理ならExecStartPre=など、systemd側の機能で目的を表現できないか確認します。どうしてもシェル構文が必要なら、シェルを明示して実行します。
相対ファイルを使うならWorkingDirectory=も確認
実行ファイル自体だけでなく、アプリへ渡した相対パスも問題になります。アプリが./config.ymlを読む設計なら、想定する作業ディレクトリを明示する必要があります。
ただし、運用上は設定ファイルなども絶対パスで指定すると依存関係が見えやすくなります。
status=203/EXECが出たら実行そのものを確認
status=203/EXECは、systemdがプロセスを実行する段階で失敗したことを示します。設定内容だけでなく、実体・権限・ファイル形式まで確認します。
| 確認項目 | 確認例 |
|---|---|
| ファイルが存在するか | ls -l /opt/example/start.sh |
| 実行権限があるか | namei -l /opt/example/start.sh / test -x /opt/example/start.sh |
| 指定ユーザーが到達できるか | sudo -u appuser test -x /opt/example/start.sh |
| スクリプトのshebang | head -n 1 /opt/example/start.sh |
| Unitで実際に何を指定しているか | systemctl cat example.service |
複数のExecStart=はType=oneshotかを確認
通常の長時間動作するサービスでは、複数のExecStart=を並べて順番に処理する設計にはしません。複数コマンドを順に実行できるのは、主にType=oneshotの用途です。
長時間動作するデーモンで準備処理が必要なら、ExecStartPre=と本体のExecStart=を分けると意図が分かりやすくなります。
変更前・変更後の確認手順
- 実行ファイルと引数を手動で確認する
- Unitを保存したら
systemd-analyze verifyで構文や参照を確認する systemctl daemon-reloadでUnit変更を読み直すsystemctl start後にstatusとjournalを確認する- 再起動後も必要ならenable状態も別途確認する