/etc/hosts file refer to another configuration file

You can't. The format for /etc/hosts is quite simple, and doesn't support including extra files.

There are a couple approaches you could use instead:

  • Set up a (possibly local-only) DNS server. Some of these give a lot of flexibility, and you can definitely spread your host files over multiple files, or even machines. Others (such as dnsmasq) offer less (but still sufficient) flexibility, but are easy to set up. If you're trying to include the same list of hosts on a bunch of machines, then DNS is probably the right answer.

  • Set up some other name service (NIS, LDAP, etc.). Check the glibc NSS docs for what is supported. Personally, I think you should use DNS in most all cases.

  • Make yourself an /etc/hosts.d directory or similar, and write some scripts to concatenate them all together (most trivial: cat /etc/hosts.d/*.conf > /etc/hosts, though you'll probably want a little better to e.g., override the default sort by current locale), and run that script at boot, or from cron, or manually whenever you update the files.

Personally, at both home and work, to have machine names resolvable from every device, I run BIND 9. That does involve a few hours to learn, though.


It's not possible, as the format is usually deeply coded into platforms' libc. However it is imaginable that an OS adds this feature, making it a non cross-platform solution.

Alternatively, you can automatically update a certain block in your hosts file. This is particularly helpful if you have a script that dynamically outputs host entries for a certain project or so (possibly with changing IPs).

Here's an example: you want to create hosts from Terraform state via terraform-inventory.

Relevant inventory output (for instance, mapping an EC2 "Name" tag to groups of exactly one host each):

$ terraform-inventory --list | jq 'with_entries(select(.key | match("^name_")))'
{
  "name_myhost-a": [
    "10.101.118.131"
  ],
  "name_myhost-b": [
    "10.101.111.189"
  ]
}

print-updated-hosts-entries.sh

#!/bin/sh
exec terraform-inventory --list | \
    jq -r 'to_entries |
           map(select(.key | match("^name_"))) |
           map(.value[0] + " " + .key[5:]) |
           join("\n")'

Script output:

./print-updated-hosts-entries.sh
10.101.118.131 myhost-a
10.101.111.189 myhost-b

And the command line to update a marked block in /etc/hosts with the script output:

sudo cp /etc/hosts "/etc/hosts.bak.$(date +%Y%m%d%H%M%S)" && \
    (
        sed -n '1,/^# MYMARKER BEGIN/{/^# MYMARKER BEGIN/!p;}' /etc/hosts; \
        echo "# MYMARKER BEGIN"; \
        ./print-updated-hosts-entries.sh; \
        echo "# MYMARKER END"; \
        sed -n '/^# MYMARKER END/,${/^# MYMARKER END/!p;}' /etc/hosts; \
    ) | \
    sudo tee /etc/hosts.new | \
    sed -n '/^# MYMARKER BEGIN/,/^# MYMARKER END/p' && \
        sudo mv /etc/hosts.new /etc/hosts

Explanation:

  • The first line obviously creates a backup
  • The subshell in parentheses has two sed calls to print all lines before and after the marker begin/end, respectively. We insert the markers in any case, putting the script output in between those lines. Even if the script fails, we still have to surrounding content of /etc/hosts (and the backup in a catastrophic scenario).
  • sudo tee /etc/hosts.new writes the piped content into a new file
  • sed -n '/^# MYMARKER BEGIN/,/^# MYMARKER END/p' prints the updated block for convenience
  • sudo mv /etc/hosts.new /etc/hosts moves the new file into place. This must be done in a separate step because if the pipe buffer runs out of space, tee /etc/hosts would start writing the file while the existing content is still being read.