How to let user set the hostname of the device dynamically/writable /etc/?

Hi folks. For a project I’m working on I’d like the user to be able to set the hostname of the device dynamically. This means I need to write to /etc/hosts and /etc/hostname. I was thinking that rather than mounting the firmware folders as writable (and this /etc) - I was wondering if it is possible to mount /etc as tmpfs (or overlay) and copy the contents into it as part of the system bootup?

Anyone know how to do that or have any other more sensible suggestions?

2 Likes

For what purpose? So other things can resolve over mDNS or for some internal purpose?

I know for the Balena Engine system Steffen solved the need for a particular file with a rootfs_overlay and a symlink to /data:

Maybe that serves the need?

2 Likes

This is a really good question since it gets at the boundaries of what’s in the Erlang runtime and what’s in Linux. The files are handled differently in Nerves.

/etc/hosts

Writing configuration to /etc is a common for Linux daemons, but Nerves uses functionality provided by Erlang when possible. For name resolution, Nerves uses Erlang’s inet application (inet_res specifically). The default configuration file is /etc/erl_inetrc. As you can see, it loads hosts from /etc/hosts so you’re in the same situation if you want to update it.

I hope that you really don’t need to update this file, but I’d use the symlink trick of adding a symlink in /etc to a writable file in /root or /tmp. This can be done in your project’s rootfs_overlay. You could also supply a custom erl_inetrc to move the hosts file. The flaw with the latter is that non-Erlang programs won’t know the new place, but that might not be an issue for you.

The name resolver reads /etc/hosts a lot, so you can update it dynamically.

Just as a side note since you brought it up, you can use overlayfs with Nerves to mount a writable filesystem on /etc. It’s not my favorite route, since I like the simplity and specificity of the symlink trick. However, search around the erlinit docs and tests for the --x-pivot-root-on-overlayfs option. OverlayFS is probably more interesting for persistent Erlang hot code updates with Nerves.

/etc/hostname

This file is read once on boot by erlinit, but only if your erlinit.config doesn’t specify a different way of getting the hostname. It’s really common to set a custom hostname by running a program to read an EEPROM, construct it from an Ethernet MAC address, or read some other place that has a unique serial number.

The erlinit docs talk about this generally. When you look through erlinit.config files in Nerves systems, you’ll see calls to the boardid program. boardid is super configurable so without knowing more about your situation, I’d just suggest to try using it first.

After you dynamically change the hostname, you’ll need to reboot. This is fixable in theory since it’s “just” a system call to change the hostname. The catch is that many programs cache the hostname, so you’ll need to restart them too, and in the end, rebooting might be easiest.

5 Likes

thanks for the incredibly detailed reply @fhunleth

1 Like

I am frustrated by this topic… I want my nerves project to set the hostname of the burned device to be based upon the build targets. The core issue that, yes, if I know my cpu serial number, my device will receive a hostname something like nerves-xxxx, but I don’t want to think of my project as “nerves” followed by some essentially random, value. I want my hostname to be myproject-rpi0, and myproject-rpi3, because I am going to have multiple instance on the same network.

All of the advice I find via, AI tools, keep offering advice which eventually boils down to copy the rootfs-overlay and then change it. But I don’t believe that will work, because different runtime targets will have different rootfs-overlays. (At least I assume they might) so there is no correct definitive one to copy.

Besides, that seems like utter overkill, to just set the hostname.

Here is the NERVES banner when I ssh in
████▄▄ ▐███
█▌ ▀▀██▄▄ ▐█
█▌ ▄▄ ▀▀ ▐█ N E R V E S
█▌ ▀▀██▄▄ ▐█
███▌ ▀▀████
breadboard 0.1.0 - nose-bar (921e63ce-a45f-58a0-74ec-42e986107784)

Serial : BB-RPI0
Uptime : 25.567 seconds
Clock : 2026-03-13 20:34:14 UTC (unsynchronized)

Firmware : Valid (A) Applications : 41 started
Memory usage : 43 MB (14%) Part usage : 0 MB (0%)
Load average : 0.19 0.05 0.02 Temperature : 41.2°C
Hostname : nerves-rpi0 Platform : rpi0 arm

wlan0 : 192.168.5.197/22, fd1f:b439:205c:4f5c:ba27:ebff:fe50:5b27/64, fd9c:eabc:d71f:1:ba27:ebff:fe50:5b27/64, fe80::ba27:ebff:fe50:5b27/64
usb0 : 172.31.43.141/30

Nerves CLI help: IEx with Nerves — nerves v1.13.1

Toolshed imported. Run h(Toolshed) for more info.

I want the hostname here, and the one that is announced of mdns, to allow ssh into the device, to be configurable. And configurable via a template that references config values, or env variables.

Can this be done?

If you just want to configure what mDNS broadcasts, you can update it with your project’s config files:


# config/target.exs

config :mdns_lite,
  # The `hosts` key specifies what hostnames mdns_lite advertises.  `:hostname`
  # advertises the device's hostname.local. For the official Nerves systems, this
  # is "nerves-<4 digit serial#>.local".  The `"nerves"` host causes mdns_lite
  # to advertise "nerves.local" for convenience. If more than one Nerves device
  # is on the network, it is recommended to delete "nerves" from the list
  # because otherwise any of the devices may respond to nerves.local leading to
  # unpredictable behavior.

  hosts: [:hostname, "your_host_name_here"],
  ttl: 120

For your example hostname posted above, you can use the following:

# config/target.exs

config :mdns_lite,
  hosts: [:hostname, "myproject-#{Mix.target()}"],
  ttl: 120

This will allow you to SSH into your devices, such as ssh myproject-rpi0 vs ssh myproject-rpi3, depending on the MIX_TARGET env var

Gus gave the easy answer and it should let you connect using myproject-rpi3.local or whatever.

To modify how the full on hostname is set we get to have some extra fun. It is all doable in rootfs_overlay.

You grab the etc/erlinit.config from your base Nerves system (example: nerves_systrem_rpi5/rootfs_overlay/etc/erlinit.config). That’s where the `nerves-` prefix is set. You can set your whole hostname there if you like.

Or you can look at rootfs_overlay/etc/boardid.config in the system. It will fall back to your rpi factory serial. But before that it will consult the uboot_env serial number keys.

So you can set those on the device with `Nerves.Runtime.KV.put(“nerves_serial_number”, “my-awesome-serial”)`. Or you can do a whole host of things that boardid lets you configure.

Changing the prefix is best done by copying erlinit.config to your project’s rootfs_overlay so you can change it more easily.

The serial is probably easiest to set in the uboot env via Nerves.Runtime.KV though you have many options if you don’t like that.

Hope that helps.

2 Likes

Sloppy reading, sorry, you did say that you weren’t certain about different erlinits and so on. I mostly restated what Frank had shared. Yeah, that could be an issue. I believe they are largely the same for rpi systems at the very least.

And boardid takes care of most of it if the nerves prefix doesn’t bother you.

If you want absolute certainty you run a custom system for each system you want and change the erlinit.config in the nerves system repo. But the builds are tedious if you don’t really need it.

I might be reading too hard, but if you only have one of each Raspberry Pi type, then forcing the hostname pattern is the way to go. Since the multicast DNS support derives the .local names from the hostname, it doesn’t need to be modified. In your project’s config/target.exsadd this:

config :nerves, :erlinit, hostname_pattern: “myproject-#{Mix.target()}”

Having said that, I still really like uses the built-in serials, since they’re unique (most of the time) and require no additional device provisioning. If you go this way, then here’s a line for your config/target.exs:

config :nerves, :erlinit, hostname_pattern: “myproject-%-.4s”

Some people like the full serials numbers and replace %-.4s with %s.

Neither of these are changeable at runtime in the way that the original poster was looking for, but I hope that this is ok for you.

If you don’t know serial numbers, you can still discover devices on the network by running mix nerves.discover.

The other answers you got are good too. I think we may all have read your post slightly differently, but I feel that one of these will responses will work.

Also, thanks for posting here so that the info can help others in the future.

3 Likes

Ah nice, the pattern config there was a missing piece for me :slight_smile: