Home Docker MacVLAN for Traefik analytics in Grafana dashboards
Post
Cancel

Docker MacVLAN for Traefik analytics in Grafana dashboards

Setting Up a Docker MacVLAN Network for Traefik Visitor Access Log Web Analytics in Promtail/Loki/Grafana for Dashboard Visualization: A Comprehensive Guide

I am working to pass the source IPs coming in to the containers in docker. I do not want to use host, and as far as I understand this is not possible in bridge mode. It is possible with a MacVLAN.

This intro

  • This tutorial is written to first introduce you to a MacVLAN on a bridged docker network.

  • Then setup a docker MacVLAN network off the bridge, exclusive to traefik. Traefik will recieve un-forwarded, non-NAT packets, without any host interfearance or extra headers applied.

  • Then we want to have Promtail pickup the access logs, forward to Loki, and have Grafana setup a dashboard.

Required Software

You will need Debian 12.



Install Script

The only requirement is Debian 12.

The rest of the script covers all materials needed to have:

  • MacVLAN on the host interface

  • Docker MacVLAN for Traefik

  • Traefik’s access logs with original source headers

  • Analytic dashboard with Promtail/Loki/Grafana


System Requirements

Make sure this is a Debian 12 system you’re working with. I dont think I have mentioned this at all?

Debian 12 only

Oh look there, yes, Debian 12. You may find a path to do this with other methods, but out of the box LXC or VM - we’re going with Debian.


Script Features

  • Automated Traefik + MacVLAN Setup: Configures Traefik with a MacVLAN Docker network for simplified reverse proxy.

  • Docker Installation (Optional): Installs Docker if not already present on the system.

  • Automatic Network Information: Detects and stores host network details (IP, gateway, subnet).

  • Systemd-Networkd Integration: Generates and applies systemd network configuration files for MacVLAN support.

  • ifupdown networking disable: Disables ifupdown2 networking if systemd-networkd is enabled.

  • Context-Aware Configuration: Adapts its configuration steps based on whether it is running before or after a system reboot, ensuring proper setup in either scenario.

  • LXC virtualization check: Will alter the script depending on the virtual environment.

  • Dynamic IP Assignment: Automatically assigns an IP address to the Traefik MacVLAN for access.

  • Docker Network Management: Creates traefik_proxy_net for Docker container communication and traefik2host for Traefik’s personal MacVLAN.

  • Configuration File Management: Verifies, downloads, and sets up necessary configuration files for Traefik, Promtail, and Grafana.

  • Docker Compose Deployment: Deploys the entire Traefik stack using Docker Compose.

  • Informational Output: Provides post-configuration instructions, including DNS record setup and application access URLs, to guide the user.


MacVLAN WARNING!!


::Information about MacVLAN:: All ports are exposed by MacVLAN. This is fine when Traefik is only serving 80 and 443, but this setup includes port 8080. Additionally, you may want to secure your Traefik metric endpoints, like, /metrics or /stats with an ipWhitelist.


Setup with Install Script

To run a demo setup and see it in action, right now – run the script:

1
2
3
mkdir Traefik-MacVLAN-main && cd ./Traefik-MacVLAN-main && wget https://raw.githubusercontent.com/MarcusHoltz/Traefik-MacVLAN/refs/heads/main/traefik_macvlan_setup_script.sh && chmod +x ./traefik_macvlan_setup_script.sh && ./traefik_macvlan_setup_script.sh

Demo

Traefik Docker MacVLAN Demo of Script Running



Tutorial Index

  1. This Intro

  2. Traefik MacVLAN Demo Deployment Script

  3. Pre-Setup

    1. What is a MacVLAN

    2. Install Docker

      1. System Requirements

      2. Manual Docker Install

    3. Network Assumptions

    4. IP Addresses, Interface Names, and Subnet size

    5. Domain Name

  4. Test a Docker MacVLAN Network

    1. Setting up a quick test

    2. Create MacVLAN Network

    3. Test the New Network Connectivity

    4. IPTables Rules to Enable Host-to-Container Communication

    5. Why did this work?

    6. Setting Up Docker-Compose

    7. Docker Compose File

    8. Testing the Compose Setup

    9. Cleanup to move on

  5. Setting Up Host bridged Docker MacVLAN Network for Traefik

    1. Reviewing Setup

    2. Docker Internal Bridge Creation

    3. Docker Compose File

    4. Test the Setup

  6. Remove all work previous up to this section

    1. Remove any old configuration

    2. Review the state of the machine

  7. Traefik, Docker-Compose, and the Host network

    1. Review a few changes first

    2. Some changes are yours to make

    3. Docker-Compose

      1. Copy the config from github
    4. What did you just copy?

      1. Blog

      2. You didnt copy enough, you need more to copy

      3. Docker-Compose file

      4. ETC

      5. Changes to the example network

    5. Persistant network configuration

      1. /etc/ files in the correct location

      2. Review the MacVLAN device definition

      3. Review the MacVLAN parent interface

      4. BONUS - Rename parent interface

      5. Enable and Restart systemd-networkd

    6. Docker network creation

    7. What happened?

    8. Run docker-compose

    9. Links to generate website requests

    10. Traefik Analytics Collected

    11. Screen Shots!

  8. What happened?

    1. Script Breakdown

    2. Reviews



Pre-Setup

This is the setup before the setup. This section is here to make sure you’re able to get started.


What is a MacVLAN?

A MacVLAN provides a direct connection to the physical interface.

The MacVLAN attaches container network interfaces directly to the physical host’s network interface, bypassing the Linux kernel bridge entirely. This means traffic flows directly between the container and the physical network without being routed through a bridge. In traditional Docker bridge networks, traffic is routed through a virtual bridge (e.g., docker0), which involves additional kernel-level processing and often requires NAT for external communication.

But, in this case, a MacVLAN provides direct access to the physical network, allowing a container to have it’s own MAC addresses on the network. This method is best for scenarios where containers need to appear as physical devices on the same network as the host.

A single parent interface (e.g., eth0) can only be attached to one MacVLAN network at a time. This ensures that traffic from different MacVLAN networks is isolated at the physical interface level. By default, containers on a MacVLAN network cannot communicate with the host because they are isolated at Layer 2.

For more advanced setups, VLAN tagging (802.1Q) can be used to further isolate traffic by assigning different VLAN IDs to different MacVLAN networks


Tutorial Requirements

The only requirement stated at the begining of this tutorial is Debian 12. The rest of the tutorial covers all materials needed to have a host to docker MacVLAN network for traefik’s access log, generating analytics with Promtail/Loki/Grafana.


System Requirements

Make sure this is a Debian 12 system you’re working with. I dont think I have mentioned this at all?

Debian 12 only

Oh look there, yes, Debian 12. You may find a path to do this with other methods, but out of the box LXC or VM - we’re going with Debian.


Docker Install Process

This tutorial focuses on installing docker-ce, with a step-by-step install process, and finally creating backwards compatability with older docker-compose command.

Docker Install Steps

Let’s do this:

1
sudo apt update
1
sudo apt upgrade -y
1
sudo apt install apt-transport-https ca-certificates curl software-properties-common
1
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
1
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
1
sudo apt update
1
sudo apt install docker-ce
1
sudo systemctl status docker
1
sudo usermod -aG docker $USER
1
groups $USER | grep docker

Add a reference to old docker-compose commands

The Docker plugin for compose is now opted over the compose binary. This adds an alias so, when you type docker-compose is uses the plugin:

1
2
3
echo -e "\n\n# Docker plugin is now opted over compose binary, now, when you type docker-compose is uses the plugin\nalias docker-compose='docker compose'" >> ~/.bashrc

Great! If you got that done, made it this far, and nothing is on fire - consider your life and entire existance a success.



Network Assumptions

There are going to be some changes that need made to the configuration as it is presented in this tutorial. These defaults are subject to change.

This tutorial uses some defaults that you may need to change.


Subnet size

This area will describe how I have my setup, again, yours may differ.

  • Presumably, you’d replace every /19 with a /24 for the overall network available to communicate over.

  • Then you’d change the Docker subnet from a /24 to a /30 to limit the amount of IP addresses available over that network.

    • If you did change the Docker subnet, you must be sure to change the address of Traefik on the MacVLAN network.

IP Addresses

  • Currently the subnet I have set for my DMZ is:

172.21.192.0/19

You may need to change this to fit your own network. What size network do you have? Is it all one subnet, do you have logical subnet segreation?

  • The IP Range for the MacVLAN subnet to communicate over:

172.21.192.0/24

How big of a range of IPs do you think are going to be using this network? Please be sure it includes the gateway for the network.

  • Gateway address for the MacVLAN subnet:

172.21.192.254

If you change subnet size, be sure that it exists within the Gateway address

  • The IP address of the Docker Host:

172.21.192.5

This address is the IP address that DHCP on the host might aquire, we’re setting a reservation or a static address.

  • The IP address of the MacVLAN interface for the Docker Host:

172.21.192.100

This is the IP address that will accept connections for Traefik. You will set your DNS to this IP Address.

  • The IP address of the temporary bridge we create:

172.21.192.10

This is only for demonstration. It will not be used in the final version at the end.

Network Address Reference Table

IP Addresses Used

Gateway address for the MacVLAN subnetIP address of the temporary bridgeIP address of the MacVLAN interface for the Docker HostIP address of the Docker Host
172.21.192.254172.21.192.10172.21.192.100172.21.192.5

Subnets Used

Full Subnet on NetworkMacVLAN Bridged Subnet
172.21.192.0/19172.21.192.0/24
172.21.192.0172.21.192.0
172.21.223.255172.21.192.255

Getting the Correct Domain Name DNS Resolved

You will need a domain name for this to work. It doesnt have to be real, but it has to resolve.

This tutorial’s example is:

1
.noweb.myneatodomain.com

Your own domain!!?

Well, you will need to search and replace text - with the domain you may have set-up:

  • ‘noweb.myneatodomain.com’

Option a: Change your DNS on PiHole

If you already have PiHole set up as your primary DNS resolver (to block ads,etc) You can always just add a DNS entry to PiHole and this should resolve correctly.

Option b: Change your DNS on Unbound

Unbound DNS is a fantastic resolver. You can always add a wildcard host domain override. Just like PiHole, anyone using the Unbound DNS server should resolve correctly.

Option c: Change your DNS for a domain purchased with your Registrar

You can always have your public domain point to a private IP address. It will resolve fine, sometimes we must do this if you cannot control the DNS server.

Option d: Change your DNS on The Local Machine

If you just want to modify the DNS for you and not everyone on the network, you can change your hosts file:

  1. Add entries to your hosts file, open your hosts file:
1
2
3
4
sudo nano /etc/hosts


  1. Add the following lines
1
2
3
172.21.192.100 whoami1.noweb.myneatodomain.com whoami2.noweb.myneatodomain.com traefik.noweb.myneatodomain.com

  1. Restart the service:

sudo systemctl restart systemd-hostnamed



First Test - a Docker MacVLAN Network

Setting up a quick test of the Docker MacVLAN Network.


Step 1: Check Network Interface

First, identify the network interface you’ll use as the parent for the MacVLAN network:

1
2
3
ip addr show

Let’s assume your interface is ens18 with IP 172.21.192.5.


Step 2: Create MacVLAN Network

Here is the MacVLAN setup from my Debian docker host. It creates me a /24 network range I can use for docker containers on my local DMZ subnet, a /19 network:

1
2
3
docker network create -d macvlan --subnet=172.21.192.0/19 --ip-range=172.21.192.0/24 --gateway=172.21.192.254 -o parent=ens18 test_macvlan


Step 3: Test the New Network

Now, let’s quickly run a whoami container on this network:

1
2
3
docker run -d --name whoami --network test_macvlan --ip 172.21.192.100 traefik/whoami

Step 3a : Test Container Connectivity

See Test if the container is reachable from another machine on your network, anything other than the docker host:

1
2
3
curl http://172.21.192.100


Step 4: Enable Host-to-Container Communication

We have to make ip tables changes to make this work, DONT WORRY. These are temporary and will revert back with any reboot.

To allow the Docker host to communicate with the container:

1
2
3
4
5
6
sudo ip link add test_macvlan link ens18 type macvlan mode bridge
sudo ip addr add 172.21.192.10/32 dev test_macvlan
sudo ip link set test_macvlan up
sudo ip route add 172.21.192.100/30 dev test_macvlan


Step 5: Test the New Network Connection

Now you have to test from the docker host, where you just typed in all the commands above:

1
2
3
curl http://172.21.192.100


1
2
3
4
5
_________________
|  Bingo Bango  |
!!!!!!!!!!!!!!!!!
Mission  Complete


Why did this work?

A MacVLAN interface allows you to assign multiple MAC addresses to a single physical network interface, essentially creating isolated virtual network interfaces on top of your host’s physical interface. Each container or virtual machine can have its own MAC address and appear as a separate device on the network, even though they share the same physical network interface on the host.

In this case, the MacVLAN interface (e.g., test_macvlan) will act as the bridge between the containers/VMs and the host, and you’ll need to adjust your iptables rules to account for both the MacVLAN interface and the host interface (like ens18).



Second Test - Setting Up with Docker-Compose to Test a Docker MacVLAN Network

In this example, we’re setting up a docker-compose file for a more complex setup, we’re doubling the docker containers.


Step 1: Docker Compose File

Open a new file and name it: docker-compose.yml

  • Paste in the following:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
services:
  whoami1:
    image: traefik/whoami
    networks:
      test_macvlan:
        ipv4_address: 172.21.192.101

  whoami2:
    image: traefik/whoami
    networks:
      test_macvlan:
        ipv4_address: 172.21.192.102

networks:
  test_macvlan:
    external: true

Save your file and return to the command line.


Step 2: Run docker-compose

1
2
3
docker-compose up -d


Step 3: Time for Testing the Compose Setup

Test both containers, from the docker host or from another machine on the same network:

1
2
3
4
curl http://172.21.192.101
curl http://172.21.192.102

Great! They all work! We included all these IP Adresses when we made our route to test_MacVLAN.

Step 4: Cleanup Running Docker Containers

You may want to remove everything running, this was just to demonstrate starting everything up together:

  • Shut down and remove your current containers:
1
2
3
docker stop $(docker ps -a -q) && docker rm $(docker ps -a -q)



Final Test - Setting Up Host bridged Docker MacVLAN Network for Traefik

Building on the previous example, let’s integrate Traefik as a reverse proxy for our services using MacVLAN and additional required networks.

Assumptions about your setup

I’m going to assume you removed all the containers, deleted your docker-container.yml, kept the MacVLAN and ip tables rules we made earlier. Starting from scratch - but same network.


Step 1. Create a new internal docker network

With this, any containers placed here can access each other without the host:

1
2
3
docker network create traefik_proxy_net


Step 2: Create Docker Compose File

Create a docker-compose.yml file with the following content:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
services:
  traefik:
    image: traefik:v2.9
    container_name: traefik
    command:
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
    ports:
      - "80:80"
      - "8080:8080"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      test_macvlan:
        ipv4_address: 172.21.192.100
      traefik_proxy_net:

  whoami1:
    image: traefik/whoami
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami1.rule=Host(`whoami1.noweb.myneatodomain.com`)"
      - "traefik.http.routers.whoami1.entrypoints=web"
    networks:
      - traefik_proxy_net

  whoami2:
    image: traefik/whoami
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami2.rule=Host(`whoami2.noweb.myneatodomain.com`)"
      - "traefik.http.routers.whoami2.entrypoints=web"
    networks:
      - traefik_proxy_net

networks:
  test_macvlan:
    external: true
  traefik_proxy_net:
    external: true


Step 3: Run Docker Compose

Start the services:

1
2
3
docker-compose up -d


Step 4: Test the Setup

Remember, this will only work if you have DNS set up correctly.

Now test the services:

1
2
3
4
curl http://whoami1.noweb.myneatodomain.com
curl http://whoami2.noweb.myneatodomain.com

You should see responses from the whoami containers, routed through Traefik.


Step 5: For Funsies Access Traefik Dashboard

Open a web browser and navigate to http://172.21.192.100:8080 to access the Traefik dashboard. Or you could try: http://traefik.noweb.myneatodomain.com:8080

This setup uses MacVLAN to give Traefik a real IP on your network, allowing it to bind to port 80. The traefik_proxy_net network enables communication between Traefik and the other containers. Traefik acts as a reverse proxy, routing requests to the appropriate whoami service based on the hostname.



Remove all work previous to this section

That was a good example of a MacVLAN and Docker’s bridge. But for the rest of this tutorial we’ll be completing a manual version of what that script at the begining did.

If you just want to run the script, and not have to mess with anything else - be sure to first clear out any work previously done.


Remove hosts file

Be sure to remove any host file entries you may have made.

1
2
3
4
sudo nano /etc/hosts



Remove the test bridge and route

You only need the bridge on the host if you expect the host to be able to talk to the MacVLAN interface, as everything available from the hosts IP 172.21.192.5, will be available to the rest of the network under the bridged address as well, 172.21.192.10.

  1. Delete the IP route:

    sudo ip route del 172.21.192.100/30 dev test_macvlan

  2. Bring the test_macvlan interface down:

    Warning:

    Sometimes this will take down your entire network. Watch out if on ssh. Most of the time I like to throw a restart command in there, just incase. Or you can just reboot the machine

    • sudo ip link set test_macvlan down; sleep 2; sudo systemctl restart networking.service

    • sudo ip link set test_macvlan down; sudo reboot

  3. Delete the IP address:

    sudo ip addr del 172.21.192.10/32 dev test_macvlan

  4. Delete the test_macvlan interface:

    sudo ip link delete test_macvlan

  5. Remove the docker MacVLAN network:

    docker network rm test_macvlan

  6. Remove the docker internal bridge network:

    docker network rm traefik_proxy_net

  7. Verify removal of networking:

    ip route list

  8. Ensure all docker networks are back to original:

    docker network ls

If you are still having any trouble, head over to the Troubleshooting section.


New Docker

You should also start fresh with docker and everything on it.

1
2
3
printf "Stopping all running containers\n"; docker stop $(docker ps -a -q) && printf "Removing all docker containers\n"; docker rm $(docker ps -a -q) && printf "Removing all docker images\n"; docker rmi -f $(docker images -a -q) && printf "Removing all docker networks (except the default ones)"; docker network prune -f && printf "Removing all docker volumes"; docker volume prune -f && printf "Clearing the docker build cache\n"; docker builder prune -f && prinf "Comprehensive cleanup starting (removes everything including unused volumes)"; docker system prune --all --volumes --force && echo "######DOCKER######"; sleep .5; echo "##   W I P E D  ##";


Reboot

Might be a good time to reboot now.



Traefik, Docker-Compose, and a MacVLAN network

This next section is less test more production. Be warned there’s a lot more steps.

If you would like, there is an scripted version of this process available. Use the Github link to check the repo out.

It will handle all the networking and ip configuration for you.

I took the directions below, converted them - Now they’re in the traefik_macvlan_setup_script.sh.

If you would still like to follow along, this section of the tutorial is a step-by-step install of the MacVLAN docker network setup with Traefik.

If you are strong of heart and will of mind, you may proceed.


The IP Addresses are changing for this section of the tutorial. Please be aware of the differences so you can make the appropreate changes to your setup.

We’re strictly using MacVLAN, without a bridge. This mode is useful when you want strict isolation between virtual interfaces while still allowing external communication.


  • The IP Adress of the Docker Host is:

172.21.192.250

  • The IP Address of Traefik on Docker’s MacVLAN is:

172.21.192.251

  • The name of the MacVLAN on the Host is called:

traefik2docker

  • The name of the Docker Network using the MacVLAN is:

traefik2host


Modifications required

Unlike the script, that will do this setup for you, I didnt set up the following with environment variables.

You will have to search and replace.


Docker-Compose

Most of this tutorial is created from scratch to help you get a better understanding, but, the docker-compose aspect of traefik’s access logs is out of scope for this tutorial.

If you would like to know more, I can suggest checking out Oli Zimpasser’s Github and Medium article, linked below.

oglimmer does a fantastic job explaining this.

Copy my repository from Github

Now to begin this section of the tutorial – instead of making this from scratch, I am asking you to copy the reqired materials from MarcusHoltz Github.

Be sure you are in a folder you have write access to. This command will download and extract to: ./Traefik-MacVLAN-main

1
2
3
wget -O temp.zip https://github.com/MarcusHoltz/Traefik-MacVLAN/archive/refs/heads/main.zip && unzip temp.zip && rm temp.zip


What did you just copy?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
.
|-- README.md
|-- docker-compose.yml
|-- etc
|   `-- systemd
|       `-- network
|           |-- 20-ens18.network
|           `-- 25-traefik2docker.netdev
|-- grafana
|   `-- provisioning
|       |-- dashboards
|       |   |-- Webanalytics.json
|       |   `-- dashboard.yaml
|       `-- datasources
|           `-- ds.yaml
|-- promtail
|   `-- promtail-config.yml
|-- traefik
|   `-- traefik.yml
`-- traefik_macvlan_setup_script.sh

10 directories, 10 files

10 Directories and 10 Files

Let’s first go over the ./ base of the downloads directory.


Files in the base of the Github repo

This repo has a lot to get into, let’s first take a look at the files available to us once we enter the repository ( ./Traefik-MacVLAN-main).

1
2
3
4
5
6
.
|-- README.md
|-- docker-compose.yml
`-- traefik_macvlan_setup_script.sh

0 directories, 3 files

This section consists of three files:

  • README.md

This file is specific to the Github repo, and unused in this configuration.

  • traefik_macvlan_setup_script.sh

The script that will complete the manual process we’re doing right now. Run it, and quit reading.

  • docker-compose.yml

This file will complete our Docker stack. It will download the images needed, spin up the containers, and assign volume and network information to them. You will need to modify this to assign an IP Address to Traefik and assgin your DNS to the containers. RUNNING THIS IS THE LAST STEP.


Folders you dont need to touch in the Github repo

This section excludes the ./etc folder. It is for a special purpose, explained later.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.
|-- promtail
|   `-- promtail-config.yml
|-- traefik
|   `-- traefik.yml
|-- grafana
|   `-- provisioning
|       |-- dashboards
|       |   |-- Webanalytics.json
|       |   `-- dashboard.yaml
|       `-- datasources
|           `-- ds.yaml

6 directories, 5 files

This section of the repository consits of several directories, each containing configuration for each service, view but dont touch:

  • grafana

  • promtail

  • traefik

No changes need to be made inside of those folders.

You didnt copy enough, you need more to copy

Oh well, looks like I lied. You do need to make a change in one of those folders. You need to:

  • You must copy the file: GeoLite2-City.mmdb

  • From this location: maxmind.com

  • To this path ./promtail/

As explained below:


Geo location lookup

Promtail will throw an error in the config if you dont do exactly as described:

Go to https://dev.maxmind.com/geoip/geolite2-free-geolocation-data and download GeoLite2-City.mmdb. Place it into the promtail directory.

Dont complain to me when promtail wont start because you forgot to do this step.


Modifications needed in the ./etc folder of the Github repo

1
2
3
4
5
6
7
8
.
|-- etc
    └── systemd
        └── network
            ├── 20-ens18.network
            └── 25-traefik2docker.netdev

3 directories, 2 files

This folder is an example of your root folder structure. I have provided a few files, to generate the new MacVLAN interface.

  • You copied a lot of directories over, but the only one we’re going to pay attention to is the ./etc folder.

Change /etc/systemd/network/20-ens18.network

You will need to change the ens18 interface name to what your interface you’re using is.

  • In the filename: 20-ens18.network

  • And inside the file:

1
2
[Match]
Name=enp0s18

Place the /etc/ files in the correct location

Again, ./etc was a skeleton folder structure for you to find and understand.

Now the files have to go into the correct location. Be sure you are working in the downloaded repo’s directory: ./Traefik-MacVLAN-main

1
2
3
sudo cp -R ./etc/ /

  • If that was successful, you can now remove the /etc/ folder from your working directory.

  • or you can re-create the required systemd network files from scratch in the next section.


Persistant network configuration

Why all the systemd network files? Why can’t we just do it like the bridge demo earlier?

That’s great how we set up the bridge demo, but none of that will work again if you reboot.

We need to add changes to the correct area for a persistant network.


Dont do this.

This was ok when a 12 oz. glass bottle of Coca-Cola cost about 1.35 U.S. dollars in North America.

In theory, you can use ifup to create the MacVLAN:

This method will create the virtual bridge up after the parent interface comes up, then restart the docker service.

First open the file to edit:

1
2
3
sudo nano /etc/network/interfaces

Then make changes on the last line, under the iface:

1
2
3
4
5
6
7
8
9
10
# The loopback network interface
auto lo
iface lo inet loopback

# The primary network interface
allow-hotplug ens18
iface ens18 inet dhcp
up sleep .5; ip link add traefik2docker link ens18 type MacVLAN mode vepa && ip link set traefik2docker up && sleep 2; systemctl restart docker.service

The reason I have all those ugly sleep delays in there, this is after the interface comes up. If it was pre-up, the interface wouldnt ever come up if there was a problem with this bridge. So it avoid that, I build the bridge after the interface comes up, which means, after the docker service starts.

Order of how this works:

  • Kernel boots

  • Network stack comes up

  • ens18 starts to get an IP

  • a script that begins with “sleep .5” starts

  • after the script sleeps for 500ms, it begins to build a MacVLAN

  • the MacVLAN’s name will be traefik2docker

  • traefik2docker will be built onto ens18

  • the MacVLAN’s state is set to up

  • the docker service is restarted to see this new vepa device

VEPA allows traffic from and to the parent device, but not to other addresses on the same parent device.

Debian’s networking service (ifup) is now finished and fully loaded. This is the process that will happen everytime you reboot or the networking.service is restarted.

This method above is terrible for the following reasons:

  • Uses the legacy networking configuration in /etc/network/interfaces

  • Relies on arbitrary sleep delays which may not be reliable

  • Requires Docker service restart which can disrupt running containers

  • May not properly handle race conditions during boot sequence

  • VEPA conducts a hairpin turn for inter-container communication and can cause unneeded network traffic.


Persistant networks with systemd

Systemd-networkd provides a more robust way to create persistent network devices on Debian 12. Here’s how to implement it:


Step 1: Create the MacVLAN device definition

You should already have this file, and you should also have no reason to edit it. This is an example as to why this file is required.

A netdev file is a configuration file used by systemd-networkd to define and manage virtual network devices. Netdev files support various types of virtual network devices, including bond, bridge, dummy, MacVLAN, vlan, and many others.

1
2
3
sudo nano /etc/systemd/network/25-traefik2docker.netdev

Then make sure this is in the file:

1
2
3
4
5
6
7
8
[NetDev]
Name=traefik2docker
Kind=macvlan

[MACVLAN]
Mode=private

What do all of the lines in the netdev file do?

  • [NetDev] is the header that defines the start of the network device configuration section.

  • Name=traefik2docker this is the name of the virtual network device being created.

  • Kind=macvlan this virtual device is of type MacVLAN. This allows for creating multiple virtual network interfaces with different MAC addresses on a single physical interface.

  • MACAddress= Keep this line equal to nothing. That allows the system will generate a random MAC address for this interface.

  • [MACVLAN] is the header that starts the section for our MacVLAN configuration.

  • Mode=private We want only traefik to have access to the host’s larger network. So we’re making this private so any endpoints using this connection, the traffic between them (other docker containers) is dropped. This ensures complete isolation at Layer 2 for all MacVLAN instances on the same physical interface. Conversly,

    • (other option) Mode=bridge This would set the MacVLAN mode to “bridge”. In this mode, all of the traefik endpoints can directly communicate with each other and the external network.

    • (other option) Mode=vepa Traffic between MacVLAN instances is always sent out to the upstream switch or bridge, even if it’s destined for another MacVLAN on the same parent device, this allows port mirroring on the switch or SPAN on the interface, but also allows for inter-docker communication. However, leaving the interface and coming right back (hairpin) can potentially increase latency and network overhead for inter-container communication on the same host.

Modifications required to /etc/systemd/network/25-traefik2docker.netdev

You dont need to change anything in this file, just be sure it finds it’s home in: /etc/systemd/network/

  • With the filename: 25-traefik2docker.netdev

Step 2: Put that MacVLAN on a parent Interface

Create the systemd-networkd configuration file to define our network settings for the interface named in the file, in this example, ens18:

1
2
3
sudo nano /etc/systemd/network/20-ens18.network

Then make sure this is in the file:

1
2
3
4
5
6
7
8
9
[Match]
Name=enp0s18

[Network]
# Note: Each network interface should be managed by only one DHCP client or network manager, so it is advised to run only one DHCP client or network manager on the system.
# Added the MacVLAN as a secondary device
MACVLAN=traefik2docker

What is going on in a systemd networkd network file?

  • [Match] Section The [Match] section in a systemd network file determines whether the configuration in that file should be applied to a specific network interface. It serves as a filter to identify which network devices the configuration should affect.

  • Name=enp0s18 This line specifies that this configuration applies to the network interface’s altname, “enp0s18”, this is a more specific name and will allow us to rename the interface later if we need to.

  • MACVLAN=traefik2docker This line creates a MacVLAN interface named “traefik2docker” as a secondary device attached to ens18. A MacVLAN allows you to create multiple virtual network interfaces with distinct MAC addresses on a single physical interface.

Modifications required to /etc/systemd/network/20-ens18.network

This is the manual setup, I didnt set these up with an .env file. You will need to modify \etc\systemd\network\20-ens18.network

Again, you will need to change the ens18 interface name to the name of what the interface you’re using is.

  • In the filename: 20-ens18.network

  • And inside the file:

1
2
[Match]
Name=enp0s18

Step 3: Optional BONUS: Rename the parent interface

If you decide to rename your parent interface, you’re kicking /etc/network/interfaces to the curb and will also need to turn on DHCP for your re-named interface, and grab a new IP.

  • The interface’s new name below is set as: proxmox

  • Modify the name, and interface as needed

1
2
3
4
5
6
7
8
MACaddr=$(ip link show ens18 | grep -o -E ..:..:..:..:..:.. | head -n 1) && sudo bash -c "cat <<EOF > /etc/systemd/network/10-interface-rename.link
[Match]
MACAddress=$MACaddr

[Link]
Name=proxmox
EOF" && sed -i '/\[Network\]/a DHCP=yes' /etc/systemd/network/20-ens18.network

Step 4: Enable and restart systemd-networkd and the VM

1
2
3
sudo systemctl enable systemd-networkd

Great. You got it enabled, but if you restart systemd-networkd - to use it, it will flush everything and hang your connection, with no chance to get back. Also, if you get stuck in a hung session… hit ~ then .

So to avoid the reboot and/or hung SSH connection - instead, just


Great time to reboot right about now

1
2
3
sudo systemctl reboot


DO NOT TRY AND RESTART SYSTEMD-NETWORKD UNLESS YOU’RE CONNECTING IN FROM A DAEMON CONNECTED DIRECTLY TO THIS SERVICE.

Dont copy this, hopefully you rebooted.

1
2
3
# DONT DO THIS DONT DO THIS DONT DO THIS DONT DO THIS DONT DO THIS DONT DO THIS
sudo systemctl restart systemd-networkd && sudo systemctl reboot # STOP - DONT
# DONT DO THIS DONT DO THIS DONT DO THIS DONT DO THIS DONT DO THIS DONT DO THIS

Step 4: Marvel at your new MacVLAN sub-interface

Look, there it is. Look at it do nothing.

1
2
3
ip link show traefik2docker

If it works, it will show: traefik2docker@ens18


Docker network creation


Step 1: Create MacVLAN network

Create the Docker MacVLAN network using this new bridge interface:

1
2
3
docker network create -d macvlan --subnet=172.21.192.0/19 --ip-range=172.21.192.240/28 --gateway=172.21.192.254 -o parent=traefik2docker traefik2host


Step 2: Create Docker internal bridge network

Create docker internal bridge network:

1
2
3
docker network create traefik_proxy_net


Step 3: YOLO restart Docker

Restart docker to test:

1
2
3
sudo systemctl restart docker.service


Step 4 (dont do this): Host-Container communication

(Optional) I dont want this, but you may need Host-Container communication (required for Debian Host to talk directly with Docker MacVLAN), or your could aways change from private MacVLAN to a VEPA MacVLAN:

1
2
3
4
5
6
   # I would avoid this if you can at all, it's only here for some person who - we all are praying for you - may need this
   sudo ip addr add 172.21.192.252/32 dev traefik2docker   # avoid doing this
   sudo ip route add 172.21.192.240/28 dev traefik2docker    # avoid doing this
   # Dont use this if you dont have to. Avoid even reading this text and move on.


Step 5: Verify Docker network

To verify the Docker network you will need jq installed: sudo apt install jq

This should display, in pretty json – “parent”: traefik2docker”

1
2
3
docker network inspect traefik2host | jq '.[].Options'


Reboot

Might be a good time to reboot now and see what broke.


What happened? What were all those changes good for?

You can SSH into the Docker Host, and will be able to access any exposed ports by docker:

  • 172.21.192.250

You will be able to access traefik over the MacVLAN:

  • 172.21.192.251

Modifications to docker-compose.yml

The docker-compose file, docker-compose.yml is in the base of the repository.

You will need to edit the docker-compose.yml to match your own personal setup.

Docker-Compose file - editing

  1. Change your DNS domain name pointing to services
  • You will find through this document, noweb.myneatodomain.com, as the sample domain. If TLS certs are not involved, you can set the domain to anything you like as long as the DNS points to the Traefik IP address declaired in the docker-compose file.

So, if you want, search and replace that sample domain with your perfered domain replacing example.com in the command below:

1
2
3
sed -i 's/noweb\.myneatodomain\.com/example.com/g' docker-compose.yml

reminder: Make sure you have your DNS set to the correct IP address, reference Getting the Correct DNS Resolved if you dont have that set up.

  1. The IP address of traefik on the Host’s bridged interface.
  • look for traefik’s service and find the networks section. Change this IP to any that are within your host’s subnet and declaired in your docker network create -d MacVLAN command you will run later, in the Docker network creation section.

This is the IP Address you set wit the DNS domain name above, it is address to port forward port 80 to.

1
2
3
    networks:
      traefik2host:
        ipv4_address: 172.21.192.251

Run docker-compose in the base of the Github repo’s directory ./Traefik-MacVLAN-main

Light a candle, rub a coin, and say a prayer.

1
2
3
docker-compose up -d

If all goes well, you will need:

Here are several sites to generate website requests to your new Traefik webanalytics dashboard:

1
2
3
4
5
6
7
8
9
10
11
12
13
- http://traefik.noweb.myneatodomain.com

- http://whoami1.noweb.myneatodomain.com

- http://whoami2.noweb.myneatodomain.com

- http://catapp.noweb.myneatodomain.com

- http://error.noweb.myneatodomain.com

- http://portainer.noweb.myneatodomain.com

- http://anything.noweb.myneatdomain.com

Services not accessible by web:

  • promtail

  • loki

Services directly accessibly by IP Adress:

http://172.21.192.251:8080 access Traefik Admin UI


Traefik Analytics Collected

You can view Traefik logs in Grafana:

  • http://grafana.noweb.myneatodomain.com

Screen Shots !!!

Screen shots of this project are included below:

Traefik Access Logs in Grafana

Catapp the ultimate web access log generator.

Traefik Access Logs in Grafana

Whoami instance 1. This demonstrates to the client what is going on as the reverse proxy.

Traefik Access Logs in Grafana

Whoami instance 2. Demonstrating consistancy to the client on what is going on inside the reverse proxy.

Traefik Access Logs in Grafana

This is the first page you will see when you first load up grafana. The menu is accessible from the top left.

Traefik Access Logs in Grafana

Clicking there will bring up the menu, select ‘Dashboards’ and find the ‘Webanalytics’ dashboard.

Traefik Access Logs in Grafana

Loading up the Webanalytics dashboard for Grafana, you can see the logs coming in.

Traefik Access Logs in Grafana

You can drill down into the logs directly inside of Grafana and use them to create more Dashboards.

Grafana Dashboard zoomed out

Here is a more zoomed out version of the Dashboard displaying more of the elements available.


What? That was a lot. I just want this to work!!

Why didnt you choose to use the script? Are you not sure about some of the required software that it will install and system state assumptions?

Dont worry!

I took the steps above and converted them to a graphic, now they’re the script breakdown below:



Script Breakdown

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
+----------------------------------------------------+
|                  Script Execution Start            |
+----------------------------------------------------+
                            |
                            v
+---------------------+     |     +-------------------+
|  Display Banner     |-----+---->| Check Prerequisites|
|  - Initial warnings |           | - Sudo validation  |
+---------------------+           | - Docker checks    |
                                  | - .env loading     |
                                  +-------------------+
+-----------------------------+               |
| Check for Domain Name       |               |
| - Prompt if not in .env     |<--------------+
+-----------------------------+
                            |
                            v
+----------------------------------------------------+
| Gather Network Information                         |
| - IP, gateway, interface, subnet detection         |
+----------------------------------------------------+
                            |
                            v
+----------------------------------------------------+
| Check/Create/Modify Systemd Network Files          | 
| - Create macvlan netdev file                       |
| - Modify for existing interface rename             |
+----------------------------------------------------+
                            |
                            v
+------------------+        +------------------+
| Pre-Reboot Path  |        | Post-Reboot Path |
| - virt-check     |        | - Get new iface  |
| - Create network |        +------------------+
| - Rename prompt  |                  |
+------------------+                  |
          |                           |
          v                           v
+------------------------------+    +-----------------------------+
| Setup Traefik's MacVLAN IP   |    | Continue to Docker Setup    |
| - Auto MacVLAN IP assignment |    +-----------------------------+
| - No IP match = prompt for 1 |                  |
+------------------------------+                  |
                            |                     |
                            v                     v
+----------------------------------------------------+
| Update .env File                                   |
| - Save all collected variables                     |
+----------------------------------------------------+
                            |
                            v
+----------------------------------------------------+
| Handle Reboot Requirements                         |
| - Check systemd-networkd - not enabled, reboot     |
+----------------------------------------------------+
                            |
                            v
+----------------------------------------------------+
| Docker Network Setup                               |
| - Create traefik_proxy_net & traefik2host networks |
+----------------------------------------------------+
                            |
                            v
+----------------------------------------------------+
| Cleanup & Verification                             |
| - Remove sample files                              |
| - Download required configs                        |
| - Check GeoLite database                           |
+----------------------------------------------------+
                            |
                            v
+----------------------------------------------------+
| Run Docker Compose                                 |
| - Bring up Traefik stack                           |
+----------------------------------------------------+
                            |
                            v
+----------------------------------------------------+
|                     Script Complete                |
+----------------------------------------------------+

Reviews!

Dont listen to me, just read the reviews!

Marcus Holtz has created a guide so obsessively detailed it borders on technical madness. Does the world really need another 8,000-word exposition on Docker networking that dissects every conceivable configuration option? Apparently, it does. What’s infuriating is how Holtz manages to make the impenetrable MacVLAN concept not just understandable but practically deployable, stealing the hours you’ve planned to spend troubleshooting network issues. His step-by-step approach is foolproof, and the automated scripts to deploy an intricate Grafana dashboard works so efficiently they’ll render half your DevOps team redundant. Read at your own risk—your infrastructure might actually start working properly, and you’ll have no one to blame but Holtz.

This post is licensed under CC BY 4.0 by the author.