Back to Engineering Logs

Docker MACVLAN: Pulling My Containers Out of Isolation

5 min read
docker
networking
traefik
Docker MACVLAN: Pulling My Containers Out of Isolation

I’ve been using Docker for years, and like almost everyone else, I always stayed in my comfort zone: the bridge network. It’s the easy way out. You spin up a container, map a couple of ports (8080:80), and call it a day.

But this weekend, while orchestrating 36 services in my infrastructure, I realized the bridge was holding me back. It feels like having all your workers living in the same room and sharing a single telephone. It works, but it becomes an absolute management nightmare as you scale.

I decided to pull my containers out of that black box.

I migrated everything to MACVLAN. If you haven’t messed around with this, the concept is simple but powerful: instead of Docker hiding your containers behind a NAT, you assign them their own MAC address.

Suddenly, the container stops being a hidden “process” on the server and becomes a first-class citizen on my local network. It gets its own IP (e.g., 192.168.1.70). My router sees it, my firewall sees it, and most importantly, port conflicts are dead.

Why complicate your life with this?

First, performance. Stripping Docker of the chore of translating every network packet (NAT) frees up CPU and lowers latency. You won’t notice it on a notepad app, but you will when you’re pushing massive amounts of real traffic.

Second, transparency. If I hit a networking snag, I can see exactly which service is chewing up what, using its real IP, without everything looking like it’s bleeding out from the host’s IP.

Building the network infrastructure

Instead of defining the network inside a volatile configuration file, I chose to build a persistent network infrastructure via CLI. This way, the network exists on its own, and my containers simply “plug” into it:

docker network create -d macvlan \
  --subnet=192.168.1.0/24 \
  --gateway=192.168.1.1 \
  --ip-range=192.168.1.64/27 \
  -o parent=enp3s0 \
  lan_directa

What are we doing here? We’re telling Docker to use the macvlan driver bound to our physical interface (parent). We define our home or office subnet and, crucially, we lock down an ip-range. This stops Docker from trying to hand out an IP that your phone or laptop is already using, carving out a slice of the pie exclusively for the containers.

Wiring up the services

To keep this from turning into a chaotic mess of impossible-to-remember IPs, I threw Traefik into the mix. It watches the network and hands out automatic SSL certificates. Inside each service’s docker-compose.yml, the setup is now drastically cleaner:

services:
    my-app:
    image: my-app-image:latest
    container_name: my-app
    networks:
      # Option 1: Connect to the network with a fixed IP
      direct_lan:
        ipv4_address: 192.168.1.70 # The fixed IP it will have on your local network
    
      # Option 2: Connect to the network without a fixed IP (and let Docker assign one automatically)
      - direct_lan:            
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.my-app.rule=Host(`app.your-domain.com`)"
      - "traefik.http.routers.my-app.entrypoints=websecure"
      - "traefik.http.routers.my-app.tls.certresolver=cloudflare"

networks:
  direct_lan:
    external: true

As you can see, the docker-compose.yml config becomes razor-sharp. We only have to do two things: assign the exact ipv4_address we want for our service within the range we defined earlier, and declare the network at the bottom of the file as external: true. This tells Compose not to spin up a new network, but to “plug” the container into the infrastructure we already built via the terminal. Traefik will spot the new service at that IP and handle all the routing and certificates.

In the end, it’s about shifting gears—stop treating containers like disposable toys and start treating them like serious infrastructure. I don’t settle for things just “booting up.” I am obsessed with rock-solid foundations.

If you are still relying on the default bridge, maybe it’s time to ask yourself if your network is built to scale, or if you’re just slapping on bandaids.

From the Lab

This experiment was conducted by Ionastec. Need this level of technical rigor for your business?

Consult Ionastec