Not logged in. Login

Express Deployment with Docker

The instructions here should help you get Express projects (or with minimal adaptation, other Node-based web projects) running on Docker.

These instructions assume Mongo as your database. It might not be the best choice, but it's what most Express tutorials assume, so I'm going with it.

Development-style Deployment

These instructions should be enough to get things running, so you can work on the code, at least. These are not appropriate to production.

For this deployment, we use a container for the application and one for Mongo. The docker-compose.yml file will be something like this:

version: '3'
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile-devel
    ports:
      - "3000:3000"
    volumes:
      - ./myapp:/code:ro
  mongo:
    image: "mongo:latest"
    restart: always

This will (when everything is done) expose your app at http://localhost:3000/

And the Dockerfile-devel like this:

FROM node:latest
WORKDIR /code
COPY wait.sh /wait.sh
RUN chmod +x /wait.sh
COPY myapp/package.json /package.json
RUN cd / && npm install -g
RUN cd / && rm -rf /node_modules/node-sass && npm install node-sass # work around https://github.com/sass/node-sass/issues/1579
CMD /wait.sh mongo 27017 \
  && node populatedb mongodb://mongo/tutorial \
  && node ./bin/www

This uses the wait.sh script that will pause until a service is available: the Postgres database in this case.

This assumes you have a package.json in the current directory with your pip requirements, and populatedb.js as described in the Mozilla tutorial that creates some data for development. Adjust accordingly.

To connect to the Mongo database, the URL will be something like mongodb://mongo/project.

Production-style Deployment

For a production-like environment, we'll have three services running: our application, a database server, and a frontend web server. The frontend server will be responsible for serving the static files.

Docker Compose

My docker-compose.yml to do all of this is:

version: '3'
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile-app
  mongo:
    image: "mongo:latest"
    restart: always
    volumes:
      - db:/data/db:rw
  web:
    build:
      context: .
      dockerfile: Dockerfile-web
    ports:
      - "8080:80"

volumes:
  db:

Database Container

The database setup should be the same as before. Note that the Compose config sets a semi-permanent volume for the database data, so it will persist when the container is destroyed/recreated.

Web Frontend Container

The easiest way to get static files into the Nginx container is probably to copy them into the image:

FROM nginx:latest
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY myapp/public/ /static

Then you can ask Nginx to first look for the file, and if it's not present, forward the request to Node:

upstream backend {
    server app:3000;
}
server {
    listen       80;
    server_name  localhost;
    root /static/;
    location / {
        try_files $uri @backend;
    }
    location @backend {
        proxy_pass http://backend;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Application Container

For production, we will copy our code into the image, not mount it externally. The only real change to the Dockerfile will be to do that:

FROM node:latest
WORKDIR /code
COPY wait.sh /wait.sh
RUN chmod +x /wait.sh
COPY myapp/package.json /package.json
RUN cd / && npm install -g
RUN cd / && rm -rf /node_modules/node-sass && npm install node-sass # work around https://github.com/sass/node-sass/issues/1579
ADD ./myapp /code
CMD /wait.sh mongo 27017 \
  && node populatedb mongodb://mongo/tutorial \
  && node ./bin/www
Updated Mon Aug. 30 2021, 07:36 by ggbaker.