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
- Thousands of those NPM dependencies are now prevented from getting any glimpse of the host.
-
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 versionOptionally, make the ./dev convenience shell script runnable:
chmod +x ./devThis will allow us to avoid typing the verbose docker compose yada yada yada every time we want to spin up our dev environment.
For the first run, we must enter shell in order to first set up NPM/PNPM/Yarn/Bun (e.g. install vite as a dep):
./dev bashCreate 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.
# List
docker ps -a
docker volume ls
docker image ls
# Clean
docker builder prune --all --force
docker system prune --all --volumes --force
# Caution: deletes volumes permanently
docker compose down --volumesThe 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:containerWe 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@latestThen, ensure that docker-compose.yml knows what to run by default:
command: >
bash -c "pnpm i && pnpm dev:docker"