Setup for a SaltStack Vagrant Environment

Often when writing Salt related code, I need an environment in which ideas can quicly be tested and interated upon. I have found that Vagrant is usually suited perfectly for filling this need. If you want to use Vagrant as well, first you’ll need to install it from the VagrantUp Hashicorp website.

The idea here is to create an environment with a single master and minion from which changes to Salt formulas, modules, pillars, orchs, beacons, engines, etc. can quickly and easily be tested.

Setting Up Vagrant

Because I like to break out my Vagrant environments based on the project or environment for which I need to work on, I create the directory ~/Documents/vagrant/salt and then change to it and run vagrant init.

Inside this directory, I need a few files:

  • Vagrantfile
  • The salt bootstrap script aka
  • A salt-master config file aka salt_master_config
  • A salt-minion config file aka salt_minion_config
  • A YAML file to describe the hosts to be created aka vagrant_hosts.yml


Vagrant is Ruby based, and the Vagrantfile is also Ruby based. Meaning it can be glommed onto for things we want or need. In my case, I used a Vagrantfile derived from

# -*- mode: ruby -*-
# vi: set ft=ruby :

require 'yaml'
hosts = YAML.load_file('vagrant_hosts.yml')

# Set options for the network interface configuration. All values are
# optional, and can include:
# - ip (default = DHCP)
# - netmask (default value =
# - mac
# - auto_config (if false, Vagrant will not configure this network interface
# - intnet (if true, an internal network adapter will be created instead of a
# host-only adapter)
def network_options(host)
options = {}
if host.has_key?('ip')
options[:ip] = host['ip']
options[:netmask] = host['netmask'] ||= ''
options[:type] = 'dhcp'

if host.has_key?('mac')
options[:mac] = host['mac'].gsub(/[-:]/, '')
if host.has_key?('auto_config')
options[:auto_config] = host['auto_config']
if host.has_key?('intnet') && host['intnet']
options[:virtualbox__intnet] = true

def box_choice(host)
choice = ''
if host.has_key?('box')
choice = host['box']
choice = 'centos/7'

$script = <<-SCRIPT
sudo yum -y install python3
sudo ln -s /srv/salt /etc/salt

Vagrant.configure("2") do |config|
hosts.each do | host |
if host.has_key?('sync_folder')
config.vm.synced_folder host['sync_folder']['host'], host['sync_folder']['guest'], type: 'nfs'
config.vm.define host['name'] do | node | = box_choice(host)
node.vm.hostname = host['name'] :private_network, network_options(host)
node.vm.provider :virtualbox do | vb | = host['name']
vb.cpus = host['cpus'] ||= 1
vb.memory = host['memory'] ||= 512

if host.has_key?('sync_folder')
node.vm.provision 'shell',
inline: $script

node.vm.provision :salt do | salt |
if host.has_key?('sync_folder')
salt.master_config = 'salt_master_config'
salt.bootstrap_script = ''
salt.bootstrap_options = '-M'
salt.python_version = '3'
salt.minion_config = 'salt_minion_config'
salt.run_highstate = false
salt.bootstrap_script = ''
salt.install_type = 'stable'
salt.install_args = '2019.2'

Salt-Bootstrap Script

The salt-bootstrap script should be retrieved directly from the SaltStack project with:

curl -o -L

Salt-Master Config

The salt master config I use is usually exactly the same as the one I use in production just stripped of comments to make for easier deciphering.

grep -v '^$\|^#' master > salt_master_config

Salt-Minion Config

The minion config is about a simple as I can make it.

file_client: remote
hash_type: sha512

Vagrant Hosts YAML

This is just a YAML file that describes the instances I want built for this environment.

- name: minion1
cpu: 1
memory: 1024
box: centos/7
- name: master1
cpu: 1
memory: 1024
box: centos/7
host: "~/Documents/salt-master-configuration"
guest: "/srv/salt"

The magic in this setup is:

  1. The master uses an NFS mount to the host for the Salt pillars, states, etc.
  2. Using the name key in the Vagrant Hosts YAML, we can simulate any environment.
  3. We can run a different version of Salt just by modifying the Vagrantfile.
  4. Minions come up automatically pointed to the Master. Their keys still must be approved manually, but that’s a small price to pay. Don’t forget, the salt pki is on your host, so you’re adding a minion key there.


For this particular setup, the entirety of the Salt configuration is contained in the salt-master-configuration directory. This is forumlas, pillars, everything.

Using Vagrant

If all the pieces are in place, just run vagrant up and the machines will be created. Once they are running, you can access them with vagrant ssh master1 or vagrant ssh minion1.

Typically, while I’m using this set up, I have an editor with the Salt related code I’m working on open as well as a terminal window with an SSH connection to minion1 running in Vagrant. Because Vagrant has setup an NFS mount between the salt-master-configuration directory on my laptop and the master1 instance, as I make changes in the file in the editor, I can use salt-call on the minion to check the effect.

Originally published at on September 8, 2020.



Climber, surfer, yogi, dad who does some IT on the side to get by.

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Aleksandr Rain

Climber, surfer, yogi, dad who does some IT on the side to get by.