To Reproduce
- On a Dokploy install with the
dokploy-traefik container already running, create two separate Docker Compose projects, each with a service that needs to be reached on a custom host port (e.g. via Settings → Server → Traefik → Additional Port Mappings), where both services' containers listen internally on the same container port (80, the common case for any web app's own HTTP server) — e.g. Project A should be reachable on host port 8085 (container port 80), Project B on host port 8888 (container port 80).
- Save an Additional Port Mapping of
8085 → 80/tcp for Project A. It applies correctly (docker ps shows dokploy-traefik publishing 8085->80).
- Now add a second mapping
8888 → 80/tcp for Project B without removing the first one — i.e. submit both entries together (this is the only way to have both live at once, since each save call is a full-array replace, not a merge).
- Inspect the resulting
dokploy-traefik container's ports (docker inspect dokploy-traefik, or docker ps).
I confirmed this against settings.updateTraefikPorts directly (via the API), sending:
{
"additionalPorts": [
{"targetPort": 80, "publishedPort": 8085, "protocol": "tcp"},
{"targetPort": 80, "publishedPort": 8888, "protocol": "tcp"}
]
}
Current vs. Expected behavior
Expected: dokploy-traefik publishes both 8085->80 and 8888->80 simultaneously — Docker's own port-binding model supports multiple host ports bound to the same container port (HostConfig.PortBindings["80/tcp"] can be an array of several {HostPort: ...} entries).
Observed: only the last entry in the additionalPorts array survives — the container ends up with 8888->80 only, 8085 silently dropped, even though the API call itself returns success (200, {"result":{"data":{"json":true}}}) and no error is surfaced anywhere.
Root cause (traced in the source, current canary branch and v0.30.7):
packages/server/src/setup/traefik-setup.ts, inside the function that builds PortBindings for the standalone dokploy-traefik container:
for (const port of additionalPorts) {
const portKey = `${port.targetPort}/${port.protocol ?? "tcp"}`;
portBindings[portKey] = [{ HostPort: port.publishedPort.toString() }];
}
portBindings is a plain object keyed by `${targetPort}/${protocol}`. Since both entries above share targetPort: 80, they collide on the same key "80/tcp", and each iteration overwrites the previous assignment instead of appending to the existing array. The fix should be to push onto any existing array for that key instead of replacing it, e.g.:
portBindings[portKey] = portBindings[portKey] ?? [];
portBindings[portKey].push({ HostPort: port.publishedPort.toString() });
I believe the equivalent Swarm-mode code path (additionalPorts.map((port) => ({ TargetPort: ..., PublishedPort: ... })), a few lines further down in the same file) does not have this problem since Swarm's EndpointSpec.Ports is a flat array rather than an object keyed by target port — this only affects the standalone (non-Swarm) container path.
This also explains a separate, related symptom: settings.getTraefikPorts (the read used by the "Additional Port Mappings" UI section) can report [] even right after a successful updateTraefikPorts call whose container recreation is visibly applied via docker ps — because readPorts reads back the live container's bound ports, and by the time you're troubleshooting a "mapping disappeared" report, the one that disappeared was never live to read back in the first place.
Provide environment information
Operating System:
OS: Linux (VM), self-hosted
Dokploy version: 0.30.7
VPS Provider: self-hosted VM (not a listed cloud provider)
What applications/services are you trying to deploy?
Two independent Docker Compose projects, each with an nginx-fronted web app + API service, both needing a custom Additional Port Mapping to container port 80.
Which area(s) are affected?
Traefik, Docker, Docker Compose
Are you deploying the applications where Dokploy is installed or on a remote server?
Same server where Dokploy is installed
Additional context
Confirmed by directly calling settings.updateTraefikPorts via the API (not just through the UI) and inspecting the resulting container's NetworkSettings.Ports/HostConfig.PortBindings via docker inspect — so this isn't a UI-only rendering issue, the container itself only ever ends up with the last additional port per unique targetPort. This matches (and I believe explains the underlying cause of) the symptoms described in #1987, #804, and #3125, though none of those pinpoint this specific overwrite-vs-append bug in traefik-setup.ts.
Will you send a PR to fix it?
Maybe, need help
To Reproduce
dokploy-traefikcontainer already running, create two separate Docker Compose projects, each with a service that needs to be reached on a custom host port (e.g. via Settings → Server → Traefik → Additional Port Mappings), where both services' containers listen internally on the same container port (80, the common case for any web app's own HTTP server) — e.g. Project A should be reachable on host port8085(container port80), Project B on host port8888(container port80).8085 → 80/tcpfor Project A. It applies correctly (docker psshowsdokploy-traefikpublishing8085->80).8888 → 80/tcpfor Project B without removing the first one — i.e. submit both entries together (this is the only way to have both live at once, since each save call is a full-array replace, not a merge).dokploy-traefikcontainer's ports (docker inspect dokploy-traefik, ordocker ps).I confirmed this against
settings.updateTraefikPortsdirectly (via the API), sending:{ "additionalPorts": [ {"targetPort": 80, "publishedPort": 8085, "protocol": "tcp"}, {"targetPort": 80, "publishedPort": 8888, "protocol": "tcp"} ] }Current vs. Expected behavior
Expected:
dokploy-traefikpublishes both8085->80and8888->80simultaneously — Docker's own port-binding model supports multiple host ports bound to the same container port (HostConfig.PortBindings["80/tcp"]can be an array of several{HostPort: ...}entries).Observed: only the last entry in the
additionalPortsarray survives — the container ends up with8888->80only,8085silently dropped, even though the API call itself returns success (200,{"result":{"data":{"json":true}}}) and no error is surfaced anywhere.Root cause (traced in the source, current
canarybranch andv0.30.7):packages/server/src/setup/traefik-setup.ts, inside the function that buildsPortBindingsfor the standalonedokploy-traefikcontainer:portBindingsis a plain object keyed by`${targetPort}/${protocol}`. Since both entries above sharetargetPort: 80, they collide on the same key"80/tcp", and each iteration overwrites the previous assignment instead of appending to the existing array. The fix should be to push onto any existing array for that key instead of replacing it, e.g.:I believe the equivalent Swarm-mode code path (
additionalPorts.map((port) => ({ TargetPort: ..., PublishedPort: ... })), a few lines further down in the same file) does not have this problem since Swarm'sEndpointSpec.Portsis a flat array rather than an object keyed by target port — this only affects the standalone (non-Swarm) container path.This also explains a separate, related symptom:
settings.getTraefikPorts(the read used by the "Additional Port Mappings" UI section) can report[]even right after a successfulupdateTraefikPortscall whose container recreation is visibly applied viadocker ps— becausereadPortsreads back the live container's bound ports, and by the time you're troubleshooting a "mapping disappeared" report, the one that disappeared was never live to read back in the first place.Provide environment information
Which area(s) are affected?
Traefik, Docker, Docker Compose
Are you deploying the applications where Dokploy is installed or on a remote server?
Same server where Dokploy is installed
Additional context
Confirmed by directly calling
settings.updateTraefikPortsvia the API (not just through the UI) and inspecting the resulting container'sNetworkSettings.Ports/HostConfig.PortBindingsviadocker inspect— so this isn't a UI-only rendering issue, the container itself only ever ends up with the last additional port per uniquetargetPort. This matches (and I believe explains the underlying cause of) the symptoms described in #1987, #804, and #3125, though none of those pinpoint this specific overwrite-vs-append bug intraefik-setup.ts.Will you send a PR to fix it?
Maybe, need help