Isolate your Node.js processes.
This could be used for running a local dev server like Vite, Next.js or Nest.js.
Why do this?
Docker, especially in rootless mode, allows for:
Greater security
Improved reproducibility
Platform compatibility issues like environment variables on Windows vs UNIX.
Developers working on the app get the exact same result every time, no matter if they are running Windows, macOS or Linux.
Ensure Docker Engine and Docker Compose are set up. Refer to platform-specific instructions: https://docs.docker.com/engine/install
With Docker Desktop (GUI) your mileage may vary.
To verify, the following commands should print:
docker -v
docker compose version
Optionally, make the ./dev
convenience shell script runnable:
chmod +x ./dev
This will allow us to avoid typing the verbose docker compose yada yada yada
every time we want to spin up our dev environment.
Create an .env
or remove env_file
in docker-compose.yml
.
Alternatively, env vars can be hardcoded (or otherwise piped in):
environment:
- DEBUG=${DEBUG}
[!NOTE] Variables via docker-compose will be available in the environment of the container, i.e. OS level.
This is identical to how env vars would be available during a CI/CD build.
For development, prefer.env
loaded via framework tooling with auto-refresh, e.g. Vite dev server.
See 💡 Docker Docs
Specify host:container
ports in docker-compose.yml
.
If our node.js process inside the container runs on 5173
but we want to avoid clashing with something else on our host machine, we could map it like so: 5174:5173
.
See 💡 Docker Docs
Command | Description |
---|---|
./dev |
Start development. Runs docker-compose.yml:15 and dev:docker . |
./dev stop |
Stop container explicitly. |
./dev bash |
Enter shell to execute commands inside the container. |
./dev logs |
View rolling logs (if you've closed them). |
./dev any-command |
Pass any command to be executed inside the container (instead of bash ). |
[!IMPORTANT] Ensure the
./dev
script is chmoded.
docker ps -a
docker volume ls
docker image ls
docker builder prune --all --force
docker system prune --all --volumes --force
The repository root .
(as in, current dir) is mounted as /app
within the container using WORKDIR
.
This means that all files in the repository root are available to any node.js process. For example,.env.prod
or other secrets.
[!TIP]
WORKDIR
mounts everything as a filesystem volume, granting us real-time two-way synchronization. Useful for actual development work.COPY
, in contrast, copies files over once at container build-time and respects.dockerignore
.
To completely isolate a node.js app, we can mount a subdirectory instead of root .
:
volumes:
- ./app:/app # host:container
We then place package.json
under app/
along with the rest of our application-specific sourceode.
Want to use PNPM or another package manager like Yarn or Bun?
Modify Dockerfile
along these lines:
# ...
WORKDIR /app
ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0
ENV PNPM_HOME=/usr/local/share/.pnpm-store
ENV PATH=$PNPM_HOME:$PATH
RUN mkdir -p $PNPM_HOME
RUN npm install -g npm@latest corepack@latest
RUN corepack enable pnpm
RUN corepack use pnpm@latest
Then, ensure that docker-compose.yml
knows what to run by default:
command: >
bash -c "pnpm i && pnpm dev:docker"