lo or localhost or 127.0.0.1 is loopback interface. Generally, this interface is used when system wants to communicate with “itself”. We know that every system has number of ports on which applications will listen (e.g MySQL will generally listen on port 3306, memcache will listen on 11211, etc).
Here is simple scenario…
We can imagine that system is large house with thousands of doors. Behind each door is one room which we call service or application (mysql, ssh, apache, nginx…). All doors have unique numbers from 1 to 65535 and room behind that door might be closed (no service runs behind them), or door might be open (application is running).
The story continues when somebody from outside (Internet or our local network) wants to communicate with any of these services, let’s say NGINX on port 80. Their host would send request packet with destination IP of our house (i.e 192.168.100.100) and destination port of our door number (i.e :80), right? So, somebody just knocked on door 80 and wants to communicate with service running behind that door. If service is active, door would be open (generally) and request would be served by NGINX (hopefully). This scenario happens when normal traffic wants to connect with our house and talk to some application/service/process.
In this story, local service like NGINX can communicate with other services on the same house by using loopback address. Let us say that NGINX received a request where user have sent his login information. Now, NGINX had received the request and needs to check if credentials sent are valid. In our example, php-fpm service is responsible for accepting PHP stuff from NGINX and processing it. Therefore, NGINX needs to send this data to php-fpm service somehow. You have guessed it, it can use loopback address and knock on php-fpm service door. Once connection is established and data is transferred, PHP checks the data and sees that it contains username and password. PHP needs to check if these credentials are valid and contact storage service (i.e MySQL). PHP can contact MYSQL by knocking on his door on localhost:3306. Mysql in room 3306 checks its data shelves and finds that username and password are valid, service contacts php-fpm again via loopback address, then php-fpm constructs valid static response page and forwards it to NGINX on localhost:80 which after forwards data via network to the user.
For example, on the system we can enter http://loopback:80 and our request would go to door 80 behind which usually runs nginx or apache service. But also, Nginx can communicate with other services on the same system like Redis, Apache, Memcached, SSH, or whatever by calling localhost:11211 and request from door 80 would go to door 11211.
Now, sending requests over loopback address will touch networking portion of kernel. However, UNIX-like systems use sockets too. Let’s stick with our example with house and thousands of unique doors. By using loopback address, request from one application will “leave” system by using TCP/IP stack (it will use ports same as somebody from outside would use). However, applications can communicate internally. Imagine that all rooms, apart from having doors facing outside of the house, also have doors that are connected with one giant hall within the house.
Therefore, each room has one public facing door and one private facing door (connected to giant hall). When using sockets, one application can communicate with other service on other service’s socket by ‘knocking’ on its internal/private door. This way, we do not use TCP/IP but kernel does its magic to send request over socket internally. This is also called Inter-process Communication and is much faster, and does not rely on open ports.