There might be multiple lists: one for kernel modules loaded within initramfs (i.e. modules necessary for basic I/O and accessing the root filesystem) and another list loaded once the root filesystem has been mounted.
For Debian and related Linux distributions like Ubuntu, there's
/etc/initramfs-tools/modules for modules to be loaded in initramfs (in the specific order listed), and
/etc/modules-load.d/ drop-in directory for specifying modules to be loaded after the root filesystem is accessible.
For any distribution using the
dracut initramfs creator, you might want to look into
/etc/dracut.conf.d/*.conf files for
filesystems lines: these will cause the specified modules to be added into initramfs, and in case of
force_drivers, explicitly loaded regardless of hardware detection.
Besides those, on modern systems, many modules are usually loaded by hardware auto-detection: the kernel will format the hardware IDs detected on any autodetection-capable bus into specific module alias names, and the modules themselves will contain wildcard strings matching the hardware they support. If a match is found, the matching module is loaded. Each driver module will usually have a more detailed hardware detection routine that can further verify the compatibility between the module and the hardware.
On architectures with no auto-detection-capable system buses (e.g. RasPi and various embedded devices), a "device tree", a
.dtb file either appended to the kernel image or loaded separately by the bootloader, will describe the system hardware: it includes identifiers for compatible "programming models", which will be used by the kernel to form module alias strings for automatic loading of appropriate modules.
The aim of kernel developers is to make the loading of kernel modules as automatic as reasonably possible.