Introduction

When connecting your LXC container to an external network, you have several methods to choose from. One option is the “routed” configuration, where the host actively routes traffic between its interfaces and the containers. Alternatively, you can use a “bridged” configuration, allowing the container to connect to a network bridge on the host, making it appear as a regular device on the same network.

In a previous article, I demonstrated how to set up an LXC container using the “routed” configuration. In this article, however, the “bridged” approach will be covered.

Step 0: Routed vs. Bridged

When connecting an LXC container with a static IP to your local network, you can choose between bridged and routed networking, depending on your use case. Bridged networking is ideal when you want your container to behave like a physical machine on your network, with full access to other devices and services. It uses a Linux bridge (e.g., lxcbr0 (like the default LXC managed bridge) or a custom br0 (like a unmanaged bridge from the host)) to connect the container to the same subnet as the host, allowing seamless communication with other local devices. This approach is best for scenarios requiring DHCP assignments, multicast traffic, or direct LAN access.

On the other hand, routed networking is suitable when you need stricter isolation while still allowing outbound traffic. Here, the host routes packets between the container and the external network using static routes or NAT, ensuring better security and control. This is useful when the container does not need direct access to all local network devices but should still communicate externally. If your container needs a dedicated IP on the LAN, use bridged mode; if you need tighter control over networking and security, routed mode is the better choice.

Step 1: Set up the unmanaged bridge on the host

Just so you know before we begin: The “unmanaged” only means that LXC is not in control of the bridge, as the bridge is created by the host itself. This allows us to specify that the bridge should be connected to specific interfaces on the host. This article shows how to do that with Ubuntu’s netplan.

First off, please install bridge-utils:

apt-get install bridge-utils

And continue to define your netplan. Be aware that you might have different files in your netplan directory already. So either work with the existing ones or create a consolidated new one, like we do here:

nano /etc/netplan/01-netcfg.yaml
network:
    version: 2
    renderer: networkd
    ethernets:
        nic0:
            set-name: nic0
            match:
                macaddress: c8:dd:40:23:31:60
            dhcp4: no
        nic1:
            set-name: nic1
            match:
                macaddress: c8:dd:40:28:4c:ca
            dhcp4: no
    bridges:
        br0:
            interfaces:
            - nic0
            dhcp4: no
            addresses:
            - 192.168.20.170/24
            nameservers:
                addresses:
                - 192.168.20.1
            routes:
            - to: default
              via: 192.168.20.1
        br1:
            interfaces:
            - nic1
            dhcp4: no
            addresses:
            - 192.168.21.170/24
            nameservers:
                addresses:
                - 192.168.21.1

In the ethernets section, I rename my two NICs to nic0 and nic1 based on their MAC address. This is not strictly necessary, but tidies up the naming for later usage.

The important bit is the bridges section: Here we create the two (unmanaged) bridges br0 and br1, define which NICs should bind to them and also setup the static IP for the hosts perspective. The routes block defines the gateway to which the traffic is directed to.

Save your file and set the netplan into action with

netplan apply

Step 2: Create a template routing profile

Before creating the LXC container, we must first set up a template for the networking profile. This template will serve as the “prototype” for all future profiles.

lxc profile create bridged

Proceed to edit this profile:

EDITOR=nano lxc profile edit bridged

And provide the template with following payload:

name: bridged
description: Route container traffic via unmanaged bridge
config:
  user.network-config: |
    version: 2
    ethernets:
      eth0:
        addresses:
        - <DUMMY-IP>/24
        nameservers:
          addresses:
          - <YOUR-DNS-IP>
        routes:
        - to: default
          via: <YOUR-GATEWAY-IP>
          on-link: true
devices:
  eth0:
    name: eth0
    nictype: bridged
    parent: br0
    type: nic

This structure serves two purposes: For one the devices block defines that the container should use the already present unmanaged bridge on the host while the config block sets up the static IP address the container should be using.

Please also make sure to adjust the interface and / or bridge names if you chose different names for your setup. Proceed by saving and exiting the configuration file.

Step 3: Create an adjusted routing profile from the template

Now that the template routing profile is set up, create a copy of it to serve as the actual configuration profile for your LXC container. Be sure to replace the placeholder with a value that fits your specific scenario.

lxc profile copy bridged bridged_<INTENDED-IP-OF-LXC-CONTAINER>
EDITOR=nano lxc profile edit bridged_<INTENDED-IP-OF-LXC-CONTAINER>
name: bridged_<INTENDED-IP-OF-LXC-CONTAINER>
description: Route container traffic via unmanaged bridge
config:
  user.network-config: |
    version: 2
    ethernets:
      eth0:
        addresses:
        - <INTENDED-IP-OF-LXC-CONTAINER>/24
        nameservers:
          addresses:
          - <YOUR-DNS-IP>
        routes:
        - to: default
          via: <YOUR-GATEWAY-IP>
          on-link: true
devices:
  eth0:
    name: eth0
    nictype: bridged
    parent: br0
    type: nic

Now, you simply need to modify this copy of the original “prototype” bridged file with the appropriate values for the upcoming LXC container.

Proceed by saving and exiting the configuration file.

Step 4: Create a LXC container with the routing profile

With the routing profile in place, you can now proceed to create the LXC container:

lxc launch <IMAGE-NAME> <INTENDED-NAME-OF-LXC-CONTAINER> --profile default --profile bridged_<INTENDED-IP-OF-LXC-CONTAINER>

Step 5: Finished!

That’s it! Your LXC container should now be routing its traffic through the unmanaged bridge to the host’s interface, connecting it to the external network.

Leave a Reply

Your email address will not be published. Required fields are marked *

You May Also Like