I’ve been a vagrant user on OSX for a long time, and one of the common methods people use to allow modifying files inside the VM in their preferred development environment is through host syncing (synced folders).
Let me tell you from experience…it’s a shit option. If you aren’t using NFS as the file system (but rather ext or FAT), it’s painfully slow, especially on large projects. In addition, using NFS changes the networking options you have available, so it introduces other complications. I had resorted to simply running VIM within my VMs because I couldn’t take the slow load time and compromises on configuration.
When I started setting up a development environment with Docker, I decided I would get it right this time. Since under the hood Docker runs on top of VirtualBox (same as vagrant) on OSX, I wasn’t ready to go through the same pain again to get everything synced.
I elected instead for this solution:
- Pass in configuration via environment variables to set the Docker environment (and thus the container behavior) without image rebuilds.
- Download another repo that contains the code in the container setup.
- Create a local SSH mount for development.
- Install node dev tools on the container.
- Gulp runs watch, browserify, coffeeify, etc., on the container without looking at any shared volumes from my host.
- Nginx serves up the dist directory that gulp modifies as I make changes.
When running in development mode, the container adds a dev user, copies my local key to the container for that dev user, and starts the ssh server.
Here’s an example of how I’m loading my Docker container:
docker run -p 52022:22 —d -e key="$(cat ~/.ssh/id_rsa.pub)" IMAGE_ID
Here’s the magic bit that makes this work in the Dockerfile (I omitted the pieces that install dependencies, download a git repo, etc.):
ENTRYPOINT sh start.sh
…and the start.sh script:
if [ "$project_env" = "production" ] then echo "Prod!" else mkdir /project/.ssh echo $key >> /project/.ssh/authorized_keys service ssh start adduser --quiet --disabled-password -shell /bin/bash --home /deviant-ui --gecos "User" dev echo "dev:dev" | chpasswd cd /project git pull origin master gulp chown -R dev /deviant-ui echo "Dev." fi nginx -g "daemon off;"
I can now mount to my local filesystem using sshfs, which is installable on OSX via brew:
mnt_ip=$(boot2docker ip) && sshfs -p 52022 dev@$mnt_ip:/project /tmp/test
Unmounting is as easy as:
sudo umount -f /tmp/test
Now, I can work on my local machine, changes are synced instantly to my dev container where my gulp process can handle file watches and change the dist folder that nginx serves from, and I can push changes to my application code without modifying my dev docker image at all.
I’m not finished setting this up yet, but I just wanted to share these things I’ve found as most of the other examples online seem to take the hard path of fighting with VirtualBox. I’ll publish a complete example when it’s ready. To the people who say a Docker container should only do one thing, my answer is that in production, it will only do one thing! In development, I value my time and sanity more than rigid philosophy.