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.