You started a container with docker run, the app is working, and then you realize you mapped the wrong host port, or forgot the -p flag entirely. Now you want to fix the mapping without losing what's inside the container.
Here's the short version: you can't change the port mapping on an existing container. Port publishing is set at creation time. When you publish a port, Docker writes the firewall and Network Address Translation (NAT) rules on the host that are wired up the moment the container is created, and there's no docker update flag to rewire them afterward. The only supported way to change a mapping is to recreate the container with the new -p value.
Recreating sounds more disruptive than it is. Most of the time it's three commands. The wrinkle is when the container holds state you can't easily rebuild, which is where docker commit and Compose earn their place.
Recreate the container with the new mapping
This is the correct fix for almost every case. First, check what's currently running so you know the image and current mapping:
1docker ps
12CONTAINER ID IMAGE COMMAND STATUS PORTS NAMESa0ed1c9fc60c httpd "httpd-foreground" Up 2 minutes 0.0.0.0:81->80/tcp web
The container web is mapping host port 81 to container port 80, and you want it on host port 8080 instead. Stop and remove the container, then run a fresh one from the same image with the corrected -p flag:
123docker stop webdocker rm webdocker run -d --name web -p 8080:80 httpd
1b7d2f4a91c33e8a0c5e6f1b2d3a4c5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2
Confirm the new mapping took effect:
1docker ps
12CONTAINER ID IMAGE COMMAND STATUS PORTS NAMESb7d2f4a91c33 httpd "httpd-foreground" Up 3 seconds 0.0.0.0:8080->80/tcp web
The catch with this approach is that removing a container deletes its writable layer, so anything written inside the container at runtime (logs, uploaded files, database state) is gone. If the container holds data you care about, that data should be living in a Docker volume or bind mount, not in the container's writable layer. With a volume attached, recreating the container is completely safe because the data exists independently of the container lifecycle:
1docker run -d --name web -p 8080:80 -v web-data:/usr/local/apache2/htdocs httpd
If you find yourself worrying about losing state every time you recreate a container, that's a signal your important data isn't on a volume yet. Fix that first, and port changes stop being scary.
When the container holds state you can't recreate
Sometimes you started a container without a volume, made changes inside it (installed packages, tweaked some config by hand), and only then realized the port mapping is wrong. Recreating from the original image would throw all of that away. In this situation, docker commit lets you snapshot the running container into a new image and start a fresh container from that snapshot with the right ports:
1234docker stop webdocker commit web web-snapshotdocker rm webdocker run -d --name web -p 8080:80 web-snapshot
Treat this as a recovery move, not a habit. A committed image is an opaque snapshot with no Dockerfile behind it, so it isn't reproducible or rebuildable from source. Use it to rescue state you'd otherwise lose, then port whatever manual changes you made back into a proper Dockerfile so the next rebuild is clean.
Manage ports declaratively with Docker Compose
If you change port mappings often, or you're tired of remembering the exact docker run flags every time you recreate a container, move the definition into a Compose file. Compose makes the port mapping a line of version-controlled configuration instead of a command you have to reconstruct from memory.
A minimal compose.yaml for the example above looks like this:
12345678910services:web:image: httpdports:- "8080:80"volumes:- web-data:/usr/local/apache2/htdocsvolumes:web-data:
To change the mapping, edit the ports value and re-apply:
1docker compose up -d
12[+] Running 1/1✔ Container web Recreated
Compose detects that the service definition changed and recreates only the affected container, leaving everything else untouched. Because the volume is declared in the same file, your data survives the recreate. This is the maintainable answer to "how do I change the port": you edit one line of config and let Compose reconcile the running state to match.
Common pitfalls
Editing the container config files directly. Search results will point you at hostconfig.json and config.v2.json under /var/lib/docker/containers/<id>/, where the PortBindings field lives. Editing those files does technically work, but it requires stopping the entire Docker daemon (every container on the host goes down, not just the one you're changing), the format is undocumented and changes between releases, and a malformed edit can corrupt the container so it won't start at all. The daemon may also overwrite your changes. There's no upside over recreating the container, and a lot of downside. Don't reach for this on anything you care about.
Mapping to a port the app isn't listening on. Publishing with -p only forwards traffic from the host port to the container port. It does nothing to make the process inside actually listen there. If your app listens on 3000 inside the container, -p 8080:5000 will connect to nothing. Check the container port the process binds to before assuming the mapping is wrong.
Accidentally exposing the port to the world. A plain -p 8080:80 binds to 0.0.0.0, which means the port is reachable from outside the host, not just from the host itself. If you only need local access, bind to the loopback address explicitly with -p 127.0.0.1:8080:80. This matters most on cloud virtual machines, where a publicly routable interface turns an internal service into an open one.
Final thoughts
Recreating a container changes its ID and resets its uptime, which means any monitoring keyed to the old container ID needs to follow the new one. Beyond that, a wrong or missing port mapping is the kind of thing you usually discover by a connection timing out, or by scanning the container's logs for a bind error, rather than by an alert. That's exactly the sort of blind spot good observability closes.
Dash0's infrastructure monitoring tracks container health and resource usage alongside real-time logs and distributed traces, so you can see whether a service is actually reachable instead of finding out from a failed request.
Start a free trial to monitor your containers, hosts, and services in one view. No credit card required.