Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,57 @@ Now, any traffic sent to port `8080` on your host machine will be forwarded to p

> [!IMPORTANT]
>
> When a port is published, it's published to all network interfaces by default. This means any traffic that reaches your machine can access the published application. Be mindful of publishing databases or any sensitive information. [Learn more about published ports here](/engine/network/#published-ports).
> When a port is published, it's published to all network interfaces by default. This means any traffic that reaches your machine can access the published application. Be mindful of publishing databases or any sensitive information. See [Binding to specific network interfaces](#binding-to-specific-network-interfaces) below, and [learn more about published ports here](/engine/network/#published-ports).

### Binding to specific network interfaces

By default, when you publish a port, Docker binds to all network interfaces (`0.0.0.0`). However, there are scenarios where you might want to restrict access to a specific network interface or IP address. You can do this by using the extended port syntax:

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In terms of BSD socket API, binding to a specific interface isn't the same as binding on a specific IP address — both can be combined. This is done via SO_BINDTODEVICE.

I think we should not conflate both here. The Engine doesn't support binding to a specific interface, but it supports binding to a specific HostIP.

```console
$ docker run -d -p IP:HOST_PORT:CONTAINER_PORT nginx
```

- `IP`: The specific IP address or network interface on your host to bind to
- `HOST_PORT`: The port number on your host machine
- `CONTAINER_PORT`: The port number within the container

For example, if you want to make your container accessible only on localhost (127.0.0.1), you can use:

```console
$ docker run -d -p 127.0.0.1:8080:80 nginx
```

This restricts access to the container's port 80 to only local connections on your host's port 8080. External machines on your network would not be able to access this service. You can verify the binding with `docker ps`:

```console
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a527355c9c53 nginx "/docker-entrypoint.…" 4 seconds ago Up 3 seconds 127.0.0.1:8080->80/tcp elegant_newton
```

#### Common use cases for IP binding

- **Security**: Binding to localhost (127.0.0.1) to prevent external access to services like databases
- **Multi-homed hosts**: Binding to specific network interfaces on servers with multiple IP addresses
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not true — and this is where the claim that it can bind to a specific interface may instill a false sense of security.

If a port is mapped to a specific IP address, and the host has multiple interfaces, that IP address will be accessible through all interfaces by default.

This can be easily tested with the following commands on a host running the Engine (or from a dind container):

$ ip netns add test
$ ip link add name veth-dockerd type veth peer name veth-test
$ ip link set veth-test netns test
$ ip addr add 10.10.0.1/24 dev veth-dockerd
$ ip link set veth-dockerd up
$ ip netns exec test ip addr add 10.10.0.2/24 dev veth-test
$ ip netns exec test ip link set veth-test up

The 'multi-homed host' here is a dind container that has its original IP address (e.g. 172.17.0.3), plus 10.10.0.1.

You can then start a container targeting a specific HostIP:

$ docker run --rm -d -p 172.17.0.3:80:80 traefik/whoami

Finally, we can test whether 172.17.0.3:80 is accessible from the 'test' netns, but first we need to add a route to that address to let it know how it can reach it:

$ ip netns exec test ip route add 172.17.0.3/32 via 10.10.0.1
$ ip netns exec test ip -brief route show
10.10.0.0/24 dev veth-test proto kernel scope link src 10.10.0.2 
172.17.0.3 via 10.10.0.1 dev veth-test 

$ ip netns exec test curl http://172.17.0.3
Hostname: 4ab70ca9633d
IP: 127.0.0.1
IP: ::1
IP: 172.18.0.2
RemoteAddr: 10.10.0.2:43822
GET / HTTP/1.1
Host: 172.17.0.3
User-Agent: curl/7.88.1
Accept: */*

If users don't want that behavior, they need to set up firewall rules. (I'm not sure if that's clearly documented though.)

However, note that this is not true for loopback addresses (i.e. 127.0.0.0/8 and ::1/128) since v28.0 (see this blog post, and this changelog entry), as these aren't supposed to be routed anyway.

- **Service isolation**: Limiting which network segments can access certain containers

#### In Docker Compose
You can also use the IP binding syntax in your Docker Compose files:

```yaml
services:
webapp:
image: nginx
ports:
- "127.0.0.1:8080:80"

database:
image: postgres
ports:
- "127.0.0.1:5432:5432"
```

This configuration ensures your database is only accessible from the host machine itself, adding a layer of security to your application stack. You can also set a default bind address for all containers using the `host_binding_ipv4` configuration parameter. This allows you to change the default binding from `0.0.0.0` to a specific IP address. For more information, see [Setting the default bind address for containers](/engine/network/packet-filtering-firewalls/#setting-the-default-bind-address-for-containers).

### Publishing to ephemeral ports

Expand Down Expand Up @@ -125,6 +175,8 @@ If you’d like to dive in deeper on this topic, be sure to check out the follow

* [`docker container port` CLI reference](/reference/cli/docker/container/port/)
* [Published ports](/engine/network/#published-ports)
* [Network drivers](/engine/network/drivers/) - Learn more about Docker's networking capabilities
* [Docker network command](/engine/reference/commandline/network/) - Reference for the `docker network` command

## Next steps

Expand Down