Table of Contents
- Overview
- Setting Up a Basic NestJS Dockerfile
- Writing the Docker Compose File
- Integrating a Database Service
- Optimizing for Local Development
- Handling Build Arguments and Environment Variables
- Adding Health Checks and Dependencies
- Scaling Your NestJS Services with Docker Compose
- Using Docker Compose in Production
- Conclusion
Overview
If you’re looking to create a robust and efficient development environment for your NestJS applications, coupling NestJS with Docker Compose is a great approach. This setup will not only streamline your development process but also ensures consistent environments across different machines.
Prerequisites
- Basic understanding of Docker and its concepts
- Basic knowledge of NestJS framework and TypeScript
- Docker and Docker Compose installed on your development machine
- An existing NestJS project or willingness to create a new one
Setting Up a Basic NestJS Dockerfile
Let’s start by creating a simple Dockerfile
for your NestJS application. This file defines how your application’s Docker image should be built.
# Dockerfile
FROM node:latest
# Create app directory
WORKDIR /usr/src/app
# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm@5+)
COPY package*.json ./
RUN npm install
# If you are building your code for production
# RUN npm ci --only=production
# Bundle app source
COPY . .
EXPOSE 3000
CMD [ "npm", "run", "start:prod" ]
This Dockerfile does the following:
- Starts from the base Node.js image
- Creates a working directory for your application code
- Copies the package.json and potentially package-lock.json files and installs the dependencies
- Copies the remaining application code
- Exposes port 3000, which NestJS uses by default
- Sets the container to start your app using the production start command
Writing the Docker Compose File
Next, you’ll craft a docker-compose.yml
file that specifies how your Docker container should run. This includes defining services, volumes, and networks configuration.
# docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
volumes:
- .:/usr/src/app
- /usr/src/app/node_modules
environment:
NODE_ENV: production
command: npm run start:prod
The docker-compose.yml
does the following:
- Defines a service called
app
- Indicates that the service should be built from the current directory (where the Dockerfile resides)
- Maps port 3000 of the host to port 3000 of the container
- Sets up volume mapping to enable live reloads on code changes and to not override node_modules on your host machine
- Specifies environment variables
- Sets the command to start your application in production mode
Integrating a Database Service
Often you will need a database service in your application stack. Let’s introduce a PostgreSQL database service to our docker-compose.yml
.
# docker-compose.yml (extended)
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
volumes:
- .:/usr/src/app
- /usr/src/app/node_modules
environment:
NODE_ENV: production
depends_on:
- db
command: npm run start:prod
db:
image: postgres
environment:
POSTGRES_DB: your_db
POSTGRES_USER: user
POSTGRES_PASSWORD: password
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
The addition of the database service includes:
- A new service
db
using the PostgreSQL image - Environment variables for the database setup
- A named volume
pgdata
to persist database data - The app service now
depends_on
the database, ensuring the database starts first
Optimizing for Local Development
For a local development environment, you want to take advantage of NestJS’s hot-reload capabilities. Adjust your Docker Compose configuration for a better development experience.
# docker-compose.yml (optimized for development)
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
volumes:
- .:/usr/src/app
- /usr/src/app/node_modules
environment:
NODE_ENV: development
command: npm run start:dev
Significant changes include:
- Environment variable NODE_ENV set to development
- The command changed to
npm run start:dev
to support hot reloading
Handling Build Arguments and Environment Variables
If your application requires build-time variables or needs to inject environment variables more securely, you can utilize Docker Compose’s support for .env files and build arguments.
# docker-compose.yml (utilizing .env file)
version: '3.8'
services:
# ...
app:
build:
context: .
args:
- NODE_VERSION=${NODE_VERSION}
environment:
- DATABASE_URL=${DATABASE_URL}
A corresponding .env
file should have:
# .env file
NODE_VERSION=14
DATABASE_URL=postgres://user:password@db:5432/your_db
The above changes now mean:
- Use of a variable
NODE_VERSION
in the build process - An environment variable
DATABASE_URL
read from the.env
file when starting up
Adding Health Checks and Dependencies
Good container orchestration should also include health checks for your services. Taking it a step further, you can define the dependency condition based on service health.
# docker-compose.yml (with health checks)
version: '3.8'
services:
# ...
app:
# ...
depends_on:
db:
condition: service_healthy
db:
# ...
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user"]
interval: 10s
timeout: 5s
retries: 5
This update to the Docker Compose file ensures the app service does not start until the db service passes its health check.
Scaling Your NestJS Services with Docker Compose
Docker Compose allows you to scale out services, which can be useful during load testing or when your application traffic varies. This can be specified within the Compose file or through command-line options.
# Command to scale app service instances using Docker Compose CLI
docker-compose up --scale app=3 -d
The command docker-compose up --scale app=3 -d
starts three instances of the app
service.
Using Docker Compose in Production
When moving to production, remember to:
- Use an image registry instead of building images on the fly
- Ensure environment variables are kept secure
- Configure logging and monitoring for your services
- Utilize orchestration solutions like Kubernetes or Swarm if scaling is necessary
Conclusion
In this walkthrough, you’ve learned how to configure a Docker Compose environment for your NestJS application, integrate a database, optimize for development, handle configuration securely, add health checks, and prepare for scaling and production. Embrace the power of Docker to make your NestJS applications more resilient and portable across different environments.