Creating the Perfect AdonisJS Docker Image

Creating the Perfect AdonisJS Docker Image

Dockerizing your AdonisJS application can be a good idea if you want to run your application in different environments.

I have created a Dockerfile I re-use regularly for my projects that I have found to work well.

The Dockerfile is based on a Dockerfile created by Romain Lainz from the AdonisJS team, with a few modifications.

The Dockerfile

FROM node:20-alpine as base

RUN apk --no-cache add curl wget

# All deps stage
FROM base as deps
WORKDIR /app
ADD package.json package-lock.json ./
RUN npm ci

# Production only deps stage
FROM deps as production-deps
WORKDIR /app
RUN npm prune --production
RUN wget https://gobinaries.com/tj/node-prune --output-document - | /bin/sh && node-prune

# Build stage
FROM deps as build
WORKDIR /app
ADD . .
RUN node ace build

FROM base
WORKDIR /app
# Copy built application
COPY --from=production-deps /app/node_modules /app/node_modules
COPY --from=build /app/build /app
COPY --from=build /app/ecosystem.config.cjs /app
RUN npm i -g pm2

# Start the server by default, this can be overwritten at runtime
EXPOSE 3000
ENV CACHE_VIEWS="true" \
    HOST="0.0.0.0" \
    PORT="3000" \
    LOG_LEVEL="info" \
    SESSION_DRIVER="cookie" \
    DB_CONNECTION=pg \
    APP_NAME="my-app"
CMD [ "pm2-runtime", "start", "/app/ecosystem.config.cjs"]

The Dockerfile follows some principles:

  • Each layer is cached, leading to faster builds
  • node-prune is used to decrease the size of the node_modules folder
  • pm2 is used to run the application inside of Docker

I added pm2 to my Dockerfile because I restart my applications around every 6 hours. It also allows me to run the applications in cluster mode without spinning up multiple containers.

Here is a typical ecosystem.config.cjs file:

// eslint-disable-next-line unicorn/prefer-module
module.exports = {
  apps: [
    {
      name: 'my-app',
      script: 'bin/server.js',
      instances: 2,
      kill_timeout: 10000,
      cron_restart: '10 */6 * * *',
    },
  ],
}

This configuration runs 2 instances of my application within the same Dockerfile, and periodically restarts the server.