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.
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 bash
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.envloaded 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
./devscript 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 --volumes
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]
WORKDIRmounts 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"