Vagrant Static External IP Addresses with the VMware Fusion Provider

Setting IP Addresses for your Vagrant VM is simple if you use a host-only NIC.
For example:

controller.vm.network :private_network, ip: "113.0.0.10"

(FYI, “controller” is the name of my VM)

If you want to do the same thing for a NIC with a NAT address for external access, the process involves a few more steps

As of the publication date of this blog post, it is not possible to specify the NAT IP address within the Vagrantfile.  Instead, we have to specify the MAC address of the VM’s NIC and modify the VMware Fusion DHCP Server to assign a specific IP address to it.

Here is a summary of the necessary tasks before I go into a bit more detail:

1. In your Vagrantfile, create the additional network definition with the :public_network keyword
2. In your Vagrantfile, modify vmx settings of the additional NIC to have a static MAC address and specify the MAC address value.
3. In your VMware Fusion application directory, modify dhcpd.conf for the VMware Fusion NAT network (usually, VMNET8) to specify the IP for the MAC you added to the Vagrantfile
4. Halt any Vagrant VMs running
5. Restart VMware Fusion
6. Vagrant up, and vagrant ssh to the VM(s) to verify the correct MACs and IPs are assigned.

For my fellow Vagrant Newbies, here are the step-by-step directions:

1. In your Vagrantfile, create the additional network definition with the :public_network keyword

controller.vm.network :public_network

2. In your Vagrantfile, modify vmx settings of the additional NIC to have a static MAC and specify the MAC.

controller.vm.provider :vmware_fusion do |v|
  v.vmx["vhv.enable"] = "TRUE" #Enable nested hypervisors to run x64 OS
  v.vmx["ethernet1.generatedAddress"] = nil #If the vmx file contains an auto-generated MAC address, remove it
  v.vmx["ethernet1.addressType"] = "static" #specify the MAC is static
  v.vmx["ethernet1.connectionType"] = "nat" #specify the NAT network
  v.vmx["ethernet1.present"] = "TRUE" #enable the NIC for the OS
  v.vmx["ethernet1.address"] = "00:0c:29:ca:d4:a0" #the MAC address
end

3. In your VMware Fusion application directory, modify dhcpd.conf for the VMware Fusion NAT network (usually, VMNET8) to specify the IP for the MAC you added to the Vagrantfile
On my system, the path is /Library/Preferences/VMware\ Fusion/vmnet8/dhcpd.conf
I added the following entry after the section that Fusion says not to modify

host controller {
    hardware ethernet 00:0c:29:ca:d4:a0;
    fixed-address 12.168.125.100;
}

4. Halt any Vagrant VMs running
check ps -ef | grep vmware-vmx to verify where they are
You will need to run vagrant halt from the directory that contains the appropriate Vagrantfile
Sample ps output: 0 31182 1 0 3:50PM ?? 0:19.98 /Applications/VMware Fusion.app/Contents/Library/vmware-vmx -s vmx.noUIBuildNumberCheck=TRUE -# product=1;name=vmrun;version=1.12.1;buildnumber=1040386;licensename=VMware Fusion for Mac OS;licenseversion=5.0; -@ duplex=3;msgs=ui /Users/youruser/Development/VagrantLab/.vagrant/machines/controller/vmware_fusion/vm.vmwarevm/precise64.vmx

Vagrantfile location: /Users/youruser/Development/VagrantLab/

5. Restart Fusion

6. Vagrant up, and vagrant ssh to the VM(s) to verify the correct MACs and IPs are assigned.
MAC and IP settings BEFORE modifying the Vagrantfile:

eth1      Link encap:Ethernet  HWaddr 00:0c:29:ca:d4:98
          inet addr:12.168.125.178  Bcast:12.168.125.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:feca:d4a0/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:9 errors:9 dropped:0 overruns:0 frame:0
          TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:1106 (1.1 KB)  TX bytes:1152 (1.1 KB)
          Interrupt:16 Base address:0x20a4

MAC and IP settings AFTER modifying the Vagrantfile:

eth1      Link encap:Ethernet  HWaddr 00:0c:29:ca:d4:a0
          inet addr:12.168.125.100  Bcast:12.168.125.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:feca:d4a0/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:9 errors:9 dropped:0 overruns:0 frame:0
          TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:1106 (1.1 KB)  TX bytes:1152 (1.1 KB)
          Interrupt:16 Base address:0x20a4

Here is my entire Vagrantfile:

# -*- mode: ruby -*-
# vi: set ft=ruby :
# vagrant up --provider=vmware_fusion

Vagrant.configure("2") do |config|
  config.vm.define :controller do |controller|
    controller.vm.box = "precise64"
    controller.vm.hostname = "controller"
    controller.vm.network :public_network
    controller.vm.provider :vmware_fusion do |v|
       v.vmx["vhv.enable"] = "TRUE"
       v.vmx["ethernet1.generatedAddress"] = nil
       v.vmx["ethernet1.addressType"] = "static"
       v.vmx["ethernet1.connectionType"] = "nat"
       v.vmx["ethernet1.present"] = "TRUE"
       v.vmx["ethernet1.address"] = "00:0c:29:ca:d4:a0"
    end
  end
end

My VM has two NICs:
ethernet0 is a NIC automatically created by Vagrant with a NAT address for communication between Vagrant and the VM.  It cannot be modified as far as I know.
ethernet1 is the NIC generated by the :public_network statement in the Vagrantfile

Thanks to Steve Fisher for the great write-up on modifying VMware Fusion DHCP.

If you have a different workflow for your IP address settings with Vagrant, share them in the comments section below

3 thoughts on “Vagrant Static External IP Addresses with the VMware Fusion Provider”

  1. Thanks for the detailed tutorial. I wanted to add a caution for those wishing in configure multiple machines within a single Vagrantfile. In my attempt to keep things as concise as possible, I put all the common v.vmx assignments into the global “config.vm.provider” block, and then within my config.vm.define for each machine, I put only the MAC address:

    #============================================================================
    # Second network adapter…
    config.vm.network :public_network

    # VMWare Fusion configuration
    config.vm.provider “vmware_fusion” do |v|
    v.vmx[“numvcpus”] = “1”
    v.vmx[“memsize”] = “1024”
    v.vmx[“vhv.enable”] = “TRUE”
    v.vmx[“ethernet1.generatedaddress”] = nil
    v.vmx[“ethernet1.addresstype”] = “static”
    v.vmx[“ethernet1.connectiontype”] = “nat”
    v.vmx[“ethernet1.present”] = “TRUE”
    end

    # Define multiple puppet nodes…
    config.vm.define “puppet1”, primary: true do |puppet1|
    puppet1.vm.hostname = “puppet1”
    puppet1.vm.provider “vmware_fusion” do |v|
    v.vmx[“ethernet1.address”] = “00:0C:29:9E:08:52”
    end
    end

    config.vm.define “puppet2” do |puppet2|
    puppet2.vm.hostname = “puppet2”
    end
    #============================================================================

    However, when done this way, none of the settings took effect. I was forced to put all the v.vmx statements inside the nested provider block, and completely omit the top-level provider block:

    #============================================================================
    # Second network adapter…
    config.vm.network :public_network

    # Define multiple puppet nodes…
    config.vm.define “puppet1”, primary: true do |puppet1|
    puppet1.vm.hostname = “puppet1”
    # VMWare Fusion configuration
    puppet1.vm.provider “vmware_fusion” do |v|
    v.vmx[“numvcpus”] = “1”
    v.vmx[“memsize”] = “1024”
    v.vmx[“vhv.enable”] = “TRUE”
    v.vmx[“ethernet1.generatedaddress”] = nil
    v.vmx[“ethernet1.addresstype”] = “static”
    v.vmx[“ethernet1.connectiontype”] = “nat”
    v.vmx[“ethernet1.present”] = “TRUE”
    v.vmx[“ethernet1.address”] = “00:0C:29:9E:08:52”
    end
    end
    #============================================================================

    It took me a while to figure this out. Hope to save others the trouble.

    – Jack

    1. Hello Jack,

      Thanks for the example and the feedback. One of the great things about Vagrant being written in Ruby is that you can take advantage of the language’s constructs like Lists and Ruby hashes within your Vagrantfile! This can help make your multi-VM Vagrantfile’s a little cleaner, and you don’t have to be concerned with duplicating code.

      Take a look at the example below from one of my presentations. We use a Ruby Hash to define the prefix of our VM names as well as the number of nodes of that type and the fourth octet of the nodes’ ip addresses. Next, we use a loop to go through the hash created at the top of the Vagrantfile to execute the Vagrantfile actions, including the vmx customizations, for each element of the hash:

      # -*- mode: ruby -*-
      # vi: set ft=ruby :
      nodes = {
      'nova' => [1, 200],
      'neutron' => [1, 210],
      }

      Vagrant.configure("2") do |config|
      config.vm.box = "hashicorp/precise64"
      nodes.each do |prefix, (count, ip_start)|
      count.times do |i|
      hostname = "%s-%02d" % [prefix, (i+1)]
      config.vm.define "#{hostname}" do |box|
      box.vm.hostname = "#{hostname}.vBrownBag"
      # Setting up private networking in case you want to pre-populate DNS. These IP's are NAT'ed so you can still reach the Internet, apt-get, etc.
      box.vm.network :private_network, ip: "178.16.172.#{ip_start+i}", :netmask => "255.255.255.0"
      box.vm.network :private_network, ip: "178.16.200.#{ip_start+i}", :netmask => "255.255.255.0"
      # If you want to use Shell provisioning, create shell scripts with the name of the VM (i.e. nova.sh, neutron.sh) in the same directory as your Vagrantfile and uncomment the following line
      #box.vm.provision :shell, :path => "#{prefix}.sh"
      box.vm.provision :puppet
      # Puppet manifests can be found and edited in the manifests subdirectory
      # If using Fusion
      box.vm.provider :vmware_fusion do |v|
      v.vmx["memsize"] = 1024
      end
      # Otherwise using VirtualBox
      box.vm.provider :virtualbox do |vbox|
      vbox.customize ["modifyvm", :id, "--memory", 1024]
      end
      end
      end
      end
      end

      Let me know if you have any further questions,
      Trevor

    2. Hello Jack,

      Thanks for the example and the feedback. One of the great things about Vagrant being written in Ruby is that you can take advantage of the language’s constructs like Lists and Ruby hashes within your Vagrantfile! This can help make your multi-VM Vagrantfiles a little cleaner, and you don’t have to be concerned with duplicating code.

      Take a look at the example below from one of my presentations (https://github.com/VMTrooper/VagrantBrownBag). We use a Ruby Hash to define the prefix of our VM names as well as the number of nodes of that type and the fourth octet of the nodes’ ip addresses. Next, we use a couple iterators (standard while/for loops would be just fine too) to go through the hash created at the top of the Vagrantfile to execute the Vagrantfile actions, including the vmx customizations, for each element of the hash:

      # -*- mode: ruby -*-
      # vi: set ft=ruby :
      nodes = {
      ‘nova’ => [1, 200],
      ‘neutron’ => [1, 210],
      }

      Vagrant.configure(“2”) do |config|
      config.vm.box = “hashicorp/precise64”

      nodes.each do |prefix, (count, ip_start)|
      count.times do |i|

      hostname = “%s-%02d” % [prefix, (i+1)]
      config.vm.define “#{hostname}” do |box|
      box.vm.hostname = “#{hostname}.vBrownBag”
      # Setting up private networking in case you want to pre-populate DNS. These IP’s are NAT’ed so you can still reach the Internet, apt-get, etc.
      box.vm.network :private_network, ip: “178.16.172.#{ip_start+i}”, :netmask => “255.255.255.0”
      box.vm.network :private_network, ip: “178.16.200.#{ip_start+i}”, :netmask => “255.255.255.0”
      # If you want to use Shell provisioning, create shell scripts with the name of the VM (i.e. nova.sh, neutron.sh) in the same directory as your Vagrantfile and uncomment the following line
      #box.vm.provision :shell, :path => “#{prefix}.sh”
      box.vm.provision :puppet
      # Puppet manifests can be found and edited in the manifests subdirectory
      # If using Fusion
      box.vm.provider :vmware_fusion do |v|
      v.vmx[“memsize”] = 1024
      end
      # Otherwise using VirtualBox
      box.vm.provider :virtualbox do |vbox|
      vbox.customize [“modifyvm”, :id, “–memory”, 1024]
      end
      end
      end
      end
      end

      Let me know if you have any further questions,
      Trevor

Leave a Reply

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