How does assigning an IP address to a LXC container is supposed to work?

Hello there,

I am pretty new to LXC and am wondering how assigning an IP addresses to a container’s veth interface works? So, here is my setup:

I run a Debian bookworm container on a Debian bookworm host. To avoid collisions in my network, I had to modify /etc/default/lxc-net:

USE_LXC_BRIDGE="true"
LXC_BRIDGE="lxcbr0"
LXC_ADDR="192.168.0.1"
LXC_NETMASK="255.255.255.0"
LXC_NETWORK="192.168.0.0/24"
LXC_DHCP_RANGE="192.168.0.2,192.168.0.254"
LXC_DHCP_MAX="253"
LXC_DHCP_CONFILE=""

And consequently added the following lines to my containers config file:

lxc.net.0.name = eth0
lxc.net.0.link = lxcbr0
lxc.net.0.flags = up
lxc.net.0.ipv4.address = 192.168.0.2/24
lxc.net.0.ipv4.gateway = 192.168.0.1

However, when I started the container, no IP address was assigned to the eth0 interface:

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0@if22: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 22:cf:97:4a:a6:b0 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::18ff:bff:fe99:88dc/64 scope link 
       valid_lft forever preferred_lft forever

I then configured nftables on the host to allow DHCP and subsequently the container got a random IP assigned, but not the IP I had configured (and intended):

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0@if21: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 22:cf:97:4a:a6:b0 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.0.80/24 brd 192.168.0.255 scope global dynamic eth0
       valid_lft 2582sec preferred_lft 2582sec
    inet6 fe80::a0cb:5cff:fe72:15e5/64 scope link 
       valid_lft forever preferred_lft forever

So my question is: How is it supposed to work that the container gets the IP assigned that was defined in the container’s config file?

(Of course I could set the static IP by editing the container’s /etc/network/interfaces, but I am interested in how it is supposed to work?!).

EDIT:
Uhhh, that’s interesting: When I disallow DHCP via nftables and run lxc-ls -f right after starting the container, it seems to get the correct - static - IP address:

NAME        STATE   AUTOSTART GROUPS IPV4        IPV6 UNPRIVILEGED 
mycontainer RUNNING 0         -      192.168.0.2 -    true 

But when I run lxc-ls -f a few seconds later, the IPv4 address has vanished:

NAME        STATE   AUTOSTART GROUPS IPV4 IPV6 UNPRIVILEGED 
mycontainer RUNNING 0         -      -    -    true

It seems something inside the container is somehow fighting the static IP address? :face_with_monocle:

Just found out that systemd-networkd was enabled by default. Disabling it either by running systemctl disable systemd-networkd inside the container or by deleting /etc/systemd/system/socket.target.wants/systemd-networkd.socket worked both fine: Now the container keeps the IP address assigned via its config file.

However, I still do still not understand how assigning the IP defined inside the config file to the containers network interface works in the first place. Would be nice if one could point me to some documentation about this magic :rainbow:

The way lxc.net.X.ipv4.Y works is by having LXC itself pre-configure the network interface when it creates it, before it even starts the init system of the container.

This works so long as nothing else in the container goes ahead and resets the interface on boot (which has been a problem on some distros in the past). Also because it just pre-configures kernel objects, it can’t handle things like configuring your DNS server or the like.

1 Like