The file descriptor limit inside a container comes from the daemon’s default rather than the host’s, and a process that opens many sockets finds it at the worst moment.
$ docker run --rm alpine sh -c 'ulimit -n'
1048576 # the 20.10 default: generous
# but a compose service can be given less, or a base
# image's entrypoint can lower it:
services:
php:
ulimits:
nofile:
soft: 65536
hard: 65536
# and the symptom when it is too low:
# "Too many open files" in the nginx or php-fpm log
The 20.10 default is high enough that this is rarely the problem, and the case that still bites is an image whose entrypoint sets a lower soft limit for its own reasons. Setting both soft and hard explicitly is worth doing for anything holding many connections — a proxy, a worker with a large pool — because the failure is a request that fails rather than a process that dies, so it is easy to attribute to something else.