This is a quick post on how to get hot module reloading working in vuejs/vue-cli in a local dockerized development setup based on a virtual machine in Virtualbox. The very short answer is to pass the environment variable CHOKIDAR_USEPOLLING=true
to the container.
Background
When using a so-called bind mount
in Docker "a file or directory on the host machine is mounted into a container" (see here for differences between bind mounts
and named volumes
)
For dockerized software development with e.g. a MacBook or a Laptop and a VM in Virtualbox (for the Docker Host) you usually pass through a directory from your MacBook to the Docker Host in the virtual machine via Shared Folders
and then this (or some directory beneath it) further into the container at runtime via a so-called bind mount
.
In contrast to a named volumes
you can write code on your MacBook and the changes are immediately available in the container. However, detecting file system changes in such a construct (Mac OS => Docker Host VM => Docker Container) sometimes does not work as if everything was on the same (physical) system with same OS and file system.
Update/Clarification: This problem only occurs when running your Docker engine inside a VM. If you are on Linux for both Docker and for coding you do not have this problem.
App Setup
This post assumes that you use the new vue-cli
... however the basic principles just outlined above should be true also for other setups (indeed it is also true for a react
app, see here and here):
npm install -g @vue/cli
vue create my_app
# stick to the default here or use a custom setup
With this in place you can now configure the docker development settings.
Docker Setup
A basic Dockerfile
for local development could look like that:
FROM node:carbon-slim
# vue-cli reqires 8.10.0+
RUN apt-get -y update \
&& apt-get install -y git
RUN npm install -g @vue/cli
WORKDIR /target/in/container
EXPOSE 8080
USER node
CMD ["yarn", "serve"]
The important bits in docker-compose
:
version: "3.3"
services:
my_app:
<...>
ports:
- "8080:8080"
volumes:
- /path/on/laptop:/target/in/container
stdin_open: true
tty: true
environment:
- <...>
- CHOKIDAR_USEPOLLING=true
<...>
The most important bit is CHOKIDAR_USEPOLLING=true
. Details on what it is and what it does can be found here.
You could of course pass the bind mount and the environment variable just as well on the docker cli
during docker run <...>
or even set the environment variable CHOKIDAR_USEPOLLING=true
with ENV
in your Dockerfile
.
Summary
With this setup you now have (with only one additional environment variable
) all you need to write code on your laptop which gets detected immediately in the container and leads to a restart of the webpack-dev-server.
Add-On
If you get sockjs-node errors in your browser console you can add the following to your vue.config.js
in the root directory of your app:
module.exports = {
configureWebpack: {
<...>
},
devServer: {
public: '<docker_host_external_ip>:8080',
},
};
As an alternative to CHOKIDAR_USEPOLLING
you can also try the watchOptions
approach:
devServer: {
watchOptions: {
ignored: /node_modules/,
aggregateTimeout: 300,
poll: 1000,
},
}
However, as the docs note:
If watching does not work for you, try out this option. Watching does not work with NFS and machines in VirtualBox.
Further Information:
This post was inspired by:
http://mherman.org/blog/2017/12/07/dockerizing-a-react-app/
Chokidar:
https://github.com/paulmillr/chokidar
Vue.js and vue-cli:
https://vuejs.org
https://github.com/vuejs/vue-cli
https://cli.vuejs.org
https://github.com/webpack/webpack-dev-server
https://webpack.js.org/configuration/watch/#watchoptions
Docker Bind Mounts and Volumes:
https://docs.docker.com/storage/bind-mounts/
https://docs.docker.com/storage/volumes/
React App Similarities:
https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#troubleshooting