Linuxサーバーを本番運用していると、この壁に必ずぶつかります。原因は「limits.confはsystemdサービスには適用されない」という、意外と知られていない仕様にあります。limits.confはPAM経由のログインセッション向けの設定であり、systemdが起動するサービスプロセスは別の経路でリソース制限を受け継ぐためです。
この記事では、RHEL 9系~Rocky Linux 9/Ubuntu 24.04 LTSを想定環境として、Linuxのファイルディスクリプタ上限を構成する3層(カーネル全体の上限・PAMセッション制限・systemdサービス制限)の仕組みを解説し、サービスごとにLimitNOFILEをdrop-inで正しく設定する手順を説明します。設定反映の確認方法と、設定後も変わらない場合のトラブルシュートまでカバーします。
この記事のポイント
・systemdサービスはlimits.confを読まないため、LimitNOFILEをUnit設定で指定する必要がある
・/proc/<PID>/limitsで稼働中プロセスの実効値を直接確認できる
・drop-inファイル(/etc/systemd/system/サービス名.service.d/)でサービス単位に永続設定する
・全サービスのデフォルトを変えるにはsystem.confのDefaultLimitNOFILEを使う
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
なぜ「limits.confを変えてもサービスに効かない」のか
「/etc/security/limits.conf」にOpen Filesの上限を書いて再ログインしたら、シェルで「ulimit -n」を実行すると確かに変わっている。なのにサービスを再起動してもエラーが出続ける。この問題の根本原因は、プロセスのリソース制限がどこから引き継がれるかにあります。
Linuxでは、プロセスが起動するとき、親プロセスのリソース制限(rlimit)をそのまま引き継ぎます。そしてsystemdが管理するサービスの親プロセスは、SSHログインセッションでも端末でもなく、systemd(PID 1)自身です。
「/etc/security/limits.conf」を読み込むのはPAM(Pluggable Authentication Modules)の「pam_limits.so」モジュールです。このモジュールはSSHログインやローカルコンソールログイン時に呼ばれます。一方、systemdはPAMを経由せずサービスプロセスを直接生成するため、limits.confの設定は一切反映されません。
つまり構造的に、「limits.confを編集 → systemdサービスに反映される」というルートは存在しないのです。
Linuxのファイルディスクリプタ制限を構成する3層の仕組み
ファイルディスクリプタ(FD)の上限は3つの層で制御されています。それぞれの役割と確認方法を把握しておくことが、トラブルシュートの出発点になります。1. カーネル全体の上限 ─ /proc/sys/fs/file-max
システム全体で同時にオープンできるファイル数の絶対上限です。個々のプロセス設定より優先され、どのプロセスもこの値を超えられません。# カーネル全体の上限を確認する cat /proc/sys/fs/file-max # 出力例(環境により異なります) 9223372036854775807 # sysctl コマンドでも確認できる sysctl fs.file-max # fs.file-max = 9223372036854775807
永続的に変更する場合は「/etc/sysctl.d/」ディレクトリに設定ファイルを追加します。
# /etc/sysctl.d/99-filemax.conf に追記する例 echo "fs.file-max = 2097152" | sudo tee /etc/sysctl.d/99-filemax.conf sudo sysctl --system
2. PAMセッション制限 ─ /etc/security/limits.conf
SSHや端末ログインなど、PAMを経由した対話的セッション向けのリソース制限です。設定形式は以下の通りです。# /etc/security/limits.conf の書式 # ドメイン タイプ 項目 値 * soft nofile 65536 * hard nofile 65536 elasticsearch soft nofile 1048576 elasticsearch hard nofile 1048576
・「hard」はソフト上限の上限(rootのみ変更可能)
・「*」はすべてのユーザーに適用するワイルドカード
【注意】前述の通り、この設定はsystemdが起動するサービスプロセスには効きません。SSHで接続したユーザーの「ulimit -n」には反映されますが、Elasticsearch・PostgreSQL・Nginxなどのサービスには届かない設定です。
3. systemdサービスの制限 ─ LimitNOFILE
systemdが管理するサービスのリソース制限は、サービスユニットファイルの「[Service]」セクションで指定します。これがsystemdサービスに対してリソース制限を設定する唯一確実な方法です。# /lib/systemd/system/nginx.service の [Service] セクション抜粋例 [Service] LimitNOFILE=65535
Open Files上限に達しているか症状を確認する
設定変更の前に、まず本当にOpen Files上限が原因かどうかを確認します。別の問題を誤った方法で解決しようとすると運用の混乱につながります。1. エラーメッセージで確認する
Open Files上限に達したとき、アプリケーションのログには次のようなメッセージが現れます。# アプリケーションログに現れる典型的なエラー(例示) [ERROR] Too many open files [ERROR] java.io.IOException: Too many open files [WARN] accept() failed: (23) Too many open files in system
2. /proc/<PID>/limits で稼働中プロセスの実効値を確認する
稼働中のプロセスが実際にどのような上限で動いているかは「/proc/<PID>/limits」ファイルで確認できます。ここに表示される値が本当の実効値であり、設定変更が正しく反映されたかの最終確認に使います。# Elasticsearch プロセスの実効 limits を確認する sudo cat /proc/$(pgrep -o java)/limits # 出力例(環境により異なります) Limit Soft Limit Hard Limit Units Max cpu time unlimited unlimited seconds Max file size unlimited unlimited bytes Max data size unlimited unlimited bytes Max stack size 8388608 unlimited bytes Max core file size 0 unlimited bytes Max resident set unlimited unlimited bytes Max processes unlimited unlimited processes Max open files 65536 65536 files Max locked memory unlimited unlimited bytes Max address space unlimited unlimited bytes # Nginx の場合 sudo cat /proc/$(pgrep -o nginx)/limits | grep "Max open files" # 出力例(環境により異なります) Max open files 65535 65535 files
3. systemctl show でサービスの設定値を確認する
# systemd から見た nginx サービスの LimitNOFILE を確認する systemctl show nginx --property LimitNOFILE # 出力例(環境により異なります) LimitNOFILE=65535 # サービスの全プロパティを表示して LimitNOFILE を探す systemctl show nginx | grep Limit # 出力例(環境により異なります) LimitNOFILE=65535 LimitNOFILESoft=65535 LimitNPROC=infinity LimitMEMLOCK=65536
Linux管理スキルをより体系的に身につけたい方には、実践的なLinuxサーバー構築セミナーでトラブルシュートの思考の型を学ぶ方法もあります。
サービスごとにLimitNOFILEをdrop-inで設定する手順
1. drop-inファイルとは
「drop-in」とは、パッケージが提供する元のユニットファイルを変更せず、上書き設定を別ファイルで追加する仕組みです。ファイルは「/etc/systemd/system/<サービス名>.service.d/」ディレクトリに「*.conf」形式で置きます。パッケージ更新で元のユニットファイルが書き換わっても、drop-inは別ファイルなので影響を受けません。手動で作成する方法と、「systemctl edit」コマンドを使う方法があります。運用ではsystemctl editを使うとパスを間違えにくいのでお勧めです。
2. 手順 ─ Elasticsearchの場合
# systemctl edit を使って drop-in を作成する # エディタが開くので [Service] セクションを追記して保存する sudo systemctl edit elasticsearch # エディタに入力する内容(下2行だけ書いて保存する) # [Service] # LimitNOFILE=1048576 # 保存すると /etc/systemd/system/elasticsearch.service.d/override.conf が生成される # 生成されたファイルを確認する cat /etc/systemd/system/elasticsearch.service.d/override.conf # 出力例(環境により異なります) # [Service] # LimitNOFILE=1048576
3. 手順 ─ Nginxの場合
# Nginx の drop-in を作成する sudo systemctl edit nginx # エディタに入力する内容 # [Service] # LimitNOFILE=65535 # 手動でディレクトリとファイルを作成する場合は以下の方法でもよい sudo mkdir -p /etc/systemd/system/nginx.service.d sudo tee /etc/systemd/system/nginx.service.d/10-limits.conf << 'EOF' [Service] LimitNOFILE=65535 EOF
4. 設定を反映して確認する
drop-inを作成したら、systemdの設定を再読み込みしてサービスを再起動します。# systemd に drop-in の読み込みを指示する(必須) sudo systemctl daemon-reload # サービスを再起動する sudo systemctl restart nginx # /proc/PID/limits で実効値が変わったことを確認する sudo cat /proc/$(pgrep -o nginx)/limits | grep "Max open files" # 出力例(環境により異なります) Max open files 65535 65535 files
全サービスのデフォルト値を変更する ─ DefaultLimitNOFILE
ElasticsearchとNginxとPostgreSQLと…とサービスの数が多い場合、個別にdrop-inを書くのが煩雑になることがあります。そのような場合は「/etc/systemd/system.conf」の「DefaultLimitNOFILE」を変更することで、ユニットファイルにLimitNOFILEを書いていないすべてのサービスのデフォルト値を変更できます。# /etc/systemd/system.conf を編集する(コメントアウトを外して値を変更) sudo vi /etc/systemd/system.conf # 変更箇所(元は #DefaultLimitNOFILE= でコメントアウト) [Manager] DefaultLimitNOFILE=65536 # 変更後、systemd 自身を再起動して反映する(要注意: サービスが全て再起動される) sudo systemctl daemon-reexec # または OS を再起動して反映する sudo reboot
トラブルシュート ─ 設定後も上限が変わらない場合
1. サービスを再起動していない
daemon-reloadとサービスの再起動が両方必要です。daemon-reloadだけでは既存のプロセスには反映されません。「/proc/PID/limits」の値が変わっているかで判断します。# 確実に反映させる手順 sudo systemctl daemon-reload sudo systemctl restart <サービス名> sudo cat /proc/$(pgrep -o <プロセス名>)/limits | grep "Max open files"
2. drop-inファイルのパスが正しくない
「/etc/systemd/system/nginx.service.d/」のように、サービス名の後に「.service.d」が必要です。「nginx.d」や「nginx/」にしていると読み込まれません。systemctl editを使えばパスは自動生成されるため、この間違いを防げます。# drop-in が正しく読まれているか確認する systemctl cat nginx # 出力例(環境により異なります) # /lib/systemd/system/nginx.service [Unit] Description=A high performance web server ... # /etc/systemd/system/nginx.service.d/override.conf [Service] LimitNOFILE=65535
3. カーネル全体の上限に当たっている
「/proc/sys/fs/file-nr」で現在オープン中のFD数を確認します。# システム全体の FD 使用状況を確認する # 出力:割り当て済み / 解放済み / 最大値 cat /proc/sys/fs/file-nr # 出力例(環境により異なります) 2656 0 9223372036854775807 # 割り当て済みが最大値に近い場合は fs.file-max を上げる echo "fs.file-max = 2097152" | sudo tee /etc/sysctl.d/99-filemax.conf sudo sysctl --system
4. アプリケーション独自の設定が上書きしている
Elasticsearchは「/etc/elasticsearch/jvm.options」や「/etc/elasticsearch/elasticsearch.yml」に独自の設定を持ちます。systemdで上限を正しく設定できていても、アプリケーション側のworker数・接続プール・スレッド数の設定がFD消費量を増やしている場合もあります。FDの消費状況は「lsof -p <PID> | wc -l」で把握できます。本記事のまとめ
Linuxのファイルディスクリプタ上限は3層の仕組みで制御されており、systemdサービスに対してはLimitNOFILEをdrop-inで設定するのが正しい方法です。| 制限の層 | 設定場所 | 適用対象 | 確認方法 |
|---|---|---|---|
| カーネル全体 | /etc/sysctl.d/99-filemax.conf |
システム全体 | cat /proc/sys/fs/file-max |
| PAMセッション | /etc/security/limits.conf |
ログインセッション(SSH等) | ulimit -n |
| systemdサービス | drop-in(LimitNOFILE=) | systemd管理のサービス | cat /proc/<PID>/limits |
| 全サービスのデフォルト | /etc/systemd/system.conf |
LimitNOFILE未設定のサービス全て | systemctl show <svc> --property LimitNOFILE |
・drop-in作成後はdaemon-reloadとサービス再起動の両方が必要
・設定が反映されたかは必ず/proc/<PID>/limitsで確認する
・単純にfailed状態や「Too many open files in system」はカーネル全体の上限(fs.file-max)を疑う
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます