Syncing folders from a Docker container to your host machine


Today, I struggled with linking my composer vendor folder from my container to my host. I was setting up a simple Lumen container for my microservice. Its Dockerfile looked like this:

FROM php:7.3-fpm-alpine

RUN docker-php-ext-install pdo_mysql

WORKDIR /var/www/html/

# Install composer
RUN php -r "readfile('');" | php -- --install-dir=/usr/bin/ --filename=composer
COPY . .
RUN composer install

It is simple. It pulls a PHP 7.3 image, installs the pdo_mysql extension, copies my project files to the working directory, and runs a composer install.


It looked properly working at first until I realized that my Intelephense VS Code plugin started throwing a bunch of Undefined type errors everywhere. I thought it was just a false positive before restarting my VS Code. I discovered that the culprit was an empty vendor folder in my workspace. Weird!

Personally, it was a frustrating experience. Especially to me who just recently picked up Docker. I did some research and stumbled upon a Stackoverflow answer which talks about a similar issue I am having. It is about a developer not having his node_modules showing up on his host machine. I appropriated the proposed solutions in the thread for my vendor folder!


As stated in one of the answers, a solution can be to create a cache directory in your Dockerfile along with a couple of tricks. Like so:

FROM php:7.3-fpm-alpine

RUN apk add rsync
RUN docker-php-ext-install pdo_mysql

# Set up cache directory
RUN mkdir /var/www/cache
WORKDIR /var/www/cache

# Install composer
RUN mkdir tests
COPY composer.* ./
RUN php -r "readfile('');" | php -- --install-dir=/usr/bin/ --filename=composer
RUN composer install

# Move to actual working directory
WORKDIR /var/www/html

Aside from what we have known so far, we are adding rsync on our container. We will be using this for our script later. We are just setting up a cache directory where we move our composer-related files there and do the composer install. After that, we move to our actual working directory!

The real MVP behind this solution is the file. Like so:


# copy from the image backup location to the volume mount

echo "Synchronizing vendor files..." rsync -a --stats /var/www/cache/vendor/* /var/www/html/vendor/ echo "Synchronized vendor files"

# run PHP server

exec php -S lumen:8000 -t public # port 8000 here will be overwritten by config

It is invoked in my docker-compose.yml. It synchronizes the contents of our cache directory to our actual vendor folder. I used rsync because using plain old cp can be unnecessary overhead; considering this script is ran every time this container is ran. Then, as usual, execute your remaining commands as needed.

Additionally, my docker-compose.yml looks like this if you are curious:

version: "3.5"

        build: .
        ports: - "${APP_PORT:-8000}:8000"
        volumes: - .:/var/www/html
        command: /var/www/html/

Final Thoughts

My vendor folder now has its proper and expected contents. It even syncs up properly whenever I do add new composer dependencies in the container! Cool beans.