Troubleshooting Unifi Controller Docker Adoption Issues: Port Mapping vs Host Networking


11 views

When running Unifi Controller in Docker with bridge networking (default), new devices appear in the controller interface but get stuck in perpetual "adopting" state. The only working solution seems to be switching to network_mode: "host", which bypasses Docker's network isolation but may not be ideal for all deployments.

Unifi devices use multiple protocols for discovery and adoption:

# Critical ports for successful adoption:
- 3478/UDP (STUN)
- 10001/UDP (device discovery)
- 8080/TCP (HTTP redirect)
- 8443/TCP (Controller UI/API) 
- 8880/TCP (HTTP portal)

The issue stems from how Unifi devices communicate back to the controller during adoption:

  1. Devices expect to reach the controller on the same IP they were discovered on
  2. With bridge networking, Docker NAT modifies the source IP
  3. The STUN service (3478/UDP) needs clean bidirectional communication

Option 1: Macvlan Networking

version: "3.7"
services:
  controller:
    image: jacobalberty/unifi:arm32v7
    networks:
      macvlan_network:
        ipv4_address: 192.168.1.200
    # ... rest of config

networks:
  macvlan_network:
    driver: macvlan
    driver_opts:
      parent: eth0
    ipam:
      config:
        - subnet: 192.168.1.0/24

Option 2: Host Networking (Simplest)

services:
  controller:
    network_mode: "host"
    # Remove all port mappings when using host mode
    # ... rest of config

1. Check controller logs:

docker logs unifi-controller | grep -i adopt

2. Verify STUN connectivity:

nc -zv -u controller_ip 3478

3. Test device inform URL:

curl -k https://controller_ip:8080/inform

For production deployments, consider these additional configurations:

environment:
  - UNIFI_DEVICE_IP=host_ip  # Explicitly set host IP
  - DISABLE_NAT_IP_RULES=false  # Enable NAT helpers
  - DISABLE_NAT_PORT_FORWARDING=false

Remember to persist these settings in your system.properties file located in the volume-mounted config directory.


When running Unifi Controller in Docker with bridge networking (default mode), you'll encounter adoption failures despite correct port mappings. The key symptoms are:

- Devices appear in "Discovered" list
- Adoption process starts but never completes
- Existing devices remain manageable
- Only works in host network mode

Unifi devices use multiple protocols for discovery and adoption:

1. STUN (UDP 3478)
2. Device discovery (UDP 10001)
3. Inform protocol (TCP 8080) 
4. Controller UI (TCP 8443)
5. L2 discovery (broadcast packets)

The critical issue is that Layer 2 broadcast packets (used for discovery) and some UDP communications don't properly traverse Docker's NAT in bridge mode.

Here's a verified docker-compose.yml that works with bridge mode by including all required ports and settings:

version: "3.8"
services:
  unifi:
    image: linuxserver/unifi-controller:7.3.83
    container_name: unifi
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Stockholm
      - UNIFI_UID=999
      - UNIFI_GID=999
    volumes:
      - ./unifi:/config
    ports:
      - "3478:3478/udp"  # STUN
      - "10001:10001/udp" # Discovery
      - "8080:8080"      # Device communication
      - "8443:8443"      # Controller GUI
      - "8843:8843"      # HTTPS portal
      - "8880:8880"      # HTTP portal
      - "6789:6789"      # Speed Test
      - "5514:5514/udp"  # Remote logging
      - "5656-5699:5656-5699/udp" # AP-Controller comms
    restart: unless-stopped

Even with proper port mappings, these factors can affect adoption:

1. Firewall rules on host (check with: sudo ufw status)
2. VLAN configurations if used
3. IGMP snooping on switches
4. Multicast DNS (mDNS) requirements

Use these commands to diagnose issues:

# Check container logs
docker logs unifi

# Verify port accessibility from AP
nc -zv YOUR_DOCKER_HOST_IP 8080
nc -zv -u YOUR_DOCKER_HOST_IP 10001

# Packet capture for L2 issues
tcpdump -i eth0 udp port 10001 or port 3478

If bridge mode still fails after all configurations:

1. Use macvlan network driver for true L2 access
2. Deploy controller on separate physical host
3. Temporary host mode for initial adoption
   (docker run --network=host ...)