Understanding Docker Port Mapping for a Next.js Application
Learn how ports work in a Dockerised Next.js setup with package.json, Dockerfile, and docker-compose.
๐ Overview
This guide explains how port configurations work in a Dockerised Next.js project. You'll understand the relationship between:
- The port defined in
package.json
- The internal port exposed via the
Dockerfile
- The host-to-container mapping in
docker-compose
Getting these right ensures smooth development and debugging experiences.
โ Key Components and Their Roles
Component | Example | Role | Must Match? |
---|---|---|---|
package.json | "start": "next start -p 3001" | Defines internal port Next.js listens to | โ Yes |
Dockerfile | EXPOSE 3001 | Declares internal port for documentation/tools | โ ๏ธ Recommended only |
docker-compose | ports: ["4001:3001"] | Maps host (4001) โ container (3001) | โ No โ host flexible |
๐ง How It Works (Step-by-Step)
- Next.js starts inside the container
"start": "next start -p 3001"
This defines the internal port your app binds to.
- Dockerfile declares EXPOSE 3001
EXPOSE 3001
This is only for documentation/tooling purposes. It does not affect the appโs behaviour. - docker-compose maps host to container
ports:
- "4001:3001"
Requests to http://localhost:4001
on the host are forwarded to port 3001 inside the container.
Example Scenario
package.json
"start": "next start -p 3001"
Dockerfile
EXPOSE 3001
ocker-compose.dev.yaml
ports:
- "4001:3001"
Behaviour
Access Point | What It Does |
---|---|
localhost:4001 | Entry point on host |
container:3001 | Where app is actually running |
โ Result | Docker forwards โ App responds |
๐ Note on NEXT_PUBLIC_PORT
NEXT_PUBLIC_PORT
is for frontend/browser use only.- It does not affect backend/server port binding.
- Always use
-p
flag innext start
to control server port.
๐งช Common Pitfall
Configuration | Result |
---|---|
next start -p 3002 + ports: "4001:3001" | โ Mismatch โ Browser fails |
next start -p 3002 + ports: "4001:3002" | โ Works |
โ Recommended Best Practices
- Use a consistent internal port across the stack:
"start": "next start -p 3001"
EXPOSE 3001
ports:
- "${HOST_PORT:-4001}:3001"
- Inject
NEXT_PUBLIC_PORT=${HOST_PORT}
into.env
to let your frontend build dynamic URLs correctly.
๐งพ Conclusion
next start -p xxx
: Defines app's internal portEXPOSE xxx
: Optional for documentation/toolingdocker-compose ports
: Maps host โ containerNEXT_PUBLIC_PORT
: Only for frontend use
With this knowledge, you'll avoid misconfigurations and ensure consistent behaviour across environments.