Running Multiple Docker Minecraft Containers on the same host

I needed to do this obviously to appease my son who needs various servers running. The spec was one creative and one survival server and I don't want to run in a VM since running itzg/minecraft-bedrock-server:latest image has been great for the single Minecraft bedrock server. This also means I can tinker with various Minecraft java images without stopping the existing container which is handy. Normally you can't do this, since the listening port 19132 can only be bound to a single container, and if you try to port map it in docker config, it won't connect from Windows clients.

I run all my docker containers from a single docker-compose.yaml, but you can manage it however you like. The essential bits are:

Set up a macvlan network for docker - this more or less allows you to assign static IPs to your containers. Bridge it with your existing ethernet connection in your docker-compose file, in my case the catchy-named enp1s0.

networks:
  macvlan:
    driver: macvlan
    driver_opts:
      parent: enp1s0
      macvlan_mode: bridge
    ipam:
      driver: default
      config:
        - subnet: 192.168.0.0/24

Set up your 2 containers with static IP addresses. You'll want to set something outside the dynamic IP allocation range (the range assigned via DHCP), which you should be able to find in your router or whatever dishes out IP addresses. This would also go in your docker-compose file under services:

  minecraft-bedrock:
    image: itzg/minecraft-bedrock-server:latest
    container_name: minecraft-bigsteve-creative
    labels:
      com.centurylinklabs.watchtower.enable: true
    environment:
      PUID: 1001
      PGID: 1001
      EULA: "TRUE"
      GAMEMODE: creative
      DIFFICULTY: easy
      SERVER_NAME: "BigSteve!"
      MAX_PLAYERS: 8
      MAX_THREADS: 4
      ALLOW_CHEATS: "false"
      ALLOW_LIST_USERS: "List of UserIds"
      ALLOW_LIST: "false"
      DEFAULT_PLAYER_PERMISSION_LEVEL: operator
      VERSION: LATEST
      TZ: Europe/London
      LEVEL_NAME: "BigSteveCreative"
    ports:
      - "19132:19132/udp"
    volumes:
      - /mnt/minecraft-bedrock/data:/data
    stdin_open: true
    tty: true
    restart: unless-stopped
    networks:
      macvlan:
        ipv4_address: 192.168.0.250

  minecraft-2024-survival:
    image: itzg/minecraft-bedrock-server:latest
    container_name: minecraft-2024-survival
    labels:
      com.centurylinklabs.watchtower.enable: true
    environment:
      PUID: 1001
      PGID: 1001
      EULA: "TRUE"
      GAMEMODE: survival
      DIFFICULTY: easy
      SERVER_NAME: "2024-Survival"
      MAX_PLAYERS: 8
      MAX_THREADS: 4
      ALLOW_CHEATS: "false"
      ALLOW_LIST_USERS: ALLOW_LIST_USERS: "List of UserIds"
      ALLOW_LIST: "false"
      DEFAULT_PLAYER_PERMISSION_LEVEL: operator
      VERSION: LATEST
      TZ: Europe/London
      LEVEL_NAME: "2024-Survival"
    ports:
      - "19132:19132/udp"
    volumes:
      - /mnt/minecraft-2024-survival/data:/data
    stdin_open: true
    tty: true
    restart: unless-stopped
    networks:
      macvlan:
        ipv4_address: 192.168.0.251
        

Setup a systemd service to ip link a mac0 interface to the ethernet, with type of macvlan, mode bridge. This means the interface should be available at boot with no further action required.

sudo vim /etc/systemd/system/macvlan-setup.service

(start of file)
[Unit]
Description=Configure macvlan interface
After=network.target

[Service]
Type=oneshot
ExecStart=/sbin/ip link add mac0 link enp1s0 type macvlan mode bridge
ExecStart=/sbin/ip addr add 192.168.0.0/24 dev mac0
ExecStart=/sbin/ip link set mac0 up
RemainAfterExit=yes
ExecStop=/sbin/ip link delete mac0
StandardOutput=journal

[Install]
WantedBy=multi-user.target

(end of file)

Then other systemd stuff

sudo chmod 644 /etc/systemd/system/macvlan-setup.service
sudo systemctl enable macvlan-setup

sudo systemctl status macvlan-setup

ip link show mac0
(output...)
149: mac0@enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether 32:15:e5:aa:fa:2e brd ff:ff:ff:ff:ff:ff

And finally, update your containers from docker-compose.

docker compose -f ~/docker/docker-compose.yml up

At this point you should be good, able to join from other Windows machines on the network to either container without issue.