docker-net-dhcp

module
v2.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 16, 2026 License: GPL-3.0

README

docker-net-dhcp

Test Integration Dependencies Release OpenSSF Scorecard OpenSSF Best Practices Docs

A Docker network plugin that gives every container an address from the DHCP server your LAN already runs (your router, a Fritz!Box, dnsmasq), over bridge, macvlan or ipvlan, for IPv4 and IPv6. It runs in two shapes: as the network driver beside --ipam-driver null, which is all three modes, or, from v2.1.0, as the network driver and Docker's IPAM driver at once, which puts the leased address into Docker's own address management and makes docker run --ip and Compose ipv4_address work. The second shape covers bridge and macvlan for IPv4: ipvlan takes --ipam-driver null (#949) and so does IPv6 (#960). The DHCP exchange runs inside the plugin on the project's own engine, the dhcp-golib library: there is no external DHCP client to install and no client process per container.

This branch is the 2.x line and every page on it describes that build. The snippets below install the current release.

Requirements

  • Docker Engine 20.10 or newer. 20.10 is the lowest version this plugin is measured on. The engine matrix drives the whole baseline on every engine line from 20.10 to the current release: the plugin created from a local build and enabled, docker network create in bridge, macvlan and ipvlan mode, a lease confirmed in the DHCP server's own log, and an endpoint that keeps its address across docker restart. Pulling the published plugin from a registry is not part of that measurement. It runs weekly and on every change to the measurement. Below 20.10 the plugin refuses to start, and the refusal names the minimum and the engine it saw. 19.03 is unsupported because it is unmeasured: on a cgroup v2 host it cannot start a container at all, so nothing there tests this plugin. Every change is also tested against the engine the integration suite runs on, 29.8.0 today, read from that run's Fixture engine drift step.
  • Plugin interface docker.networkdriver/1.0, which is what the plugin manifest declares. The plugin negotiates the Docker API version with the daemon. It publishes both numbers on /Plugin.Health as engine_version and api_version. The minimum above is a version of the engine, not of the API, and nothing is refused on the API version.
  • One directory, created once per host, before docker plugin install (the line is in the quick start below). Docker will not create a missing bind source, so without it the install fails at start-up and leaves the plugin installed but disabled, after which the identical command answers only plugin ... already exists and names nothing. Recovery: the reference.
  • Architecture. linux/amd64 on the bare tag, linux/arm64 on the -arm64 tag. A Docker plugin cannot be installed from a multi-architecture manifest list, so the tag is how the architecture is chosen, in every snippet that names the image and not only the install line. Why, in full: Install, upgrade, uninstall.
  • Privileges. The manifest asks for host networking, the host PID namespace, the Docker socket, a bind mount of the state directory, a read-only bind mount of /var/run/docker, and CAP_NET_ADMIN, CAP_NET_RAW, CAP_SYS_ADMIN, CAP_SYS_PTRACE. docker plugin install prompts for the set; what each is for is in SECURITY.md.
  • Kernel link types. Bridge mode needs veth and bridge; macvlan and ipvlan each need the kernel module of the same name. A stock distribution kernel loads one the first time that link type is asked for, so there is normally nothing to do: measured on Linux 6.12, ipvlan was absent from lsmod before the first ip link add ... type ipvlan and present after, with no modprobe. A kernel built without the type, or a host where module loading is turned off, fails docker network create for that mode.
  • Root on the host. The plugin runs as root and its socket lives under /run/docker/plugins, which only root can read. Reading /Plugin.Health therefore needs sudo. Without it curl -s prints nothing and exits 7, which is what an absent socket also gives, so a permission problem looks like a stopped plugin.
  • Mode constraints. bridge expects a host bridge you maintain; macvlan and ipvlan attach to a host NIC and change nothing on the host, at the cost of the kernel rule that a child cannot reach its own host's address. Both in macvlan / ipvlan modes.

Quick start

# Once per host, before the install. See Requirements above.
sudo mkdir -p /var/lib/net-dhcp

# amd64
docker plugin install ghcr.io/claymore666/docker-net-dhcp:v2.1.1
# arm64
docker plugin install ghcr.io/claymore666/docker-net-dhcp:v2.1.1-arm64

One network, created once. macvlan needs only a host NIC; bridge wants a bridge you bring yourself (bridge mode):

docker network create -d ghcr.io/claymore666/docker-net-dhcp:v2.1.1 \
  --ipam-driver null -o mode=macvlan -o parent=eth0 lan-dhcp

docker run --rm -ti --network lan-dhcp alpine ip address show

--ipam-driver null stops Docker handing out addresses that would collide with the real LAN. From v2.1.0 there is a second supported shape: name the plugin again in place of null and the leased address goes into Docker's own address management, which makes --ip and Compose's ipv4_address work.

docker network create -d ghcr.io/claymore666/docker-net-dhcp:v2.1.1 \
  --ipam-driver ghcr.io/claymore666/docker-net-dhcp:v2.1.1 \
  -o mode=macvlan -o parent=eth0 lan-dhcp

One of the two is required. On arm64 the -arm64 tag goes in these lines too, because a network records the tagged reference as its driver. Add -o ipv6=true for a DHCPv6 lease beside the v4 one; it needs the null line, because the IPAM shape is IPv4 only in v2.1.0 and refuses the combination (#960). The two shapes are set out in the driver reference.

After that, plain Compose. No static addresses, no sidecar, nothing per container:

services:
  app:
    image: nginx
    networks: [lan-dhcp]

networks:
  lan-dhcp:
    external: true

Why this one

  • The address comes from the LAN's own server, so the router's lease table, its MAC reservations and, with -o register_dns=true, its DNS all see the container as one more host on the network. The alternative is a hand-assigned address in every Compose file.
  • The lease is held for as long as the container runs. Renewal, rebind, NAK and expiry run in the plugin, one client per endpoint, and the lifecycle is visible on the health endpoint. There is no external DHCP client to install, supervise or reap.
  • IPv6 is the same one line. -o ipv6=true adds a DHCPv6 lease with its own timers, its own counters and a DUID that survives a restart. On --ipam-driver null networks; the IPAM shape is IPv4 only in v2.1.0.
  • A restart keeps the address. In bridge and macvlan the MAC is carried across docker restart, so a server-side reservation still matches and the old address is re-requested; a plugin restart or upgrade re-adopts running containers, so their leases do not lapse (how).
  • The lease can go back when the container stops. -o release_lease=on_stop hands the address to the server at docker stop, per network and off by default (reference).
  • No host plumbing per container. macvlan and ipvlan attach to a NIC that is already there: no bridge to build, no route to add, nothing on the host to undo afterwards.

What is planned, and what this project has decided not to do, is on the roadmap.

Origin and licence

This began as a fork of devplayer0/docker-net-dhcp (quiet since 2021); since 2.0 it is its own product, with its own DHCP engine.

GPL-3.0. See LICENSE.md. The upstream project is GPL-3.0 and this derivative stays under the same licence.

Documentation

Published at https://claymore666.github.io/docker-net-dhcp/, one version per release; the same pages live in docs/.

Images go to GHCR (ghcr.io/claymore666/docker-net-dhcp:vX.Y.Z, primary) and are mirrored to Docker Hub under two names, claymore666/net-dhcp:vX.Y.Z and claymore666/docker-net-dhcp:vX.Y.Z. The two Hub names are the same image at the same digest; install from either.

Verifying releases

Every release from v1.1.0 is cosign-signed (keyless) on both registries and ships an SBOM; SLSA build provenance is attested for the GHCR image only, so verify provenance against the ghcr.io reference. Both commands need cosign v3 or newer. v2 cannot read the bundle format the release signs with, and it fails in a way that looks like a broken signature. Replace VERSION:

cosign verify ghcr.io/claymore666/docker-net-dhcp:VERSION \
  --certificate-identity-regexp '^https://github.com/claymore666/docker-net-dhcp/.github/workflows/release.yml@' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

gh attestation verify oci://ghcr.io/claymore666/docker-net-dhcp:VERSION --repo claymore666/docker-net-dhcp

The whole procedure, including rebuilding the binaries yourself, is in Verifying releases.

Project & community

  • Security policy / vulnerability reporting: SECURITY.md. Do not open a public issue for a vulnerability.
  • Bug reports & feature requests: the issue forms. Include the plugin version, your Docker version, the mode, and the plugin log. Docker has no plugin logs subcommand.
  • Questions: ask in Discussions; each issue form stamps a type label, so a question has no honest home on the tracker.
  • Governance & code of conduct: GOVERNANCE.md, CODE_OF_CONDUCT.md.

Contributing

Contributions are welcome. Open a pull request against the dev branch; Contributing is the whole of what is asked, and make check runs the fast CI lane locally in about a minute. In short:

  • Go code is gofmt-formatted and passes go vet and staticcheck; shell and workflow files pass shellcheck and actionlint.
  • New functionality is expected to ship with tests, and a per-package coverage ratchet enforces that at release time.
  • Commits and pull request descriptions carry no AI-assistant attribution and the commit author is a person. The attribution check reads every commit and the description.
  • Every required check must be green; branch protection holds the list.
  • Looking for somewhere to start? There are no starter tasks open at the moment. The ones that were seeded were closed as the work they described landed, and this section says so instead of sending you to an empty list. If you would like a first task, ask in Discussions and say what interests you: a subsystem, a bug you hit, a piece of the documentation you found thin. One will be scoped against it. When starter tasks exist again they carry the good first issue label and this section links to them.

This is solo-maintained, so please allow a few days for a response.

Directories

Path Synopsis
cmd
net-dhcp command
pkg
buildinfo
Package buildinfo carries what this binary was built from.
Package buildinfo carries what this binary was built from.
dhcp
Package dhcp is the chassis between the plugin and the DHCP library: it owns everything the library must not know — Docker identity, the sandbox namespace, the option vocabulary operators type at `docker network create` — and nothing about the protocol.
Package dhcp is the chassis between the plugin and the DHCP library: it owns everything the library must not know — Docker identity, the sandbox namespace, the option vocabulary operators type at `docker network create` — and nothing about the protocol.
test
arm64-netboot/nfs-watchdog command
nfs-watchdog pets the SoC hardware watchdog only while the root filesystem still answers, so an arm64 CI host whose NFS server disappears resets itself instead of wedging (#632).
nfs-watchdog pets the SoC hardware watchdog only while the root filesystem still answers, so an arm64 CI host whose NFS server disappears resets itself instead of wedging (#632).
integration/harness
No `//go:build integration` tag, for the reason raguard_parse.go gives: this is a pure function over bytes, so it is driven in the fast lane against VERBATIM strings captured from the image the suite actually runs containers in.
No `//go:build integration` tag, for the reason raguard_parse.go gives: this is a pure function over bytes, so it is driven in the fast lane against VERBATIM strings captured from the image the suite actually runs containers in.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL