Preconfiguring LEDE or OpenWrt with UCI

Updated: 2017-12-27

OpenWrt/LEDE rely on the UCI framework for configuring the devices they run on. All common services (DNS, DHCP, firewall, dynamic DNS, ...) are configured through UCI, with the human-readable results being saved into configuration files in /etc/config. However, that's just for the user's convenience; UCI itself is a command line tool. To list the settings, enter the following into the terminal:

# uci show network

That shows all UCI network settings; you can filter out pretty much anything. UCI is the tool to turn to when you deploy multiple devices; it allows you to preset your desired configuration and micro-manage almost anything. For example, OpenWrt/LEDE disable the wireless network by default, but you could set your build to enable it and pre-set the SSID, encryption type and key.

UCI syntax

UCI makes a few important distinctions between settings. I'll run through them quickly. First a few general commands. Pending changes can be shown with:

# uci changes

You can revert those by referring to the hash shown above. Changes are only saved once you issue

# uci commit
Adding a section

Sections show up in the configuration files in /etc/config as config $value. E.g., entering the following will add a config host section in /etc/config/dhcp:

# uci add dhcp host

Unless you are adding to an existing section, you first need to define a new section before you can add an actual setting. If the section is not unique (ie, if you can create more than one of it with the same name), UCI will number the sections; this is not visible in the configuration files themselves but does show when you have the settings printed with the uci show command. That allows you to determine which section you need to add to. To add a named section, you don't use uci add but uci set:

# uci set system.led_sata='led'

That will show up in /etc/config/system like this:

config led 'led_sata'

Whereas a regular section (like e.g. the DHCP one above) would show like this:

config host
Adding a unique setting

In this case we'll add settings to the previously created host section. We'll add more details so we can complete the section, which is meant for defining static DHCP leases. For ease of use we'll assume here this is your first host section. Numbering starts at 0.

# uci set dchp.@host[0].name='bellerophon'
# uci set network.@host[0].ip=''
# uci set network.@host[0].mac='a1:b2:c3:d4:e5:f6'

After you commit, you'll see this appear as a statically defined lease. You'll also see it in the LuCI web interface under the network settings.

Adding a setting with multiple possible values

Some settings can hold multiple values. For these, one uses uci add_list instead of uci set. For example, to add another listening address for uhttpd, enter:

# uci add_list uhttpd.main.listen_http=''
# uci commit uhttpd
# uci show uhttpd.main.listen_http
uhttpd.main.listen_http='' '[::]:80' ''

Scripting your custom settings and including them into your image

Now that it's clear how to add these settings, it's time to script them and integrate them into your builds, so any routers you set up will be preconfigured to your liking. There's a myriad of possibilities: changing your LAN configuration, pre-seeding static DHCP settings, preconfiguring SSH, dynamic DNS, predefining a guest network, firewall rules, ... Anything you want, really.

A word of warning: apparently stuff in /etc/uci-defaults will be run at every 'first boot' (read: after a succesful flash), so also when you perform a sysupgrade while keeping settings. If you want the commands to run only once, you need to implement some checks (e.g. verify if a certain value has already been set). A simple example would be checking the timezone setting, which, to my knowledge, is unset on a default OpenWrt/LEDE installation:

[ $(uci -q get system.@system[0].zonename) = "America/New York" ] && exit 0

That implies of course you need to set the timezone after the initial flash (be it through a batch script or manually).

Batch scripts go into env/files/etc/uci-defaults/ in your buildroot. They don't need to be executable. You'll probably want your batch scripts to be run after what's provided by default, so prefix them with 99_. A simple batch script to add a static DHCP lease would look like this:

$ cat 99_customisations
uci -q batch <<-EOT
  add dhcp host
  set dhcp.@host[0].name='bellerophon'
  set network.@host[0].ip=''
  set network.@host[0].mac='a1:b2:c3:d4:e5:f6'
  commit dhcp

For more info on including custom files in your OpenWrt/LEDE build, see this link.