Write it from scratch
Understand the most used Dockerfile instructions
Having an optimized Docker layers
Building in parallel with Docker multi-stage feature
FROM node:18.16
FROM node:18.16
WORKDIR /opt/my-app/
FROM node:18.16 AS node-build ## defining the stage name as node-build
WORKDIR /opt/my-app/
COPY package.json /opt/my-app/
FROM node:18.16 AS node-build
WORKDIR /opt/my-app/
COPY package.json /opt/my-app/
RUN npm install
FROM node:18.16 AS node-build
WORKDIR /opt/my-app/
COPY package.json /opt/my-app/
RUN npm install
FROM alpine AS install-heroku ## defining the stage name as install-heroku
FROM node:18.16 AS node-build
WORKDIR /opt/my-app/
COPY package.json /opt/my-app/
RUN npm install
FROM alpine AS install-heroku ## defining the stage name as install-heroku
RUN apk update \
&& apk add curl
FROM node:18.16 AS node-build
WORKDIR /opt/my-app/
COPY package.json /opt/my-app/
RUN npm install
FROM alpine AS install-heroku ## defining the stage name as install-heroku
RUN apk update \
&& apk add curl
ENV HEROKU_VERSION="stable"
RUN curl -LO "https://cli-assets.heroku.com/channels/${HEROKU_VERSION}/heroku-linux-x64.tar.gz" \
&& tar xf heroku-linux-x64.tar.gz
FROM node:18.16 AS node-build
WORKDIR /opt/my-app/
COPY package.json /opt/my-app/
RUN npm install
FROM alpine AS install-heroku ## defining the stage name as install-heroku
RUN apk update \
&& apk add curl
ENV HEROKU_VERSION="stable"
RUN curl -LO "https://cli-assets.heroku.com/channels/${HEROKU_VERSION}/heroku-linux-x64.tar.gz" \
&& tar xf heroku-linux-x64.tar.gz
FROM node:18.16 ## in this case we don't need to define the stage name
FROM node:18.16 AS node-build
WORKDIR /opt/my-app/
COPY package.json /opt/my-app/
RUN npm install
FROM alpine AS install-heroku ## defining the stage name as install-heroku
RUN apk update \
&& apk add curl
ENV HEROKU_VERSION="stable"
RUN curl -LO "https://cli-assets.heroku.com/channels/${HEROKU_VERSION}/heroku-linux-x64.tar.gz" \
&& tar xf heroku-linux-x64.tar.gz
FROM node:18.16 ## in this case we don't need to define the stage name
WORKDIR /opt/my-app/
FROM node:18.16 AS node-build
WORKDIR /opt/my-app/
COPY package.json /opt/my-app/
RUN npm install
FROM alpine AS install-heroku ## defining the stage name as install-heroku
RUN apk update \
&& apk add curl
ENV HEROKU_VERSION="stable"
RUN curl -LO "https://cli-assets.heroku.com/channels/${HEROKU_VERSION}/heroku-linux-x64.tar.gz" \
&& tar xf heroku-linux-x64.tar.gz
FROM node:18.16 ## in this case we don't need to define the stage name
WORKDIR /opt/my-app/
COPY --from=node-build /opt/my-app/node_modules /opt/my-app/node_modules
FROM node:18.16 AS node-build
WORKDIR /opt/my-app/
COPY package.json /opt/my-app/
RUN npm install
FROM alpine AS install-heroku ## defining the stage name as install-heroku
RUN apk update \
&& apk add curl
ENV HEROKU_VERSION="stable"
RUN curl -LO "https://cli-assets.heroku.com/channels/${HEROKU_VERSION}/heroku-linux-x64.tar.gz" \
&& tar xf heroku-linux-x64.tar.gz
FROM node:18.16 ## in this case we don't need to define the stage name
WORKDIR /opt/my-app/
COPY --from=node-build /opt/my-app/node_modules /opt/my-app/node_modules
COPY --from=install-heroku /heroku/bin/heroku /usr/local/bin
FROM node:18.16 AS node-build
WORKDIR /opt/my-app/
COPY package.json /opt/my-app/
RUN npm install
FROM alpine AS install-heroku ## defining the stage name as install-heroku
RUN apk update \
&& apk add curl
ENV HEROKU_VERSION="stable"
RUN curl -LO "https://cli-assets.heroku.com/channels/${HEROKU_VERSION}/heroku-linux-x64.tar.gz" \
&& tar xf heroku-linux-x64.tar.gz
FROM node:18.16 ## in this case we don't need to define the stage name
WORKDIR /opt/my-app/
COPY --from=node-build /opt/my-app/node_modules /opt/my-app/node_modules
COPY --from=install-heroku /heroku/bin/heroku /usr/local/bin
COPY . /opt/my-app/
FROM node:18.16 AS node-build
WORKDIR /opt/my-app/
COPY package.json /opt/my-app/
RUN npm install
FROM alpine AS install-heroku ## defining the stage name as install-heroku
RUN apk update \
&& apk add curl
ENV HEROKU_VERSION="stable"
RUN curl -LO "https://cli-assets.heroku.com/channels/${HEROKU_VERSION}/heroku-linux-x64.tar.gz" \
&& tar xf heroku-linux-x64.tar.gz
FROM node:18.16 ## in this case we don't need to define the stage name
WORKDIR /opt/my-app/
COPY --from=node-build /opt/my-app/node_modules /opt/my-app/node_modules
COPY --from=install-heroku /heroku/bin/heroku /usr/local/bin
COPY . /opt/my-app/
ENTRYPOINT [ "/usr/local/bin/npm" ]
## or enforce a single parameter when running the container
## [ "/usr/local/bin/npm", "start" ]
FROM node:18.16 AS node-build
WORKDIR /opt/my-app/
COPY package.json /opt/my-app/
RUN npm install
FROM alpine AS install-heroku ## defining the stage name as install-heroku
RUN apk update \
&& apk add curl
ENV HEROKU_VERSION="stable"
RUN curl -LO "https://cli-assets.heroku.com/channels/${HEROKU_VERSION}/heroku-linux-x64.tar.gz" \
&& tar xf heroku-linux-x64.tar.gz
FROM node:18.16 ## in this case we don't need to define the stage name
WORKDIR /opt/my-app/
COPY --from=node-build /opt/my-app/node_modules /opt/my-app/node_modules
COPY --from=install-heroku /heroku/bin/heroku /usr/local/bin
COPY . /opt/my-app/
ENTRYPOINT [ "/usr/local/bin/npm" ]
## The user can run the docker and pass any argument, the default parameter will be start
## e.g. to run `npm test` instead of `npm start`:
## docker run -p 5001:5001 -it zero-to-hero-docker-example test
CMD [ "start" ]
Build the Docker based on the previous example
Running the docker
Exposing a local port and accessing it from your local machine
cd zero-to-hero/docker-container/docker-example/node-js-getting-started
cd zero-to-hero/docker-container/docker-example/node-js-getting-started
docker build -t zero-to-hero-docker-example .
cd zero-to-hero/docker-container/docker-example/node-js-getting-started
docker build -t zero-to-hero-docker-example .
docker run --rm -p 5001:5001 -it zero-to-hero-docker-example
## or run the `node test`
## docker run --rm -p 5001:5001 -it zero-to-hero-docker-example test
cd zero-to-hero/docker-container/docker-example/node-js-getting-started
docker build -t zero-to-hero-docker-example .
## docker build [OPTIONS] PATH | URL | -
## `-t`: Same as `--tag`, name and optionally a tag in the 'name:tag' format
## `zero-to-hero-docker-example`: A image name without specifying a tag.
## The tag will be latest if not specified
## `.`: The path to the Dockerfile, `.` is the current folder
cd zero-to-hero/docker-container/docker-example/node-js-getting-started
docker build -t zero-to-hero-docker-example .
docker run --rm -it -p 5001:5001 zero-to-hero-docker-example
## or docker run --rm -p 5001:5001 -it zero-to-hero-docker-example test
## docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
## `--rm`: Automatically remove the container when it exits
## `-i`: Same as `--interactive`, keep STDIN open even if not attached
## `-t`: Same as `--tty`, allocate a pseudo-TTY
## `test`: A command that was specified in the package.json file
Bad practices, like
Best practices, like
Small docker image.
Organize your Dockerfile in a good way and guarantee the caching
Use Multi-platform to support different CPU architectures, like ARM
Use multi-stage for parallel building
Graceful shutdown with dumb-init or tini
Rotless container with podman
Beyond the app, use it for toollbox and keep your machine clean