How do I get node.js “live editing” to work with Docker

My weekend project was to explore Docker and I thought a simple node.js project would be good. By “live edit” I mean I’d like to be able to manipulate files on my host system and (with as little effort as possible) see the Docker container reflect those changes immediately.

The Dockerizing a Node.js web app went smoothly, and then the Googling and thrashing began. I think that I now know the following:

  • Communication between linked docker containers
  • Can't run Cassandra on Docker with Spark
  • How do you manage development tasks when setting up your development environment using docker containers?
  • docker run -> name is already in use by container
  • Starting services at container startup
  • Can't CD into directory with 755 permissions?
    • If I use the ADD method noted on the nodejs tutorial, then I can’t have live edit because ADD is fulfilled completely at docker build (not at docker run).

    • If I mount the node project’s directory with something like -v `pwd`:/usr/src/app, then it won’t run because either node_modules doesn’t exist (and the volume is not available at build time to get populated because -v is a docker run argument) or I need to prepopulate node_modules in the host’s project directory, which just doesn’t feel right and could have OS compatibility issues.

    My mad newbie thrashing can be distilled to three attempts, each with their own drawbacks or apparent failures.

    1) The Node.js tutorial using ADD Works perfectly, but no “live edit”. I have no expectation this should be what I need, but it at least proved I have the basic wiring in place and running.

    FROM node:argon
    
    # Create app directory
    RUN mkdir -p /usr/src/app
    WORKDIR /usr/src/app
    
    # Install app dependencies
    COPY package.json /usr/src/app/
    RUN npm install
    
    # Bundle app source
    COPY . /usr/src/app
    
    EXPOSE 8080
    CMD [ "npm", "start" ]
    

    2) Try building the node dependencies as globals from the Dockerfile. This seemed less cool, but reasonable (since I wouldn’t expect to change dependencies often). However, it also simply didn’t work, which really surprised me.

    FROM node:argon
    
    RUN npm install -g express
    
    WORKDIR /usr/src/app
    # which will be added via `docker run -v...`
    
    EXPOSE 8080
    

    3) Upon build, ADD only the package.json to a temporary location and get node_modules set up, then move that to the host’s project directory, then mount the project’s directory with -v `pwd`:/usr/src/app. If this would have worked I’d have tried to add nodemon and would theoretically have what I want. This seemed to me to be by far the most clever and commonsense, but this simply didn’t work. I monkeyed with a few things attempting to fix, including host directory permissions, and had no joy.

    FROM node:argon
    
    WORKDIR /usr/src/app
    
    # Provides cached layer for node_modules
    ADD package.json /tmp/package.json
    RUN cd /tmp && npm install
    RUN cp -a /tmp/node_modules /usr/src/app/
    
    EXPOSE 8080
    

    I suspect there are multiple instances of me not understanding some basic concept, but as I searched around it seemed like there were a lot of different approaches, sometimes complicated by additional project requirements. I was, perhaps foolishly, trying to keep it simple. 🙂

    Stats:

    • Running on Mac OS 10.10
    • Docker 1.12.0-rc2-beta17 (latest at time of writing)
    • Kitematic 0.12.0 (latest at time of writing)
    • Node.js 4.4.7 (latest pre-6 at time of writing)

    UPDATE: Attempting to pull some of what I’ve tried to learn together, I’ve done the following and had better luck. Now it builds but docker run -v `pwd`:/usr/src/app -p 49160:8080 -d martink/node-docker3 doesn’t stay running. However, I can “Run” and “Exec” in Kiteomatic and in shell I can see that node_modules looks good and has been moved into the right place, and I can manually do node server.js and have joy.

    FROM node:argon
    
    # Copy over the host's project files
    COPY . /usr/src/app
    # This provides a starting point, but will later be overridden by `-v`, I hope
    
    # Use this app directory moving forward through this file
    WORKDIR /usr/src/app
    
    # PULL TOGETHER `NODE_MODULES`
    ## Grab the package.json from the host and copy into `tmp`
    COPY package.json /tmp/package.json
    
    ## Use that to get `node_modules` set up in `tmp`
    RUN cd /tmp && npm install
    
    ## Copy that resulting node_modules into the WORKDIR
    RUN cp -a /tmp/node_modules /usr/src/app/
    
    EXPOSE 8080
    

    I think my questions might now be whittled down to…

    • How do I make server.js start when I run this?
    • How do I see “live edits” (possibly start this with nodemon)?

  • Can not start Docker 1.9.1
  • Host name does not match the certificate subject provided by the peer, but it's a perfect match
  • docker create service vs. docker deploy stack
  • Docker exits CMD on start
  • Windows 7 - Error: connect ENOENT //./pipe/docker_engine
  • Intranet name resolution in docker container
  • One Solution collect form web for “How do I get node.js “live editing” to work with Docker”

    It appears this Dockerfile gets me what I need.

    FROM node:argon
    
    # Adding `nodemon` as a global so it's available to the `CMD` instruction below
    RUN npm install -g nodemon
    
    # Copy over the host's project files
    COPY . /usr/src/app
    # This provides a starting point, but will later be overridden by `docker run -v...`
    
    # Use this app directory moving forward through this file
    WORKDIR /usr/src/app
    
    # PREPARE `NODE_MODULES`
    ## Grab the `package.json` from the host and copy into `tmp`
    COPY package.json /tmp/package.json
    
    ## Use that to get `node_modules` set up
    RUN cd /tmp && npm install
    
    ## Copy that resulting `node_modules` into the WORKDIR
    RUN cp -a /tmp/node_modules /usr/src/app/
    
    EXPOSE 8080
    CMD [ "nodemon", "./server.js" ]
    

    I build this like so:

    docker build -t martink/node-docker .
    

    And run it like so:

    docker run -v `pwd`:/usr/src/app -p 49160:8080 -d  martink/node-docker
    

    This…

    • Stays running
    • Responds as expected at http://localhost:49160
    • Immediately picks up changes to server.js made on the host machine’s file

    I’m happy that it seems I have this working. If I’ve any bad practices in there, I’d appreciate feedback. 🙂

    Docker will be the best open platform for developers and sysadmins to build, ship, and run distributed applications.