docker-radvd runs radvd, the Linux IPv6 router advertisement daemon, in a container, so the hosts on your LAN configure their IPv6 addresses with SLAAC. You write the radvd.conf. The image does not generate one from settings.
docker-radvd gives the IPv6 hosts on your network their prefix, default route and DNS servers from a radvd.conf you control.
- Sends router advertisements with radvd built from a pinned release.
- Checks a changed
radvd.confbefore an in-place reload, so a malformed edit leaves the last good config running. - Runs radvd's worker as an unprivileged user once its socket is open.
- Reports config errors in
docker logs, with four Loki alert rules ready to load. - Pairs with keepalived so only the node holding a shared address advertises.
docker-radvd is built for admins who already write radvd configurations and want radvd in a container, on one node or on two nodes with keepalived for failover.
You need a Linux host with Docker on the LAN segment, on amd64 or arm64. The container uses host networking and the NET_RAW capability.
Three other options suit other setups:
- Consider your distribution's radvd package to run radvd on the host itself.
- Consider dnsmasq if it already serves DNS or DHCP on your LAN. Its router advertisement subsystem gives IPv6 hosts basic autoconfiguration.
- Consider CoreRAD if you want an advertisement daemon with Prometheus metrics and a sample Grafana dashboard.
docker-radvd is free software under the Apache-2.0 license. radvd is under a BSD-style license.
The image is on GitHub Container Registry and Docker Hub, for amd64 and arm64. This is the compose.yaml in this repository. Besides latest, each release is tagged with its full, minor and major version, such as v3.1.1, v3.1 and v3. These are the image's own version numbers, separate from the radvd version inside it.
services:
radvd:
image: ghcr.io/cplieger/docker-radvd:latest
container_name: radvd
restart: unless-stopped
# Advertisements go out on a real LAN interface, so radvd needs host networking.
# If this host is the default router, set net.ipv6.conf.all.forwarding=1 on the host first.
network_mode: host
cap_add:
- NET_RAW # required for the raw ICMPv6 socket radvd sends advertisements on
# Put your radvd.conf in ./radvd before the first start, or radvd exits.
volumes:
- "./radvd:/etc/radvd:ro"-
In the folder that holds
compose.yaml, create aradvdfolder. -
Save this as
radvd/radvd.conf.interface eth0 { AdvSendAdvert on; MinRtrAdvInterval 30; MaxRtrAdvInterval 100; prefix 2001:db8:1::/64 { AdvOnLink on; AdvAutonomous on; }; RDNSS 2001:db8:1::1 {}; DNSSL example.lan {}; }; -
In
radvd.conf, replaceeth0with the host's LAN interface,2001:db8:1::/64with your prefix and2001:db8:1::1with your DNS server. -
If another device is the default router, add
AdvDefaultLifetime 0;inside theinterfaceblock. -
If this host is the default router, turn on IPv6 forwarding on the host with
sudo sysctl -w net.ipv6.conf.all.forwarding=1. -
Run
docker compose up -d.
Run docker logs radvd. You should see starting radvd and radvd's own version ... started line, and the container stays up. If you see exiting, failed to read config file, radvd rejected radvd.conf, and its line before that names the problem. Then run sudo rdisc6 eth0 on another IPv6 host on the LAN. An advertisement from this host within a few seconds means it works.
On two nodes, both radvd instances advertise by default, and hosts pick whichever router they heard last. To have only one node advertise, let docker-keepalived move a floating link-local address to the MASTER node, and point AdvRASrcAddress in radvd.conf at it. The address must be link-local, because hosts silently discard an advertisement sent from a global address. Keep IgnoreIfMissing on, its default, so radvd stays running and silent on the BACKUP node. On failover, the new MASTER starts advertising within seconds. High availability has the full example and how to check it.
After you edit radvd.conf, restart the container:
docker restart radvdA restart starts radvd again with no check, so a broken edit stops radvd and the container restarts in a loop until you fix it. To have the edit checked first, send the reload signal from inside the container with docker exec radvd kill -HUP 1. A malformed edit is then refused, and the running daemon keeps its last good config. A refused edit logs SIGHUP reload refused.
docker kill -s HUP radvd also reloads with the check. After it, Docker stops applying the restart policy until the next docker start or docker restart, so prefer docker restart where it matters. Neither this image nor the shipped compose.yaml sets a stop_signal, so this happens whatever signal the kill sends. With a stop_signal set, always and on-failure stay armed for other signals, but unless-stopped is disarmed by the kill regardless. How it works covers what the check catches and misses.
radvd reads every setting from radvd.conf. The one environment variable sets how much radvd logs. Configuration has the detail of each table.
| Variable | Description | Default |
|---|---|---|
RADVD_DEBUG_LEVEL |
radvd's --debug level, 0 to 5. 0 still logs every warning and error. Any other value stops the container at startup |
0 |
| Mount | Description |
|---|---|
/etc/radvd |
Folder holding your radvd.conf. Mount it read-only |
| Capability | Why it is needed |
|---|---|
NET_RAW |
Required. Opens the raw ICMPv6 socket radvd sends advertisements on. Without it radvd exits at startup |
NET_ADMIN |
Not needed. Docker keeps /proc/sys read-only, so radvd's interface parameter writes fail either way |
| Setting | Value | Reason |
|---|---|---|
network_mode |
host (or macvlan) |
Advertisements go out on a real LAN interface, which a container network would isolate |
net.ipv6.conf.all.forwarding |
1 (or 2) on the host, when this node advertises a default route |
Compose cannot set it under host networking. A config with AdvDefaultLifetime 0 advertises no default route and needs no forwarding |
radvd opens its raw ICMPv6 socket as root, then runs its worker as the unprivileged radvd user, leaving a small root helper beside it. The config mount is read-only. The container needs host networking, so it reaches every interface on the host. It listens on no port. Security covers a read-only hardened profile, how to verify the image signature, what the image contains and how it is kept up to date.
The healthcheck runs pidof radvd every 30 seconds, so the container is healthy while radvd runs. When radvd exits, the entrypoint passes its exit status on and the container stops within a second, so your restart policy handles a crash. The probe does not see a radvd that runs but sends nothing, such as a BACKUP node by design, so check the wire with rdisc6.
open_icmpv6_socket: Operation not permitted. The container lacksNET_RAW. Add it tocap_add.exiting, permissions on conf_file invalid.radvd.confis writable by others or by theradvduser. Runsudo chown root:root radvd/radvd.confandsudo chmod 644 radvd/radvd.conf.IPv6 forwarding seems to be disabled, but continuing anyway. radvd logs this whenever IPv6 forwarding is off on the host. If this node advertises a default route, set the sysctl from step 5. WithAdvDefaultLifetime 0the line is expected and needs no fix.the TERM could not be delivered to radvd; a graceful stop cannot be confirmed. The container lacks theKILLcapability, which acap_droplist such asALLremoves. Add it back undercap_add.
Monitoring shows how to read what radvd sends.
radvd and the entrypoint log to docker logs, and radvd has no metrics endpoint. Four Loki alert rules ship in alerts/logql.yaml. Monitoring and alerts lists them and shows how to load them.
- Configuration covers debug levels, capabilities, networking and file permissions.
- High availability covers the keepalived pairing and how to check it.
- How docker-radvd works covers the entrypoint, reloads and the design.
- Monitoring and alerts lists the alert rules and how to check the wire.
- Security covers the privilege model, the hardened profile, signatures and what the image contains.
This project packages radvd into a container image, built from its source on GitHub. All credit for the daemon goes to its maintainers. The high-availability pattern follows Firstyear's post on HA radvd on Linux.
See CONTRIBUTING.md.
This project is built with care and follows security best practices, but it is intended for personal / self-hosted use. No guarantees of fitness for production environments. Use at your own risk.
This project was built with AI-assisted tooling using Claude, GPT, and Kiro. The human maintainer defines architecture, supervises implementation, and makes all final decisions.
Apache-2.0. See LICENSE.
The image carries the license text of every bundled component under /usr/share/licenses/. The image packages radvd, which carries a BSD-style permissive license.