dhcpcd setup

created onOctober 7, 2025

(Dynamic Host Configuration Protocol Client Demon) is the demon responsible for querying a DHCP server (i.e., ISC kea) and configure the machine for the network it is about to join from the lease it receives from the DHCP server. A DHCP lease contains configuration values, i.e.:

  • the IP address for a device.
  • the subnet mask.
  • the default gateway.
  • the IP addresses of DNS servers in the network.
  • a unique client identifier (i.e., a hostname).
  • the lease duration – the time duration during which the lease is valid. After the lease is expired, dhcpcd automatically request a new lease.
  • the address of a NTP server.
should not be confused with – the latter ist the ISC dhcpd, which reached its EOL in the end of 2022 and is now superseded by .

starting dhcpcd

start for a specific interface, request a lease for the interface, wait for an address to be assigned before forking to background:

dhcpcd --waitip <interface>

start for a specific interface, immediately fork to background, request no lease for the interface:

dhcpcd -b --inactive <interface>

start for a specific interface, immediately fork to background, request a lease for the interface with a timeout of 10 seconds, write log to file specified in arg :

dhcpcd -b -t 10 --waitip <interface> --logfile <logfile>

renew lease

Usually, this is not necessary. But it is useful to test the setup of and/or . To renew a lease for a specific interface, run:

dhcpcd -k eth0 && dhcpcd -n eth0

prevent dhcpcd to de-configure interface on exit

de-configures the interface when it exits unless the option or is used.

This is useful if SSH clients connect to the host, and they need to be notified of the host shutting down.

configure dhcpcd to act on events with run-hook scripts

can act on specific events. On notification of an event, the script runs run-hook scripts, usually located in . Most distributions provide run-hook scripts in , which can be copied to .

The names of the scripts in are preceded by a two-digit number, i.e. 10-wpa_supplicant, 20-resolv.conf, etc. The scripts are invoked in the ascending order of the preceding numbers.

events for run-hook scripts

The following tables list most events gets notified of:

PREINIT dhcpcd is starting up and any pre-initialisation required should be performed now.
CARRIER dhcpcd has detected the carrier is up.
NOCARRIER dhcpcd lost the carrier (cable may have been unplugged or association to the wireless point lost).
NOCARRIER_ROAMING dhcpcd lost the carrier but the interface configuration is persisted.
INFORM / INFORM6 dhcpcd informed a DHCP server about its address and obtained other configuration details.
BOUND / BOUND6 dhcpcd obtained a new lease from a DHCP server.
RENEW / RENEW6 dhcpcd renewed its lease.
REBIND / REBIND6 dhcpcd has rebound to a new DHCP server.
REBOOT / REBOOT6 dhcpcd successfully requested a lease from a DHCP server.
STATIC dhcpcd has been configured with a static configuration which has not been obtained from a DHCP server.
TIMEOUT dhcpcd failed to contact any DHCP servers but was able to use an old lease.
EXPIRE / EXPIRE6 dhcpcd’s lease or state expired and it failed to obtain a new one.
NAK dhcpcd received a NAK from the DHCP server. This should be treated as EXPIRE.
RECONFIGURE dhcpcd has been instructed to reconfigure an interface.
STOP / STOP6 dhcpcd stopped running on the interface.
STOPPED dhcpcd has stopped entirely.
DEPARTED The interface has been removed.
FAIL dhcpcd failed to operate on the interface. Normally happens when dhcpcd does not support the raw interface, which means it cannot work as a DHCP or ZeroConf client. Static configuration and DHCP INFORM is still allowed.
DUMP dhcpcd has been asked to dump the last lease for the interface.

environment variables in run-hook scripts

On notification of an event, dhcpcd will clear all environment variables aside from $PATH. The following variables will then be set, along with any protocol supplied ones:

$interface the name of the interface.
$protocol the protocol that triggered the event.
$reason as described in the table of reasons above.
$pid the pid of dhcpcd.
$ifcarrier the link status of $interface: unknown, up or down.
$ifmetric $interface preference, lower is better.
$ifwireless 1 if $interface is wireless, otherwise 0.
$ifflags $interface flags.
$ifmtu $interface MTU.
$ifssid the SSID the interface is connected to.
$interface_order A list of interfaces, in order of preference.
$if_up true if the interface is up, otherwise false. This is more than IFF_UP and may not be equal.
$if_down true if the interface is down, otherwise false. This is more than IFF_UP and may not be equal.
$af_waiting Address family waiting for, as defined in dhcpcd.conf(5).
$profile the name of the profile selected from dhcpcd.conf(5).
$new_delegated_dhcp6_prefix space-separated list of delegated prefixes.

alternating between two interfaces with dhcpcd

Most machines, especially laptops, come with an ethernet and a wlan interface. I want my laptop to have the same IP, regardless which one of the two interfaces is active. I want eth0 to have a higher priority than wlan0, as long as eth0 has a carrier (ethernet cable plugged in and connected to router). That is, as long as the ethernet cable is not plugged in, wlan0 should be setup. If a WLAN access point is not available, I can plug in the ethernet cable and the eth0 gets set up. I’m not using NetworkManager, Conman or WICD or whatever.

In order to be able to assign the same IP to two different interfaces, the DHCP server the machine connects to has to support the assignment of the same IP to two different interfaces, i.e. by assigning the same IP to two different interface hardware MACs. The now obsolete ISC dhcpd supports this. kea, which supersedes dhcpd, supports this from version 1.9.1 and later.

In days gone, this worked with , which is no longer supported. But with some twiddling with the hosts startup scripts and a dhcpcd run-hook script for eth0, this functionality can be achieved. The following works on void Linux. For other distributions the changes for the startup scripts will most likely differ, but you get the picture.

startup script

Here’s my startup script for the network:

ip link set eth0 up ip link set wlan0 up device= ip link show eth0 | grep -q "NO-CARRIER" eth0Avail=$? if [ ${eth0Avail} -eq 0 ]; then device=wlan0 else device=eth0 fi printf "setting up ${device}\n" dateTime=$(date +"%m%d-%k%M") if [ ${device} = "wlan0" ]; then printf "starting wpa_supplicant\n" wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant/wpa_supplicant.conf -f /var/log/wpa_supplicant/wpa_supplicant-${dateTime}.log printf "starting dhcpcd for ${device}\n" dhcpcd -b -t 10 --waitip wlan0 --logfile /var/log/dhcpcd/dhcpcd-${dateTime}.log dhcpcd -b --inactive eth0 --logfile /var/log/dhcpcd/dhcpcd-${dateTime}.log fi if [ ${device} = "eth0" ]; then printf "starting dhcpcd for ${device}\n" dhcpcd -b -t 10 --waitip eth0 --logfile /var/log/dhcpcd/dhcpcd-${dateTime}.log dhcpcd -b --inactive wlan0 --logfile /var/log/dhcpcd/dhcpcd-${dateTime}.log fi

Here is how it works:

  • both interfaces eth0 and wlan0 are set up with .
  • checks if eth0 has a carrier.
  • if eth0 has a carrier. it is taken as the active device – that is, the device to be configured. if eth0 has no carrier, wlan0 is the active device.
  • if wlan0 is the active device:
    • wlan0 is brought up to connect to an access point with .
    • one instance of dhcpcd is started for wlan0 to acquire the network config with a timeout of 10 seconds.
    • one instance of dhcpcd ist started for eth0 in inactive mode.
  • if eth0 ist active device:
    • one instance of dhcpcd is started for eth0 to acquire the network config with a timeout of 10 seconds
    • one instance of dhcpcd ist started for wlan0 in inactive mode.

That is, there are always two dhcpcd instances started, one for wlan0, one for eth0. For the active device, the dhcpcd instance requests the config values from the DHCP server. For the inactive device, the dhcpcd instance is started in inactive mode.

run-hook script

I put the following code block into after the function definitions in the script:

dhcpcdLog=/tmp/dhcpcd-run-hooks.log log() { echo $@ >> $dhcpcdLog } ifaceEthernet="eth0" ifaceWifi="wlan0" log $(date +"%m/%d %k:%M") "-----------------------------------------" log "interface: " ${interface} log "ifcarrier: " ${ifcarrier} log "reason: " ${reason} if [ "$interface" = "$ifaceEthernet" ]; then case ${ifcarrier} in "up") if [ ${reason} = "CARRIER" ] then log "got carrier for ${ifaceEthernet}, switching to ${ifaceEthernet}" dhcpcd --release wlan0 fi ;; "down") if [ ${reason} = "EXPIRE" ] then log "got no carrier for ${ifaceEthernet}, switching to $ifaceWifi" dhcpcd -t 10 --waitip wlan0 fi ;; *) log "unknown carrier state for eth0" ;; esac fi

The code works as follows:

  • check if is eth0.
  • if is eth0 and eth0 has a carrier, release the lease for wlan0. Note that no lease for eth0 is explicitly requested. The dhcpcd instance for eth0 does this automatically.
  • if is eth0 and eth0 has a no carrier, and the reason is , switch to interface wlan0, that is, acquire the config for wlan0 from the DHCP server.

The above code should also work if it is put into a new script file, ,i.e. , though I haven’t tested this.

references

dhcpcd man page

dhcpcd.conf man page

x