Linux「Too many open files」エラーの原因と解決方法|ulimit・システム設定を徹底解説

Linux「Too many open files」エラーの原因と解決方法|ulimit・システム設定を徹底解説

Linuxサーバーやアプリケーションを運用していると、突然このエラーが出てサービスが止まった経験はないでしょうか。原因が分かれば対処は難しくありませんが、設定が複数の場所に分散していてどこを直せばいいか分からないというのがこのエラーのやっかいなところです。

本記事では以下の疑問に答えます。

  • そもそも「Too many open files」とは何か
  • 現在の上限値と使用状況をどう確認するか
  • 一時的・恒久的な解決方法はどれか
  • Nginx・Node.js・Java など個別アプリへの対処法
  • 再発を防ぐ監視・チューニング方法

目次

結論:先に答えを出す

急いで直したい場合は以下を実行してください。

# ① 現在の上限を確認
ulimit -n

# ② 現在のセッションだけ一時的に上限を上げる(再起動で元に戻る)
ulimit -n 65536

# ③ 恒久的に上げる(/etc/security/limits.conf に追記)
echo "* soft nofile 65536" | sudo tee -a /etc/security/limits.conf
echo "* hard nofile 65536" | sudo tee -a /etc/security/limits.conf

# ④ システム全体の上限も確認・変更
cat /proc/sys/fs/file-max
echo "fs.file-max = 2097152" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

詳細と各ミドルウェア別の対処法は以下で解説します。


「Too many open files」とは

ファイルディスクリプタとは

Linuxではファイル・ソケット・パイプなどあらゆるI/Oリソースを「ファイルディスクリプタ(fd: file descriptor)」として管理します。プロセスがファイルを開いたりネットワーク接続を確立したりするたびに、ファイルディスクリプタが1つ消費されます。

ファイルディスクリプタが使われるもの:
- 通常のファイル(テキスト・バイナリ)
- ソケット(TCP/UDP接続)
- パイプ(|)
- 標準入出力(stdin=0, stdout=1, stderr=2)
- デバイスファイル
- ディレクトリ(opendir)
- epoll / inotify などのカーネルオブジェクト

なぜ上限があるのか

無制限にファイルディスクリプタを開けるとメモリを食いつぶすため、Linuxはプロセスごと・ユーザーごと・システム全体にそれぞれ上限を設けています。この上限に達したときに出るのが Too many open files です。

エラーが出る典型的な状況

  • Webサーバー(Nginx・Apache)への急激なアクセス増加
  • アプリケーションのファイルディスクリプタリーク(開いたまま閉じない)
  • 接続数の多いデータベース(MySQL・PostgreSQL・Redis)
  • Node.js・Java などのサーバーサイドアプリ
  • ログのローテーション・大量ファイル処理

上限値の仕組み:3つのレイヤー

「Too many open files」を理解するには、上限値が3つのレイヤーで管理されていることを知る必要があります。

レイヤー1:システム全体の上限
  /proc/sys/fs/file-max
  → カーネル全体で同時に開けるファイルディスクリプタの総数

レイヤー2:ユーザーごとの上限
  /etc/security/limits.conf(PAM経由)
  → ユーザー単位の soft/hard 上限

レイヤー3:プロセスごとの上限
  ulimit -n(シェルセッション)
  systemd の LimitNOFILE(サービス単位)
  → 1プロセスが同時に開けるファイルディスクリプタ数

それぞれの上限の中で最も小さいものが有効になります。システム全体を上げてもプロセスの上限が低ければ意味がありませんし、逆もしかりです。


現在の状況を確認する

まず状況を把握することが重要です。

プロセスごとの上限を確認

# 現在のシェルセッションの上限
ulimit -n
# → 1024(デフォルト)

# soft と hard の上限を両方確認
ulimit -Sn   # soft limit
ulimit -Hn   # hard limit

# すべての ulimit 設定を一覧表示
ulimit -a

特定プロセスの上限と使用数を確認

# プロセスIDを調べる
pgrep nginx
pgrep node

# そのプロセスの fd 上限を確認
cat /proc/<PID>/limits | grep "open files"
# Max open files  65536  65536  files

# 現在開いている fd の数を確認
ls /proc/<PID>/fd | wc -l

# または lsof で確認
lsof -p <PID> | wc -l

# プロセス名で確認
lsof -c nginx | wc -l

システム全体の使用状況を確認

# システム全体の上限
cat /proc/sys/fs/file-max

# 現在の使用状況(割り当て済み・未使用・最大)
cat /proc/sys/fs/file-nr
# 例: 3456  0  2097152
#     ↑使用中 ↑未使用 ↑最大値

# システム全体で開いている fd 数
lsof | wc -l

# ユーザーごとに開いている fd 数
lsof | awk '{print $3}' | sort | uniq -c | sort -rn | head -20

# プロセスごとに開いている fd 数(多い順)
for pid in $(ls /proc | grep '^[0-9]'); do
  count=$(ls /proc/$pid/fd 2>/dev/null | wc -l)
  echo "$count $pid $(cat /proc/$pid/comm 2>/dev/null)"
done | sort -rn | head -20

fd リークを疑う場合の確認

# 特定プロセスが開いているファイルを種類別に集計
lsof -p <PID> | awk '{print $5}' | sort | uniq -c | sort -rn

# ソケット接続の状態を確認
ss -s
ss -tnp | grep <PID>

# 時系列で fd 数が増えているか監視
watch -n 1 "ls /proc/<PID>/fd | wc -l"

解決方法

方法1:ulimit で一時的に上げる(即効)

# 現在のセッションだけ上限を上げる
ulimit -n 65536

# 確認
ulimit -n
# → 65536

⚠️ ターミナルを閉じると元に戻ります。サーバー再起動後も消えます。

hard limit を超えた値は設定できません。

# hard limit の確認
ulimit -Hn
# → 4096

# hard limit を超えると失敗する
ulimit -n 65536
# bash: ulimit: open files: cannot modify limit: Operation not permitted

hard limit を超えて設定したい場合は root で実行するか、/etc/security/limits.conf を変更します。


方法2:/etc/security/limits.conf で恒久的に設定

最も一般的な恒久的解決方法です。

sudo vi /etc/security/limits.conf
# /etc/security/limits.conf
# <domain>  <type>  <item>   <value>

# 全ユーザーに適用
*           soft    nofile   65536
*           hard    nofile   65536

# rootユーザーは別途指定が必要
root        soft    nofile   65536
root        hard    nofile   65536

# 特定ユーザーだけ設定
www-data    soft    nofile   65536
www-data    hard    nofile   65536

# 特定グループに設定
@webdev     soft    nofile   65536
@webdev     hard    nofile   65536

soft と hard の違い:

種類説明
softデフォルトの上限。ユーザーが ulimit -n で変更できる上限
hardユーザーが設定できる最大値。root のみ変更可能

設定後は再ログインが必要です(再起動は不要)。

# 設定を反映するには再ログイン
exit
# 再接続後に確認
ulimit -n

方法3:sysctl でシステム全体の上限を上げる

プロセスごとの上限を上げてもシステム全体の上限が低い場合はエラーが続きます。

# 現在のシステム全体上限
cat /proc/sys/fs/file-max

# 一時的に変更(再起動で元に戻る)
sudo sysctl -w fs.file-max=2097152

# 恒久的に変更
sudo vi /etc/sysctl.conf
# /etc/sysctl.conf に追記
fs.file-max = 2097152

# 関連パラメータも合わせて設定するケースが多い
fs.nr_open = 1048576
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
# 設定を即時反映
sudo sysctl -p

# 確認
cat /proc/sys/fs/file-max

方法4:systemd サービスの設定(サービス単位)

systemd で管理されているサービス(Nginx・MySQL など)は、limits.conf の設定が効かない場合があります。サービスファイルに直接設定します。

# サービスの設定をオーバーライドする(推奨)
sudo systemctl edit nginx

エディタが開くので以下を追記します:

[Service]
LimitNOFILE=65536
# 設定を反映
sudo systemctl daemon-reload
sudo systemctl restart nginx

# 設定が反映されたか確認
cat /proc/$(pgrep nginx | head -1)/limits | grep "open files"

既存のサービスファイルを直接編集する方法(非推奨・アップデートで上書きされる):

# サービスファイルの場所を確認
systemctl cat nginx | head -3

# 直接編集は避け、override を使う
sudo systemctl edit nginx  # /etc/systemd/system/nginx.service.d/override.conf が作られる

方法5:PAM の設定確認(limits.conf が効かない場合)

/etc/security/limits.conf を設定したのに反映されない場合、PAM の設定を確認します。

# pam_limits が有効か確認
grep pam_limits /etc/pam.d/common-session
# session required pam_limits.so  ← この行が必要

grep pam_limits /etc/pam.d/common-session-noninteractive
# session required pam_limits.so  ← この行が必要

# なければ追記
echo "session required pam_limits.so" | sudo tee -a /etc/pam.d/common-session

アプリケーション別の対処法

Nginx

# nginx.conf で設定
sudo vi /etc/nginx/nginx.conf
# nginx.conf
worker_processes auto;  # CPUコア数に合わせる

events {
    worker_connections 65536;  # ワーカーあたりの最大接続数
    use epoll;
    multi_accept on;
}

# worker_processes × worker_connections が同時接続の最大数
# OS側の ulimit もそれ以上に設定が必要
# systemd 経由の設定(上記 方法4 を参照)
sudo systemctl edit nginx
[Service]
LimitNOFILE=65536
sudo systemctl daemon-reload
sudo systemctl restart nginx

# 確認
nginx -T | grep worker_connections

Apache

# /etc/apache2/mods-enabled/mpm_event.conf
<IfModule mpm_event_module>
    StartServers          2
    MinSpareThreads      25
    MaxSpareThreads      75
    ThreadLimit          64
    ThreadsPerChild      25
    MaxRequestWorkers   150
    MaxConnectionsPerChild 0
</IfModule>
# systemd 経由で fd 上限を設定
sudo systemctl edit apache2
[Service]
LimitNOFILE=65536

Node.js

Node.jsはシングルプロセスで多数の接続を扱うため、fd の枯渇が起きやすいです。

# Node.js プロセスの fd 使用状況を確認
lsof -p $(pgrep node) | wc -l

# 起動前に ulimit を設定してから Node を起動
ulimit -n 65536
node app.js

# PM2 で管理している場合
# ecosystem.config.js
module.exports = {
  apps: [{
    name: 'app',
    script: 'app.js',
    node_args: '--max-old-space-size=4096',
    env: {
      NODE_ENV: 'production'
    }
  }]
}
# PM2 自体の fd 上限は systemd 経由で設定
sudo systemctl edit pm2-root
[Service]
LimitNOFILE=65536

Node.js アプリ側でのリーク確認:

// 開いているハンドルを確認(デバッグ用)
process._getActiveHandles().length
process._getActiveRequests().length

// graceful shutdown を実装してリークを防ぐ
process.on('SIGTERM', () => {
  server.close(() => {
    db.end();
    process.exit(0);
  });
});

Java(Spring Boot など)

# JVM プロセスの fd 確認
lsof -p $(pgrep java) | wc -l

# 起動スクリプトで ulimit を設定
#!/bin/bash
ulimit -n 65536
exec java -jar app.jar
# systemd サービスファイルのオーバーライド
[Service]
LimitNOFILE=65536

Java アプリ側でのリーク確認:

// ファイルを確実に閉じる(try-with-resources)
try (FileInputStream fis = new FileInputStream("file.txt")) {
    // 処理
} // ← 自動的にcloseされる

// 接続プールのサイズを確認・調整
# application.properties (Spring Boot)
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.minimum-idle=5

MySQL / MariaDB

# MySQL の open_files_limit を確認
mysql -e "SHOW VARIABLES LIKE 'open_files_limit';"
# → open_files_limit  5000

# 現在開いているファイル数を確認
mysql -e "SHOW STATUS LIKE 'Open_files';"
# /etc/mysql/mysql.conf.d/mysqld.cnf

[mysqld]

open_files_limit = 65536

# systemd 経由(MySQL 5.7以降は systemd が管理)
sudo systemctl edit mysql
[Service]
LimitNOFILE=65536
sudo systemctl daemon-reload
sudo systemctl restart mysql
mysql -e "SHOW VARIABLES LIKE 'open_files_limit';"

Redis

# redis.conf
sudo vi /etc/redis/redis.conf
# redis.conf
maxclients 10000
# systemd 経由
sudo systemctl edit redis
[Service]
LimitNOFILE=65536

Docker コンテナ

# コンテナの fd 上限を確認
docker exec <container> bash -c 'ulimit -n'

# コンテナ起動時に上限を設定
docker run --ulimit nofile=65536:65536 nginx

# docker-compose.yml での設定
# docker-compose.yml
services:
  app:
    image: node:20
    ulimits:
      nofile:
        soft: 65536
        hard: 65536

fd リーク(ファイルディスクリプタリーク)の調査

上限を上げても根本原因がリークの場合は、時間が経つと再び発生します。

リークを検出する

# fd 数が増え続けていないか監視
PID=$(pgrep myapp)
while true; do
  count=$(ls /proc/$PID/fd 2>/dev/null | wc -l)
  echo "$(date): $count fds open"
  sleep 10
done

# 開いているファイルの種類を確認(ソケットが増え続けていないか等)
lsof -p $PID | awk '{print $5}' | sort | uniq -c | sort -rn

# TIME_WAIT 状態のソケットが多い場合
ss -s | grep TIME-WAIT

一般的なリークのパターンと対策

# パターン1:ファイルを開いたまま閉じていない
# → コードレビューで try-finally / try-with-resources の確認

# パターン2:例外発生時にcloseが漏れている
# → アプリのエラーハンドリングを確認

# パターン3:接続プールの設定ミス(接続を返却していない)
# → コネクションプールのタイムアウト設定・モニタリング

# パターン4:シグナルハンドラ未実装でゾンビプロセスが溜まる
# → SIGCHLD ハンドラや waitpid の実装確認

再発防止:監視と適切な上限値

監視スクリプト

#!/bin/bash
# /usr/local/bin/check_fd.sh
# cron で定期実行

THRESHOLD=80  # 上限の80%を超えたら警告

for pid in $(ps aux | grep -v grep | awk 'NR>1 {print $2}'); do
  limit=$(cat /proc/$pid/limits 2>/dev/null \
    | grep "open files" | awk '{print $4}')
  current=$(ls /proc/$pid/fd 2>/dev/null | wc -l)
  comm=$(cat /proc/$pid/comm 2>/dev/null)

  if [ -n "$limit" ] && [ "$limit" != "unlimited" ] && [ "$limit" -gt 0 ]; then
    usage=$((current * 100 / limit))
    if [ "$usage" -ge "$THRESHOLD" ]; then
      echo "WARNING: $comm (PID $pid): $current/$limit fds ($usage%)"
    fi
  fi
done
# cron に登録
echo "*/5 * * * * /usr/local/bin/check_fd.sh >> /var/log/fd_check.log 2>&1" \
  | sudo crontab -

適切な上限値の目安

用途推奨値
一般的なWebアプリ65536
高トラフィックWebサーバー(Nginx等)200000〜
データベースサーバー65536〜200000
システム全体(fs.file-max)2097152

上限を無制限に上げるのではなく、実際の使用量の2〜3倍程度を目安にして、監視で常に把握することが重要です。


設定チェックリスト

問題が発生したときに確認する項目をまとめます。

# 1. プロセスの fd 使用状況を確認
ls /proc/<PID>/fd | wc -l
cat /proc/<PID>/limits | grep "open files"

# 2. システム全体の使用状況を確認
cat /proc/sys/fs/file-nr
cat /proc/sys/fs/file-max

# 3. ulimit の設定を確認
ulimit -n
ulimit -Hn

# 4. limits.conf の設定を確認
grep nofile /etc/security/limits.conf
grep nofile /etc/security/limits.d/*.conf

# 5. PAM の設定を確認
grep pam_limits /etc/pam.d/common-session

# 6. systemd サービスの設定を確認
systemctl show nginx | grep LimitNOFILE
cat /proc/$(pgrep nginx | head -1)/limits | grep "open files"

# 7. sysctl の設定を確認
sysctl fs.file-max
sysctl fs.nr_open

トラブルシューティング

limits.conf を設定したのに反映されない

# 原因1:再ログインしていない
exit  # 再ログインする

# 原因2:PAM が設定されていない
grep pam_limits /etc/pam.d/common-session
# → なければ追記
echo "session required pam_limits.so" | sudo tee -a /etc/pam.d/common-session

# 原因3:systemd サービスは limits.conf を読まない
# → systemctl edit でサービス単位に設定する

# 原因4:/etc/security/limits.d/ の設定が上書きしている
ls /etc/security/limits.d/
cat /etc/security/limits.d/*.conf

systemd サービスで LimitNOFILE が効かない

# daemon-reload を忘れていないか確認
sudo systemctl daemon-reload
sudo systemctl restart サービス名

# 設定が正しく読み込まれているか確認
systemctl show サービス名 | grep LimitNOFILE

# override.conf が正しい場所にあるか確認
ls /etc/systemd/system/サービス名.service.d/
cat /etc/systemd/system/サービス名.service.d/override.conf

root ユーザーで limits.conf が効かない

# root は limits.conf の * では対象外
# root 用の設定を明示的に追加する
sudo vi /etc/security/limits.conf
root    soft    nofile  65536
root    hard    nofile  65536

Dockerコンテナ内で設定できない

# コンテナ内では ulimit コマンドが制限される場合がある
# ホスト側またはdocker run / compose で設定する
docker run --ulimit nofile=65536:65536 イメージ名

よくある質問(FAQ)

Q1. 上限はどのくらいまで上げていいですか?

1プロセスあたり 1048576(fs.nr_open のデフォルト上限)まで設定できます。ただし1つのfdはカーネルメモリを消費するため、闇雲に上げるのではなく実際の使用量を把握したうえで必要な値に設定しましょう。一般的なWebアプリなら 65536、高トラフィックなサーバーなら 200000 程度が現実的です。

Q2. soft と hard のどちらを上げればいいですか?

両方上げましょう。soft だけ上げても hard が低いと ulimit -n で soft を超える値に変更できません。通常は同じ値に設定します。

* soft nofile 65536
* hard nofile 65536

Q3. ulimit -n unlimited は使えますか?

ulimit -n unlimited は設定できません。nofile は unlimited を指定できない項目です(カーネルの制約)。代わりに大きな数値(1048576 など)を設定してください。

Q4. アプリを再起動せずに設定を反映できますか?

/proc/<PID>/limits はプロセスが動いている間は変更できません。設定ファイルを変更後にアプリを再起動する必要があります。緊急時は ulimit -n で現在のシェルの上限を上げてからそのシェル内でアプリを起動し直すか、prlimit コマンドで動作中プロセスの上限を変更できます。

# prlimit で動作中プロセスの上限を変更(Linux 3.2以降)
sudo prlimit --nofile=65536:65536 --pid <PID>

# 確認
cat /proc/<PID>/limits | grep "open files"

Q5. 「Too many open files in system」と「Too many open files」は違いますか?

違います。

  • Too many open files:プロセスまたはユーザーの上限(ulimit -n)に達した
  • Too many open files in system:システム全体の上限(fs.file-max)に達した

前者はプロセス・ユーザーの設定を、後者は sysctl fs.file-max を上げます。

Q6. コンテナ環境でも同じ対処法が使えますか?

コンテナ内の ulimit はホストの設定に依存します。Dockerの場合は --ulimit nofile=65536:65536 オプションか docker-compose.ymlulimits で設定します。Kubernetesの場合はPodのSecurityContextで設定できますが、コンテナランタイムの設定も必要になる場合があります。


まとめ

「Too many open files」は3つのレイヤーの上限を把握して対処することがポイントです。

1. プロセス/ユーザーの上限  → ulimit -n / /etc/security/limits.conf
2. systemd サービスの上限   → systemctl edit / LimitNOFILE
3. システム全体の上限       → sysctl fs.file-max / /etc/sysctl.conf

対処の手順:

  1. ulimit -n/proc/<PID>/limits で現在の上限を確認
  2. ls /proc/<PID>/fd | wc -l で実際の使用数を確認
  3. 上限を超えていれば /etc/security/limits.conf または systemctl edit で上げる
  4. システム全体の上限も fs.file-max で確認・調整
  5. fd の数が増え続けるならリークを疑ってコードを確認
  6. 監視スクリプトで再発防止

単に上限を上げるだけでなく、なぜ fd が多く使われているのか(リークなのか正常な使用なのか)を把握することが根本的な解決につながります。


参考リンク

公式ドキュメント

関連記事(本サイト)


本記事は2026年6月時点の情報をもとに、Ubuntu 24.04 LTS・Linux カーネル 6.x での動作確認に基づき作成しています。カーネルバージョンやディストリビューションによって挙動が異なる場合があるため、最新情報は各公式ドキュメントをご確認ください。