Dokploy / Dokploy/dokploy

Allow Traefik port mappings to bind to specific host IPs

Open
#5,157 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement
Dominant language
TypeScript
Stars
37.4k
Forks
3k
Avg merge
1d 3h
Merged PRs (30d)
73

Description

What problem will this feature address?

Dokploy currently does not allow Traefik port bindings to be associated with a specific host IP/interface.

This is useful on multi-homed hosts that have, for example:

  • a public IPv4 address;
  • a Tailscale / NetBird / WireGuard address;
  • potentially other private or management interfaces.

A concrete use case is running two Traefik entrypoints on the same external port while keeping them isolated at the network-binding level:

Public interface:
203.0.113.10:443 -> Traefik :443

Private VPN interface:
100.100.100.10:443 -> Traefik :8443

For example:

entryPoints:
  websecure:
    address: :443

  websecure-private:
    address: :8443

The public entrypoint serves public applications, while websecure-private is reachable only through the Tailscale/VPN interface.

Docker supports this for standalone containers:

-p 203.0.113.10:443:443/tcp
-p 100.100.100.10:443:8443/tcp

However, Dokploy currently models Traefik Additional Port Mappings using only:

targetPort
publishedPort
protocol

and recreates the standalone dokploy-traefik container without a HostIp.

As a result, an additional mapping such as:

targetPort: 8443
publishedPort: 443

becomes effectively:

0.0.0.0:443 -> 8443

while the standard Traefik HTTPS mapping is already:

0.0.0.0:443 -> 443

This produces two conflicting bindings on 0.0.0.0:443.

I encountered this in practice after changing another Traefik setting in Dokploy. Dokploy recreated the Traefik container, the IP-specific mappings were lost, and Docker refused to start the new container with an error equivalent to:

failed to set up container networking:
Bind for 0.0.0.0:443 failed: port is already allocated

The dokploy-traefik container remained in Created state, which made every service behind Traefik unavailable, including the Dokploy UI itself.

Manually recreating the standalone Traefik container with IP-specific bindings immediately resolves the problem:

203.0.113.10:80  -> 80
203.0.113.10:443 -> 443/tcp
203.0.113.10:443 -> 443/udp

100.100.100.10:443 -> 8443/tcp

This is particularly useful for setups where administrative/private services should be reachable only through Tailscale or another VPN, without relying solely on application-layer IP filtering.

This request is related to #2915 and #4715, but there is an important distinction: those discussions mostly concern published ports of Docker Swarm services.

This use case specifically concerns Dokploy's standalone dokploy-traefik container, for which Docker does support binding published ports to a specific host IP.

Describe the solution you'd like

Allow an optional host/bind IP to be configured for Traefik port mappings.

Ideally, this should apply to both:

  1. the standard Traefik HTTP/HTTPS/HTTP3 bindings;
  2. each Additional Port Mapping.

Supporting a Host IP only for Additional Port Mappings would not be sufficient.

For example, this is still impossible:

0.0.0.0:443        -> Traefik :443
100.100.100.10:443 -> Traefik :8443

because the first mapping already occupies port 443 on every host interface.

The desired configuration is:

203.0.113.10:443   -> Traefik :443
100.100.100.10:443 -> Traefik :8443

Conceptually, the additional-port model could become something similar to:

additionalPorts?: {
  targetPort: number;
  publishedPort: number;
  protocol?: string;
  hostIp?: string;
}[];

For the standalone Traefik container, the corresponding Docker PortBinding could then include:

{
  HostIp: port.hostIp ?? "",
  HostPort: port.publishedPort.toString()
}

The UI could expose an optional field such as:

Target Port:    8443
Published Port: 443
Protocol:       TCP
Host IP:        100.100.100.10

Leaving Host IP empty should preserve the current behavior and bind to all interfaces, so the change can remain backward-compatible.

The standard Traefik bindings should have equivalent optional bind-address configuration, for example:

Standard HTTP:
Host IP:        203.0.113.10
Published Port: 80

Standard HTTPS:
Host IP:        203.0.113.10
Published Port: 443

Additional Port:
Host IP:        100.100.100.10
Published Port: 443
Target Port:    8443
Protocol:       TCP

Port-conflict validation should also take the bind IP into account rather than treating the published port alone as the unique resource.

For example, these mappings should be valid:

203.0.113.10:443/tcp   -> 443
100.100.100.10:443/tcp -> 8443

while these should remain invalid:

0.0.0.0:443/tcp -> 443
0.0.0.0:443/tcp -> 8443

and:

100.100.100.10:443/tcp -> 443
100.100.100.10:443/tcp -> 8443

It would also be valuable for Dokploy to validate that a specified bind IP exists on the host before applying the configuration.

Ideally, updating Traefik ports or environment variables should also be fail-safe: if the newly created Traefik container cannot start, Dokploy should avoid leaving the currently working reverse proxy permanently unavailable.

Describe alternatives you've considered

Several workarounds exist, but none provide the same combination of network-level isolation and configuration persistence inside Dokploy.

1. Manually recreate dokploy-traefik with IP-specific Docker bindings

This works correctly:

docker run ... \
  -p 203.0.113.10:80:80/tcp \
  -p 203.0.113.10:443:443/tcp \
  -p 203.0.113.10:443:443/udp \
  -p 100.100.100.10:443:8443/tcp \
  traefik:...

However, this configuration exists outside Dokploy's current port model.

Any Dokploy operation that recreates the Traefik container can lose the IP-specific bindings and replace them with bindings on all interfaces.

This makes the workaround fragile and can prevent Traefik from starting.

2. Use the same public :443 entrypoint for both public and private services and apply a Traefik IPAllowList middleware

This is simpler and can restrict private routers to the Tailscale CIDR.

However, it changes the security model: the private entrypoint/routers are still listening through the public network interface and access control happens at the reverse-proxy layer.

For some installations, it is preferable for the private entrypoint to never be bound to the public interface at all.

3. Host firewall / iptables rules

Filtering traffic at the host level can provide similar restrictions.

However, this adds configuration outside Dokploy, needs to interact correctly with Docker networking rules, and still does not allow Dokploy to faithfully reconstruct the intended Docker port bindings.

4. Run a separate socat or proxy container

A proxy can bind exclusively to the VPN address and forward traffic internally to Traefik.

This works but adds another container, another proxy layer, additional configuration, and another component to maintain for something Docker standalone port bindings already support natively.

5. Use Tailscale Serve, Cloudflare Tunnel, or another private access proxy

These are valid alternative architectures.

However, they introduce a separate ingress mechanism and do not solve the more general requirement of allowing Dokploy's Traefik to bind different entrypoints to different host interfaces.

Additional context

Observed environment:

Dokploy: v0.29.14
Traefik: v3.6.7
Deployment: single-node self-hosted Dokploy
Traefik: standalone container managed by Dokploy
Private network: Tailscale

The current Traefik setup code models Additional Port Mappings with targetPort, publishedPort, and protocol, but does not expose a HostIp for standalone Docker PortBindings.

The standard HTTP/HTTPS/HTTP3 bindings are also created without a HostIp.

Related issues:

  • #2915 — Allow Binding Published Ports to a Specific Host IP for Enhanced Security
  • #4715 — Allow binding published ports to 127.0.0.1 only
  • #3125 — Additional port mapping: crash on used port / validation
  • #3313 — Traefik recreation when modifying Additional Port Mappings
  • #4048 — IP Allow-List Filtering for Exposed Ports

#2915 is especially close to the desired behavior and includes VPN/Tailscale/NetBird use cases, but its discussion identifies limitations related to Docker Swarm service publishing.

The specific request here is for Dokploy's standalone Traefik container, where Docker PortBindings do support a HostIp.

Besides enabling VPN-only/private Traefik entrypoints, this capability could also be useful for:

  • hosts with multiple public interfaces;
  • dedicated management networks;
  • LAN-only entrypoints;
  • loopback-only bindings;
  • VPN-only entrypoints;
  • separating trusted and untrusted ingress interfaces;
  • IPv4/IPv6-specific exposure where applicable.

A particularly useful use case is keeping public and private HTTPS entrypoints on the standard client-facing port 443 while binding them to different interfaces:

Public IP:443 -> public Traefik entrypoint
VPN IP:443    -> private Traefik entrypoint

This lets users keep normal HTTPS URLs for both public and private services without exposing the private entrypoint on the public interface.

I would be happy to test an implementation with a multi-interface/Tailscale setup.

Will you send a PR to implement it?

Maybe, need help

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with Dokploy’s current Traefik setup code and the standalone container’s Docker PortBindings, including the standard and additional port mapping paths. Trace how port mappings are modeled, rendered in the UI, validated, and used during container recreation. Done means optional Host IPs work for all requested bindings, conflicts account for bind IP and protocol, empty values preserve current behavior, and failed recreation does not leave the proxy unavailable.

Written by the indexing model from the issue text.

Assessment

Tech stack
docker, typescript
Domain
devops, infrastructure
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.