With the removal of Mutagen from Docker for Mac Edge, there's been a lot of interest in continued use of Mutagen for development. While development of gRPC-FUSE continues, I'd like to offer a workaround to developers looking to emulate the previous Docker for Mac Edge functionality. This workaround involves manually creating Docker volumes and Mutagen synchronization sessions. While this isn't as elegant as the Docker for Mac UI, it does provide more granular control over synchronization.
This isn't the only way to use Mutagen with Docker for Mac, it's just an initial workaround proposal. If users have additional setups they'd like to share (e.g. using an SSH sidecar container), please add them below! As @driq already pointed out, Mutagen's Compose integration offers another way to automate this caching.
Please feel free to email me at jacob@mutagen.io if you'd like to discuss your specific setup and needs via email or video chat.
Also, if you'd be interested in a tool that automates this (maybe even a simple GUI), please let me know below!
Install Mutagen
First, you'll need to install Mutagen. If you're on macOS, the best and easiest way to do this is with Homebrew:
brew install mutagen-io/mutagen/mutagen
Create a volume for caching files
Now, for each directory that you want to sync/cache inside the Docker for Mac VM, you'll need to create a named volume:
docker volume create mycache
Create a container to access the volume
In order for Mutagen to access the volume, you'll need to create a container with the volume mounted. You can use the Mutagen sidecar container for this purpose (it's just a no-op entry point):
docker container create --name mycachecontainer -v mycache:/volumes/mycache mutagenio/sidecar
docker container start mycachecontainer
The /volumes directory already exists inside the Mutagen sidecar container, so it makes a good location to create mountpoints.
You can use one sidecar container per volume, or one sidecar container for multiple volumes. The only disadvantage with the latter approach is that you have to mount volumes at container creation time, which makes adding volumes later tricky.
(Optional) Change volume ownership/permissions
Docker volumes default to root ownership with rwxr-xr-x permissions. If you'd like to change that, now is a good time to do that. You can use something like the following command(s):
docker exec mycachecontainer chown 1001:1001 /volumes/mycache
docker exec mycachecontainer chmod go+w /volumes/mycache
The Mutagen sidecar container runs as root, so you can change ownership to any UID/GID you like, I've just chosen 1001 for this example. Your exact needs will vary depending on the containers you're using. If you run your containers as root, this may not matter at all.
Create a Mutagen synchronization session
Finally, you'll want to create a Mutagen synchronization session to sync files into the volume. This will look something like the following:
mutagen sync create [options] <path> docker://mycachecontainer/volumes/mycache
A more concrete example might look something like:
mutagen sync create --name mycache --sync-mode=two-way-resolved --ignore-vcs --ignore 'node_modules/' --default-owner-beta="id:1001" ~/Projects/myapp docker://mycachecontainer/volumes/mycache
Mutagen synchronization sessions have a lot of options that you can use to customize behavior. For a full list, see mutagen sync create --help. I would recommend reading the section on synchronization sessions in the Mutagen documentation, as well as the sections on ignores, version control systems, and permissions.
The most important takeaways are the following:
- Mutagen is a synchronization engine, meaning that it needs to copy files into the target volume. While Mutagen is designed to handle 10,000-100,000 files efficiently, you can't synchronize your entire home directory into the container and expect good performance. You should target caching of individual codebases.
- Mutagen has multiple synchronization modes and defaults to bidirectional synchronization. You probably want to use the
two-way-resolved mode (i.e. --sync-mode=two-way-resolved)
- Mutagen allows you to ignore content and you should use this to your advantage. For example, things like
node_modules directories make ideal candidates for ignoring. They are big and aren't generally portable between macOS and Linux anyways.
- You shouldn't synchronize version control directories like
.git. Use the --ignore-vcs flag.
- Mutagen will execute as
root inside the sidecar container. This means that files and directories it creates inside the cache will default to root ownership with restrictive permissions, but it also means that you can use Mutagen's permission and ownership settings to configure this however you'd like to suit the needs of your application.
- For storing common configuration, you can create a file at
~/.mutagen.yml. Mine looks something like:
sync:
defaults:
ignore:
vcs: true
paths:
- ".DS_Store"
- "*.py[cod]"
- "__pycache__/"
- "*.egg-info/"
- "*~"
- "*.sw[a-p]"
- You can get detailed status information on Mutagen synchronization sessions using the
mutagen sync list and mutagen sync monitor commands. For more information on Mutagen's general command structure, check out the getting started guide.
Using the cache
In order to use the cache, you'll most likely want to replace a bind mount in your Docker Compose file with the named volume that you're using as a cache. This transition might look something like the following:
services:
myapp:
...
volumes:
- .:/path/in/container
to
volumes:
mycache:
external: true
services:
myapp:
...
volumes:
- mycache:/path/in/container
You can also use the cache in manually created containers via the -v flag in docker [container] create and docker [container] run.
Tearing it all down
Once you decide that you're done using a particular cache (which could be after an hour, a day, a year, etc.), you can tear it all down with the following:
mutagen sync terminate mycache
docker container stop mycachecontainer
docker container rm mycachecontainer
docker volume rm mycache
With the removal of Mutagen from Docker for Mac Edge, there's been a lot of interest in continued use of Mutagen for development. While development of gRPC-FUSE continues, I'd like to offer a workaround to developers looking to emulate the previous Docker for Mac Edge functionality. This workaround involves manually creating Docker volumes and Mutagen synchronization sessions. While this isn't as elegant as the Docker for Mac UI, it does provide more granular control over synchronization.
This isn't the only way to use Mutagen with Docker for Mac, it's just an initial workaround proposal. If users have additional setups they'd like to share (e.g. using an SSH sidecar container), please add them below! As @driq already pointed out, Mutagen's Compose integration offers another way to automate this caching.
Please feel free to email me at jacob@mutagen.io if you'd like to discuss your specific setup and needs via email or video chat.
Also, if you'd be interested in a tool that automates this (maybe even a simple GUI), please let me know below!
Install Mutagen
First, you'll need to install Mutagen. If you're on macOS, the best and easiest way to do this is with Homebrew:
Create a volume for caching files
Now, for each directory that you want to sync/cache inside the Docker for Mac VM, you'll need to create a named volume:
Create a container to access the volume
In order for Mutagen to access the volume, you'll need to create a container with the volume mounted. You can use the Mutagen sidecar container for this purpose (it's just a no-op entry point):
The
/volumesdirectory already exists inside the Mutagen sidecar container, so it makes a good location to create mountpoints.You can use one sidecar container per volume, or one sidecar container for multiple volumes. The only disadvantage with the latter approach is that you have to mount volumes at container creation time, which makes adding volumes later tricky.
(Optional) Change volume ownership/permissions
Docker volumes default to
rootownership withrwxr-xr-xpermissions. If you'd like to change that, now is a good time to do that. You can use something like the following command(s):The Mutagen sidecar container runs as
root, so you can change ownership to any UID/GID you like, I've just chosen1001for this example. Your exact needs will vary depending on the containers you're using. If you run your containers asroot, this may not matter at all.Create a Mutagen synchronization session
Finally, you'll want to create a Mutagen synchronization session to sync files into the volume. This will look something like the following:
A more concrete example might look something like:
Mutagen synchronization sessions have a lot of options that you can use to customize behavior. For a full list, see
mutagen sync create --help. I would recommend reading the section on synchronization sessions in the Mutagen documentation, as well as the sections on ignores, version control systems, and permissions.The most important takeaways are the following:
two-way-resolvedmode (i.e.--sync-mode=two-way-resolved)node_modulesdirectories make ideal candidates for ignoring. They are big and aren't generally portable between macOS and Linux anyways..git. Use the--ignore-vcsflag.rootinside the sidecar container. This means that files and directories it creates inside the cache will default torootownership with restrictive permissions, but it also means that you can use Mutagen's permission and ownership settings to configure this however you'd like to suit the needs of your application.~/.mutagen.yml. Mine looks something like:mutagen sync listandmutagen sync monitorcommands. For more information on Mutagen's general command structure, check out the getting started guide.Using the cache
In order to use the cache, you'll most likely want to replace a bind mount in your Docker Compose file with the named volume that you're using as a cache. This transition might look something like the following:
to
You can also use the cache in manually created containers via the
-vflag indocker [container] createanddocker [container] run.Tearing it all down
Once you decide that you're done using a particular cache (which could be after an hour, a day, a year, etc.), you can tear it all down with the following: