To use perfect-scrollbar with Select2, we can hook on select2:open and select2:closing events, and style Select2 options container with position: relative.

Here is the example code for reference:

var perfectScrollbarOptions = {};

/** Destroy and re-initialize perfect-scrollbar. */
function reinitPerfectScrollbar() {
    $('.select2-results__options')
        .perfectScrollbar('destroy')
        .perfectScrollbar(perfectScrollbarOptions);
}

var $select = $('select');
$select
    .select2();
    .on('select2:open', function () {
        // setTimeout is required here as we need to wait for all DOM
        // operations carried out by Select2.
        setTimeout(() => {
            $('.select2-results__options').perfectScrollbar(perfectScrollbarOptions);

            // Re-initializing perfect-scrollbar is required on keyup and input
            // as Select2 clears everything inside '.select2-results__options'
            // and reset its content on those events.
            $('.select2-search__field').on('keyup input', reinitPerfectScrollbar);
        }, 0);
    })
    .on('select2:closing', function () {
        $('.select2-search__field').off('keyup input', reinitPerfectScrollbar);
        $('.select2-results__options').perfectScrollbar('destroy');
    });

Note: setTimeout in our select2:open hook is required, since Select2 refreshes options in select even after select2:open is fired. So we simply need to wait for that before we carry out our DOM dependent operations.

Note: Re-initializing perfect-scrollbar on both keyup and input events of Select2 search field is required as Select2 binds both of those two events and will update (empty and then refill) search results in response.

.select2-results__options {
    position: relative;
}

Note: A non-static positioned container is required for perfect-scrollbar to work correctly. See Before using perfect-scrollbar.