Environment Variables, Interactive Flags, and Hot‑Reload Comforts
Part 4 explored volume flavours and production‑only installs. Today we wire *.env files** into Docker Compose, learn what
stdin_open
andtty: true
really do, and keep our live‑reload Node loop humming along.*
Learning Aims
-
Inject key/value pairs from a
.env
file into both your container and your Compose YAML (port mapping, secrets, feature flags). - Understand how
stdin_open: true
andtty: true
make a container behave like a friendly terminal—critical for interactive CLIs and nodemon. - Read a Dockerfile and Compose file token‑by‑token, with extra focus on environment‑variable sections.
1 Project Snapshot
docker-with-env-file/
├─ Dockerfile
├─ docker-compose.yml
├─ .env # PORT=3000
├─ package.json # dev=nodemon script
└─ index.js # prints Hello from port $PORT
index.js
uses dotenv to read the port from environment variables:
require("dotenv").config();
const express = require("express");
const app = express();
const PORT = process.env.PORT;
app.get("/", (_, res) => res.send(`Hello from port ${PORT}!`));
app.listen(PORT, () => console.log(`🚀 Server running on ${PORT}`));
2 Dockerfile — Word‑by‑Word
FROM node:20-alpine # 1
WORKDIR /usr/src/app # 2
# 1️⃣ copy manifests first for npm cache
COPY package*.json ./ # 3
RUN npm i && npm cache clean --force # 4
# 2️⃣ copy source last
COPY . . # 5
CMD ["npm", "run", "dev"] # 6
# | Token | Beginner‑friendly explanation |
---|---|---|
1 | FROM node:20-alpine |
Start from a slim Alpine image with Node 20. Smaller download, faster build. |
2 | WORKDIR /usr/src/app |
Sets the current directory inside the image. Docker will create it. |
3 | COPY package*.json ./ |
Copies package.json and package-lock.json . Doing this before the rest of your code lets Docker reuse the dependency layer when only source files change. |
4 | RUN npm i && npm cache clean --force |
Installs deps, then wipes the npm cache to shed ≈20 MB. For dev we keep devDependencies so nodemon is available. |
5 | COPY . . |
Drops your source code into the image. Cached npm layer remains intact. |
6 | CMD [...] |
This specifies the command to run when a container is started from this image. In this case, we're running npm run dev , which in our package.json is configured to start the server using nodemon for automatic restarts on file changes. |
3 docker-compose.yml — Token‑by‑Token
version: "3.9"
services:
app:
build: .
container_name: env_demo
command: npm run dev
ports:
- "${PORT}:${PORT}"
env_file:
- .env
volumes:
- .:/usr/src/app
- /usr/src/app/node_modules
environment:
CHOKIDAR_USEPOLLING: "true"
CHOKIDAR_INTERVAL: "1000"
stdin_open: true
tty: true
A docker-compose.yml
file is used to define and run multi-container Docker applications. It's a powerful tool for managing the services, networks, and volumes for your application in a single file.
version: "3.9"
-
version
: This specifies the version of the Docker Compose file format we're using.
services:
-
services
: This is where we define the different services (containers) that make up our application. In this case, we only have one service, which we've namedapp
.
app:
build: .
-
build: .
: This tells Docker Compose to build the image for this service using theDockerfile
in the current directory (.
).
container_name: env_demo
-
container_name
: This sets a custom name for the container, making it easier to identify.
command: npm run dev
-
command
: This overrides the defaultCMD
in theDockerfile
. Here, we're explicitly telling the container to runnpm run dev
.
ports:
- "${PORT}:${PORT}"
-
ports
: This maps a port on the host machine to a port in the container. The${PORT}
syntax is a variable that will be substituted with the value from our.env
file. This allows us to access the application from our web browser.
env_file:
- .env
-
env_file
: This is a crucial part of our setup. It tells Docker Compose to load environment variables from a file named.env
in the same directory.
volumes:
- .:/usr/src/app
- /usr/src/app/node_modules
-
volumes
: This creates a link between a directory on our host machine and a directory inside the container. This is incredibly useful for development because any changes we make to our code on our local machine will be immediately reflected inside the container without needing to rebuild the image.- The first volume (
.:/usr/src/app
) maps our project directory to the container's working directory. - The second volume (
/usr/src/app/node_modules
) is a way to prevent thenode_modules
directory on our local machine from overwriting the one inside the container.
- The first volume (
environment:
CHOKIDAR_USEPOLLING: "true"
CHOKIDAR_INTERVAL: "1000"
-
environment
: This allows us to set additional environment variables directly in thedocker-compose.yml
file. These variables are used to configurenodemon
to watch for file changes correctly within the Docker environment.
4 Running the Stack
docker compose up
What happens:
Compose reads .env
in the project root, sets PORT=3000 for substitution.
Image builds (if absent) and container starts.
Nodemon launches on port 3000; visit http://localhost:3000
to see Hello from port 3000!.
Edit index.js
→ nodemon restarts thanks to bind mount.
Changing .env
Edit .env
to PORT=4000
.
Stop the container (Ctrl +C).
Re‑run docker compose up.
Compose rebuilds nothing, but passes the new port; mapping changes to 4000:4000
and Express listens on the new value.
Containers only read env vars at start‑time, so a restart is required.
5 What if .env Goes Missing?
${PORT}
in ports: would expand to blank, causing a Compose error: “invalid published port”.
Inside the container process.env.PORT
would be undefined, and Express would crash or fall back to a default if coded.
Always commit an example .env.sample
and use CI to validate required vars.
6 Conclusion
✅ Feeding secrets & configs from .env
files into both Compose tokens and container environments.✅ The role of stdin_open (‑i)
and tty: true (‑t)
for interactive containers.✅ Variable‑precedence: environment: > env_file:
> project‑root .env
.✅ How to restart containers to pick up updated env vars.
Keep Docker‑ing!
Top comments (0)