Для dev-окружения имеет смысл монтировать весь код, включая папку node_modules, как том. И выполнять npm i/composer install в нужный момент.
Та же команда выполняется при сборке образа, в результате все необходимые пакеты оказываются установлены при тестировании и на проде.
Да, такой подход оставляет вероятность расхождения данных между dev и prod версиями. Для моей ситуации эта вероятность достаточно незначительна, чтобы ею пренебречь. В другом же случае стоит воспользоваться вашим вариантом 2.5.
Варианты 1 и 2 не рекомендую по простым причинам:
1. Нарушает принцип единообразия окружения: если у разработчиков, на stage, test и prod окружениях окажутся разные версии nodejs, это может обернуться большими проблемами. Поэтому все операции необходимо производить с помощью программ, установленных в контейнере.
2. Можно напороться на ситуацию, когда удаленные сервера зависимостей не отвечают (перегружены, ddos, упали и пр) или одна из зависимостей с них удалена (я так уже попадал). Другими словами, все содержимое папки node_modules/vendor должно храниться в контейнере. Либо пусть ваш админ поднимает зеркало того же packagist и морочае ся с актуальностью пакетов на нем (в чем смысла мало, если можно хранить все в образе контейнера).