Learn through the super-clean Baeldung Pro experience:
>> Membership and Baeldung Pro.
No ads, dark-mode and 6 months free of IntelliJ Idea Ultimate to start with.
Last updated: April 20, 2025
Machines with a common local area network (LAN) can talk to each other using various network protocols, and as developers, we often use this ability. It helps us test websites on various devices and screen sizes without deploying them on the Internet, or share database connections over the network. If we’re feeling very geeky, we could also transfer large files between two machines without using a dedicated app or an Internet service.
For all these things to work, the machines need to be able to find each other. They usually do so using local IP addresses (192.168.X.X), assigned to them by the network. But, wouldn’t it be nice if they had names? They would be easier to remember. Moreover, if the names stayed constant even when the IP addresses changed, it would be more convenient for us.
In this tutorial, we’ll see how we can access a machine on a LAN using its hostname instead of its IP address.
For this tutorial, we’ll try to access the first machine, with hostname foo, from a second machine with hostname bar. We’ll try to do this over a wireless LAN (WiFi), but these steps would work the same for a wired network (Ethernet) as well. We’ll discuss different ways of doing this, with their advantages and limitations.
With each method, to check if the connection is working, we’ll use the ping command with the hostname and see if it resolves to the correct IP address. To make sure our ping command is working properly, we can first try it directly with the IP address of the foo machine:
$ ping 192.168.1.3
PING 192.168.1.3 (192.168.1.3) 56(84) bytes of data.
64 bytes from 192.168.1.3: icmp_seq=1 ttl=64 time=27.9 ms
64 bytes from 192.168.1.3: icmp_seq=2 ttl=64 time=57.6 ms
64 bytes from 192.168.1.3: icmp_seq=3 ttl=64 time=79.6 ms
64 bytes from 192.168.1.3: icmp_seq=4 ttl=64 time=103 ms
^C
--- 192.168.1.3 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3006ms
rtt min/avg/max/mdev = 27.881/67.056/103.136/27.771 ms
We can see that all 4 packets sent to the machine were received back. We’ll try to achieve the same output using a hostname (foo.local) with the ping command, instead of the IP address.
On any network, whether it’s a LAN or even the Internet, a hostname is simply a human-friendly reference to an IP address. When we use a hostname with a network service, it’s first resolved into the IP address that it stands for. The service then uses the IP address to establish the connection to that machine.
Over the Internet, this is done by a global domain name system (DNS). On Linux systems, we can also use the /etc/hosts file to map which name should resolve to which IP address. Entries in this file will have a higher precedence over the DNS service for resolving hostnames.
Let’s see what’s in this file on the bar machine, by default:
# Static table lookup for hostnames.
# See hosts(5) for details.
There are no entries here. Let’s add an entry for foo.local, pointing to 192.168.1.3:
# Static table lookup for hostnames.
# See hosts(5) for details.
192.168.1.3 foo.local
Notably, we’ve added the IP address first, followed by a tab, and then the hostname. Once we save the file, we can try to ping using the hostname:
$ ping foo.local
PING foo.local (192.168.1.3) 56(84) bytes of data.
64 bytes from foo.local (192.168.1.3): icmp_seq=1 ttl=64 time=11.1 ms
64 bytes from foo.local (192.168.1.3): icmp_seq=2 ttl=64 time=51.6 ms
64 bytes from foo.local (192.168.1.3): icmp_seq=3 ttl=64 time=72.5 ms
64 bytes from foo.local (192.168.1.3): icmp_seq=4 ttl=64 time=100 ms
^C
--- foo.local ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3005ms
rtt min/avg/max/mdev = 11.096/58.879/100.339/32.555 ms
We can see that the name foo.local is resolved to the IP address 192.168.1.3. We also successfully received a response from the machine.
Now, wasn’t this easy? However, this method has its shortcomings. Firstly, to make foo accessible through its hostname on all devices on the network, we have to change the /etc/hosts file on each device on the network. This is cumbersome.
Secondly, if we want to access multiple devices using their respective hostnames, we need to add and maintain multiple entries on multiple devices.
Finally, this can get really messy if the IP addresses of the machines change every now and then, which is the default case with most home networks.
Multicast DNS (mDNS) is a standard computer networking protocol that enables machines on a small network to discover each other using hostnames, with zero configuration. The machines need to have a software implementation of this protocol for this service to work.
On Linux devices, this is usually provided by the Avahi packages, and by the Bonjour package on Apple devices. Windows may or may not support this protocol based on how old the OS is.
Once we have the packages ready, and a hostname is used in a network program, the machines can send multicast queries to ask a machine on the network with that particular hostname to identify itself. The target machine then responds, sending a multicast message with its IP address.
Because of the packages used and the mechanism of the protocol, Multicast DNS is also known by various other names such as “Zeroconf”, “Rendezvous”, “Avahi”, or “Bonjour”.
The Avahi daemon is packaged for most Linux distributions. We need to install it to make our machine discoverable and discover other mDNS machines on the LAN. On Ubuntu, we can install it using apt:
$ sudo apt install avahi-daemon
Then, we need to start and enable the service:
$ sudo systemctl start avahi-daemon
$ sudo systemctl enable avahi-daemon
Once we’ve installed and started the Avahi (or another Zeroconf service) on both foo and bar machines, we can simply refer to these machines from each other using their hostnames with the .local domain.
Now, let’s try pinging the foo machine from the bar machine:
$ ping foo.local
PING foo.local (192.168.1.3): 56 data bytes
64 bytes from 192.168.1.3: icmp_seq=0 ttl=64 time=51.518 ms
64 bytes from 192.168.1.3: icmp_seq=1 ttl=64 time=6.175 ms
64 bytes from 192.168.1.3: icmp_seq=2 ttl=64 time=5.566 ms
64 bytes from 192.168.1.3: icmp_seq=3 ttl=64 time=4.893 ms
^C
--- foo.local ping statistics ---
4 packets transmitted, 4 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 4.893/17.038/51.518/19.912 ms
The name foo.local resolved to the machine with IP address 192.168.1.3 successfully, and we received a response! The best part about this method is that we didn’t need to edit any file or manually reference IP addresses anywhere.
With the Zeroconf method, we didn’t have to modify any file on any machine on the network. We only needed each machine to have the service running. This makes our life easy when we have multiple machines on the network with changing IP addresses.
Moreover, this protocol is standard and works with multiple operating systems. So we can access our Linux machines from MacBooks and vice versa.
In this article, we explored two methods to connect to a machine using its hostname over a LAN. First, we looked at modifying the /etc/hosts file on the machine we’re connecting from. This was straightforward to execute, and we didn’t need to modify anything on the machine that we needed to connect to.
This method is great for temporarily accessing one machine. However, it could get cumbersome and messy when we have multiple devices on the LAN with IP addresses that are changing frequently.
Later, we looked at the Multicast DNS method, which requires us to install an mDNS service on each machine. But once that’s done, we can access all the machines using their hostnames with zero configuration.