## Intro

Docker bridge networking is the default networking mode for containers on a single host. In production, small misconfigurations can lead to IP conflicts, broken service discovery, or connectivity failures. This guide provides a practical operations checklist to help you manage bridge networks safely and reliably. You will learn how to inventory your environment, make scoped configuration changes, verify connectivity, recover from common failures, and run repeatable operational checks.

A clear bridge networking operations process reduces rework by separating research, implementation, and review. The first useful pilot should be narrow, measurable, and easy to inspect locally before deployment. The steps here are designed for developers, DevOps consultants, and technical startup teams who need a dependable bridge network setup without relying on overlay networks or external tooling.

Throughout this guide, you will find concrete commands with expected output and configuration examples. All examples use the default docker0 bridge and a custom bridge network called app-bridge. These are constructed for illustration and should be adapted to your environment.

## Version and Environment Inventory

Before making changes, record the versions and topology. This baseline helps identify compatibility issues and simplifies rollback. Assign a single owner, such as the platform engineer on call, to maintain this inventory and update it after any network change. Review the inventory quarterly or after major Docker upgrades.

### Docker Version

Run:

```bash
docker version --format '{{.Server.Version}}'
```

Expected output (example):

```
24.0.7
```

Note the client and server versions separately if they differ. Use `docker version` for full details. Inconsistent client and server versions can cause API mismatches and unexpected behavior, especially when using newer network options. For example, a client from Docker 27 talking to a server on Docker 20 may fail to set some bridge options silently.

### Daemon Configuration

Check the effective daemon settings, especially for bridge networking:

```bash
docker info --format '{{json .Plugins.Network}}'
```

Expected output (example):

```json
["bridge","host","ipvlan","macvlan","null","overlay"]
```

Look for `bridge` in the list. If it is missing, the daemon is not built with bridge support, which is extremely rare but would break all bridge networking. Next, inspect the default bridge configuration:

```bash
docker network inspect bridge
```

This returns a JSON array with details like `Subnet`, `Gateway`, and `Options`. For example:

```json
[
  {
    "Name": "bridge",
    "Id": "a1b2c3d4...",
    "Scope": "local",
    "Driver": "bridge",
    "EnableIPv6": false,
    "IPAM": {
      "Driver": "default",
      "Options": null,
      "Config": [
        {
          "Subnet": "172.17.0.0/16",
          "Gateway": "172.17.0.1"
        }
      ]
    },
    "Containers": {},
    "Options": {
      "com.docker.network.bridge.default_bridge": "true",
      "com.docker.network.bridge.enable_icc": "true",
      "com.docker.network.bridge.enable_ip_masquerade": "true",
      "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
      "com.docker.network.bridge.name": "docker0",
      "com.docker.network.driver.mtu": "1500"
    }
  }
]
```

Key fields to record:
- `Subnet`: The default bridge usually uses 172.17.0.0/16. If your organization uses that range internally, you should change the daemon default or avoid the default bridge.
- `Options.com.docker.network.bridge.enable_icc`: Inter-container communication is enabled by default on the default bridge, but this is not secure for production. Custom bridges give you finer control.
- `Options.com.docker.network.bridge.enable_ip_masquerade`: This allows containers to reach external networks via NAT. Keep it enabled unless you have specific routing needs.

### Host Kernel and iptables

Bridge networking relies on iptables for NAT and filtering. Verify kernel version and iptables version:

```bash
uname -r
iptables --version
```

Ensure the `br_netfilter` module is loaded. This module allows iptables to see bridged traffic, which is essential for inter-container communication policies and port publishing.

```bash
lsmod | grep br_netfilter
```

If missing, load it and make it persistent:

```bash
sudo modprobe br_netfilter
# Add the following lines to /etc/modules-load.d/br_netfilter.conf:
# br_netfilter
```

Also verify the sysctl settings:

```bash
sudo sysctl net.bridge.bridge-nf-call-iptables
```

Expected:

```
net.bridge.bridge-nf-call-iptables = 1
```

If it is 0, enable it:

```bash
sudo sysctl -w net.bridge.bridge-nf-call-iptables=1
# Make it permanent in /etc/sysctl.conf or /etc/sysctl.d/99-bridge.conf
```

### Topology Diagram

Create a simple diagram showing the Docker host, the docker0 bridge, custom bridges, and connected containers. Note IP subnets, exposed ports, and any host firewall rules. Store this diagram in version control or documentation. Include the following details:
- Host IP addresses and interfaces.
- Each bridge name, subnet, and gateway.
- Container names, static or dynamic IPs, and published ports.
- Any firewall rules affecting bridge traffic.

Use a tool like draw.io, Lucidchart, or even a text file with Mermaid syntax. Update the diagram whenever a network changes.

### Prerequisites

Confirm the following before any network changes:

- Docker daemon is running: `docker info` returns without error.
- User has permissions to create networks and run containers (typically in the `docker` group or with sudo).
- No conflicting subnets on the host network. For example, if the host LAN is 10.10.0.0/24, do not use that range for a bridge.
- Default bridge is not used for production containers (best practice). Run `docker network inspect bridge` and ensure the `Containers` section is empty. If any production containers are attached, migrate them to a custom bridge using the steps in the next section.

## Safe Configuration Path

Instead of modifying the default docker0 bridge, create a custom bridge network with explicit IPAM settings. This limits blast radius, allows better isolation, and eases management. Decide who owns network configuration: in a small team, this is often the DevOps engineer; in larger teams, a platform engineering group owns the network templates. Revisit the configuration quarterly or when the Docker environment is upgraded.

### Create a Custom Bridge Network

Choose a non-overlapping subnet from your private range. In this example, use 10.10.0.0/24 with gateway 10.10.0.1. The `--subnet` and `--gateway` flags are mandatory if you want deterministic IP assignment.

```bash
docker network create \
  --driver bridge \
  --subnet 10.10.0.0/24 \
  --gateway 10.10.0.1 \
  --opt com.docker.network.bridge.name=br-app \
  app-bridge
```

Expected output:

```
<network-id>
```

The `--opt com.docker.network.bridge.name=br-app` names the Linux bridge interface on the host, which makes it easier to identify in `ip addr` or `brctl show`. If you omit this, Docker uses a random interface name like `br-4e5f6a7b8c9d`. Verify creation:

```bash
docker network ls | grep app-bridge
```

Expected output:

```
<network-id>   app-bridge   bridge   local
```

Inspect the new network:

```bash
docker network inspect app-bridge
```

Confirm the subnet and gateway match your intended values.

### Attach Containers

When running containers, attach them to the custom network. For example, run a web server and a client:

```bash
docker run -d --name web --network app-bridge nginx:alpine
docker run -dit --name client --network app-bridge alpine sh
```

Check that both containers have IPs in the 10.10.0.0/24 range:

```bash
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' web
```

Expected output (example):

```
10.10.0.2
```

### Configure IP Assignment

By default, containers get dynamic IPs from the subnet. For stateful services that need a stable address, assign static IPs using `--ip`. This requires that the subnet is explicitly defined when creating the network and that the IP is outside any dynamic allocation pool if you have configured one.

```bash
docker run -d --name db --network app-bridge --ip 10.10.0.10 postgres:16
```

Be cautious to avoid IP conflicts. Before assigning a static IP, inspect the network to see which IPs are already in use:

```bash
docker network inspect app-bridge --format '{{range .Containers}}{{.IPv4Address}}{{"\n"}}{{end}}'
```

Expected output (example):

```
10.10.0.2/24
10.10.0.3/24
```

Then choose an unused IP. Also check that the network IPAM pool is large enough. For a /24 subnet, you have 253 usable addresses; if you expect more containers, use a larger subnet like /22.

### Enable Inter-Container Communication

By default, containers on the same custom bridge network can communicate with each other on all ports. If you need isolation, set `--opt com.docker.network.bridge.enable_icc=false` when creating the network. This tells Docker to add iptables rules that block traffic between containers on the same bridge unless they are explicitly linked or ports are published.

Example:

```bash
docker network create \
  --driver bridge \
  --subnet 10.10.10.0/24 \
  --opt com.docker.network.bridge.enable_icc=false \
  isolated-bridge
```

Then use `--link` (deprecated but still works) or more commonly use published ports and service discovery. With `icc=false`, each container can still communicate with the outside world but not with each other directly. To allow specific traffic, you can use `--publish` on one container and have others access it via the host IP and published port, or use a separate network with `icc=true` for services that need to talk.

### Adjust MTU if Needed

For overlay networks, VPNs, or jumbo frame environments, you might need a lower MTU to avoid fragmentation. Use `--opt com.docker.network.driver.mtu=1400`.

```bash
docker network create \
  --driver bridge \
  --subnet 10.10.20.0/24 \
  --opt com.docker.network.driver.mtu=1400 \
  low-mtu-bridge
```

After creating, verify by running `ip link show br-<network-id>` on the host and check the MTU value.

### Document Changes

Keep a log of network names, subnets, gateways, and attached services. Use a configuration file or Infrastructure as Code tool like Terraform or Ansible. For example, a simple YAML manifest for your networks:

```yaml
networks:
  - name: app-bridge
    subnet: 10.10.0.0/24
    gateway: 10.10.0.1
    options:
      bridge_name: br-app
      icc: true
      mtu: 1500
    owner: DevOps Team
    review_frequency: quarterly
```

Commit this file to version control and require a pull request for changes.

## Verification and Diagnostics

After configuring, run checks to confirm connectivity, name resolution, and network isolation. Perform these checks immediately after any network change and as part of your routine monitoring.

### Basic Connectivity

From the `client` container, ping the `web` container by name. Docker's embedded DNS allows name resolution on custom networks but not on the default bridge.

```bash
docker exec client ping -c 4 web
```

Expected output (trimmed):

```
PING web (10.10.0.2): 56 data bytes
64 bytes from 10.10.0.2: seq=0 ttl=64 time=0.120 ms
...
--- web ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
```

If ping fails, check:
- Container status: `docker ps -a` to see if both are running.
- Network assignment: `docker inspect -f '{{.NetworkSettings.Networks}}' client` should show `app-bridge`.
- IP address: `docker exec client ip addr` to confirm it has an IP in the subnet.

### DNS Resolution

Test reverse lookup inside the container:

```bash
docker exec client nslookup 10.10.0.2
```

Expected output includes the container name `web`:

```
Server:         127.0.0.11
Address:        127.0.0.11#53

2.0.10.10.in-addr.arpa  name = web.
```

If the lookup fails, ensure the container is on a custom network, not the default bridge, and that the embedded DNS server at 127.0.0.11 is reachable.

### Port Exposure

If you published a port, test from the host. For example, run web with `-p 8080:80`:

```bash
docker run -d --name web --network app-bridge -p 8080:80 nginx:alpine
curl http://localhost:8080
```

Expected output: the nginx default page HTML, starting with `<!DOCTYPE html>`.

Check port mapping with:

```bash
docker port web
```

Expected:

```
80/tcp -> 0.0.0.0:8080
```

If you cannot reach the service, verify the host firewall allows inbound traffic on 8080, and check iptables NAT rules with `sudo iptables -t nat -L -n | grep 8080`.

### Inspect Network Details

View the network with `docker network inspect app-bridge`. Check the `Containers` section for IP and MAC addresses. Example snippet:

```json
"Containers": {
  "<container-id>": {
    "Name": "web",
    "IPv4Address": "10.10.0.2/24",
    "MacAddress": "02:42:0a:0a:00:02"
  }
}
```

Verify that the IPs are within your subnet and that MAC addresses are unique.

### iptables Rules

Check that iptables has rules for the custom bridge. Run:

```bash
sudo iptables -L -n -v | grep br-app
```

You should see rules for forwarding and masquerading. For example:

```
Chain FORWARD (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 ACCEPT     all  --  br-app br-app  anywhere             anywhere
```

If you have `icc=false`, you will see DROP rules between containers. If no rules appear, check if the bridge interface name is correct and if the network was created with the expected options.

### Network Isolation Test

If you enabled `icc=false`, test that containers cannot ping each other (unless explicitly allowed). For example, from `client`, ping `db`:

```bash
docker exec client ping -c 2 db
```

Expected: no response or timeout.

If the ping succeeds, isolation is not working. Check the network options: `docker network inspect isolated-bridge | grep -o 'com.docker.network.bridge.enable_icc": "[^"]*'` should show `false`. Also verify iptables rules for the bridge.

## Failure Modes and Recovery

Bridge networks can fail due to IP conflicts, DNS issues, firewall misconfigurations, or daemon restarts. Here are common failure modes and how to recover. Appoint an on-call engineer to handle network incidents, with a primary and secondary contact. Document each incident and review monthly to identify recurring issues.

### IP Address Conflict

**Symptom**: Containers lose connectivity or cannot start with static IP. Docker may report an error like `failed to allocate IP address` if the IP is already in use outside Docker.

**Check**: `docker network inspect app-bridge` and look for duplicate IPs or `--ip` outside subnet. Also check the host's ARP table: `ip neigh | grep 10.10.0.10`. If an external device has the same IP, you have a conflict.

**Recovery**: Disconnect and reconnect the container with a correct IP, or remove and recreate the network after stopping all attached containers.

```bash
docker network disconnect app-bridge <container-id>
docker network connect --ip 10.10.0.12 app-bridge <container-id>
```

If the IP is taken by an external device, either change the external device's IP or choose a different static IP for the container.

### DNS Resolution Failure

**Symptom**: Containers cannot resolve names, but IP connectivity works.

**Check**: Ensure the network is not the default bridge (default bridge does not provide DNS). Verify `/etc/resolv.conf` inside the container:

```bash
docker exec web cat /etc/resolv.conf
```

Expected:

```
nameserver 127.0.0.11
options ndots:0
```

The `127.0.0.11` is Docker's embedded DNS server. If it is missing or different, the container may not be on a user-defined network.

**Recovery**: Attach the container to a custom bridge if it was on the default bridge; restart the container if DNS settings are stale. If the issue persists, check if the Docker daemon's DNS configuration is broken: `docker info | grep -A3 'DNS'`.

### Connectivity Lost After Daemon Restart

Docker restores user-defined networks on daemon restart. If not, ensure network configuration files exist in `/var/lib/docker/network/files`.

```bash
ls /var/lib/docker/network/files/
```

Each network should have a file named like `<network-id>.local`. If missing, the network may not be recoverable.

**Recovery**: Manually recreate the network with the same parameters, then reconnect containers. Use a script or IaC to ensure reproducible creation.

```bash
docker network create --driver bridge --subnet 10.10.0.0/24 --gateway 10.10.0.1 --opt com.docker.network.bridge.name=br-app app-bridge
# reconnect each container
docker network connect app-bridge web
docker network connect app-bridge client
```

Check that containers retain their IPs if they were static, or get new IPs if dynamic, and update DNS accordingly.

### Firewall Over-Blocking

If a host firewall blocks inter-container traffic, adjust iptables or firewalld. Example: allow all traffic on the `br-app` interface:

```bash
sudo iptables -I FORWARD -i br-app -j ACCEPT
sudo iptables -I FORWARD -o br-app -j ACCEPT
```

But be careful: this disables any isolation on that bridge. A more targeted rule allows only specific ports between containers.

For firewalld, add the bridge interface to the trusted zone:

```bash
sudo firewall-cmd --permanent --zone=trusted --change-interface=br-app
sudo firewall-cmd --reload
```

### Rollback Strategy

Before making changes, snapshot the network configuration using `docker network inspect` and save to a file. To rollback, stop containers, remove the custom network, and restore the previous state.

```bash
# Save current network configs
docker network inspect app-bridge > app-bridge-before.json

# On rollback, disconnect containers
for c in $(docker network inspect app-bridge -f '{{range .Containers}}{{.Name}} {{end}}'); do
  docker network disconnect app-bridge $c
done

# Remove the network
docker network rm app-bridge

# Recreate from snapshot (manually or via script)
# Use the parameters from the saved JSON to recreate the network
```

Automate snapshots before every change and store them in a versioned backup.

## Common Pitfalls and Mistakes

Even experienced operators make mistakes with Docker bridge networking. Here are common pitfalls, why they happen, and how to avoid or recover from them.

### Using the Default Bridge for Production

**Why it happens**: It is the easiest option; `docker run` without `--network` attaches to the default bridge.

**Why it is a problem**: The default bridge does not provide automatic DNS resolution between containers; you must use legacy `--link`. It also does not allow easy isolation or custom subnets. Changes to the default bridge require daemon restart and affect all containers.

**How to avoid**: Create a custom bridge for each application or environment. Enforce this through a policy or by using a wrapper script that always specifies `--network`.

**Recovery**: Stop the container, create a custom bridge, and re-run the container with `--network custom-bridge`. If the container needs to be reachable by others, use the container name as hostname.

### IP Address Exhaustion

**Why it happens**: Using a small subnet like /28 or /29, or assigning many static IPs without cleanup.

**Why it is a problem**: New containers cannot get IPs, causing failures.

**How to avoid**: Monitor IP usage. Use `docker network inspect` to count used IPs. For dynamic pools, set an appropriate subnet size based on expected container count. If you use static IPs, maintain an IPAM spreadsheet or database. Review monthly.

**Recovery**: Expand the subnet by recreating the network with a larger subnet (requires stopping all containers and reconnecting). Alternatively, free up unused IPs by removing dead containers.

### Forgetting to Set `enable_icc=false` for Multi-Tenant Networks

**Why it happens**: The default is true, and operators may not think about isolation.

**Why it is a problem**: Any container on the same bridge can access any other container on any port, bypassing application-level security.

**How to avoid**: Set `--opt com.docker.network.bridge.enable_icc=false` for networks that host untrusted or multi-tenant workloads. Then explicitly allow needed communication via published ports or more granular iptables rules.

**Recovery**: You cannot change `icc` on an existing network; you must recreate the network with the option and reconnect containers.

### Ignoring Host Firewall Interactions

**Why it happens**: Assuming Docker manages all firewall rules and the host firewall (ufw, firewalld, iptables) won't interfere.

**Why it is a problem**: Host firewall rules may block bridge traffic, especially if they have a default deny policy on the FORWARD chain. Docker inserts its own rules, but order matters and other tools can disrupt them.

**How to avoid**: Understand your host's firewall configuration. Ensure Docker's rules are not overridden. For ufw, set `DEFAULT_FORWARD_POLICY="ACCEPT"` in `/etc/default/ufw` if needed, and use `ufw allow` for published ports.

**Recovery**: Temporarily disable the host firewall to test, then add appropriate rules. For iptables, insert ACCEPT rules for the bridge interface, but be cautious not to open too broadly.

### Static IP Conflicts with Dynamic Range

**Why it happens**: When creating a network, Docker's default IPAM allocator reserves the entire subnet for dynamic allocation, so even static IPs may be within that range. If you later manually assign an IP outside the subnet, it fails. But more subtle: if you assign a static IP that falls within the dynamic pool, Docker's IPAM does not know about it and may assign the same IP to another container.

**Why it is a problem**: IP conflicts cause intermittent connectivity or container start failures.

**How to avoid**: Reserve a sub-range for static IPs by using `--ip-range` when creating the network. For example, use subnet 10.10.0.0/24 and ip-range 10.10.0.128/25, leaving 10.10.0.1-127 for static assignment.

```bash
docker network create \
  --driver bridge \
  --subnet 10.10.0.0/24 \
  --ip-range 10.10.0.128/25 \
  --gateway 10.10.0.1 \
  app-bridge-static
```

Then dynamic containers get IPs >= 10.10.0.128, and you can safely assign static IPs < 10.10.0.128.

**Recovery**: Recreate the network with a proper `--ip-range`, or manually track static IPs to avoid the dynamic pool.

## Operations Checklist

Use this checklist for routine operations and periodic reviews. It covers the key areas from this guide. Assign a single owner (e.g., the on-call DevOps engineer) to perform daily checks and a platform lead to perform monthly audits. Revisit the checklist itself quarterly.

### Daily/Weekly Checks

| Check | Command | Expected Result |
|-------|---------|-----------------|
| Containers running | `docker ps` | All desired containers up; no unexpected restarts |
| Network list | `docker network ls` | Expected custom bridges present; no orphan networks |
| Ping between key services | `docker exec <container> ping -c 1 <other>` | 0% packet loss |
| DNS resolution | `docker exec <container> nslookup <name>` | Container IP returned |
| Host port mapping | `curl localhost:<host-port>` | Service responds with expected content |
| IP address usage | `docker network inspect <network> -f '{{range .Containers}}{{.IPv4Address}}{{"\n"}}{{end}}'` | No IPs outside subnet; no duplicates |

Example of ping and DNS checks:

```bash
docker exec client ping -c 1 web
docker exec client nslookup web
```

Expected:

```
1 packets transmitted, 1 packets received, 0% packet loss
Server:    127.0.0.11
Address 1: 127.0.0.11

Name:      web
Address 1: 10.10.0.2
```

### Monthly/Quarterly Audits

- Review IPAM allocations for exhaustion: check `docker network inspect <network>` and count used IPs. Compare against the subnet size to ensure at least 20% free space.
- Verify no production containers are attached to the default bridge network: `docker network inspect bridge` should show no containers. If any are found, migrate them following the steps above.
- Update documentation and diagrams to reflect current state.
- Test failure recovery: simulate container crash (`docker kill <container>` and see if restart policy works), and test network recreation in a staging environment.
- Clean unused networks: `docker network prune` (careful: removes all unused networks). Do a dry run first with `docker network ls -f dangling=true`.

### Change Management

- Always test new network configurations on a staging host first. Create a mirror of the production bridge network and validate all connectivity and DNS checks.
- Use version-controlled configuration files for network creation. Apply changes via CI/CD pipeline where possible.
- Schedule maintenance windows for network changes. For critical networks, choose low-traffic periods and notify stakeholders at least 48 hours in advance.
- Have a rollback plan ready. Snapshot configurations before changes and rehearse the rollback procedure at least once per quarter.

## Conclusion

Docker bridge networking is a foundational technology that requires careful operational attention. By following this checklist, you can reduce downtime and configuration drift. Start with a thorough inventory of your environment, then create custom bridge networks with explicit subnets and options. Verify connectivity and DNS before deploying to production, and be prepared to recover from common failures using the steps outlined.

Your next steps:

- Run the inventory commands and document your current state in a shared wiki or repository.
- Create a new custom bridge network for a test application and validate connectivity and isolation.
- Integrate the daily checks into your monitoring system (e.g., cron jobs, Nagios, or Prometheus exporters).
- Share this checklist with your team and assign an owner for each recurring task.

Remember, a clear bridge networking operations process reduces rework by separating research, implementation, and review. The first useful pilot should be narrow and easy to inspect locally. Use this guide as a living document and adapt it to your organization's standards.