Salt SSH allows you to execute commands and apply state to minions without having to install a salt-minion. The only requirement is for the minion to have python installed unless using the -r option to execute raw commands.

For reference the following software will be used in this post.

  • salt-ssh - 2018.3.1 (Oxygen)
  • Ubuntu (host) - 16.04
  • Debian (guest) - 9.4


Create and change into a working directory named salt-ssh.

mkdir /home/bradmin/salt-ssh && cd /home/bradmin/salt-ssh

I will use pipenv to manage a virtual environment and the installation of salt-ssh. If you are not using pipenv you can use pip with a virtual environment to install salt-ssh.

pipenv install salt-ssh

Activate the virtual environment.

pipenv shell


By default Salt expects its configuration files to be located in the /etc/salt directory. I am running Salt SSH as a non-root user so there are a number of directories that need to be created which are writable for my user.

mkdir -p {config,salt/{files,templates,states,pillar,formulas,pki/master,logs}}

# Directory Structure
├── config
└── salt
    ├── files
    ├── formulas
    ├── logs
    ├── pillar
    ├── pki
    │   └── master
    ├── states
    └── templates


The master config file has the same declarations that you would define when using Salt in master mode. Create a master config file with the following contents that points Salt SSH to the location of the previously created directories.

# salt-ssh/config/master
    - "/home/bradmin/salt-ssh/salt"

cachedir: "/home/bradmin/salt-ssh/salt/cache"

pki_dir: "/home/bradmin/salt-ssh/salt/pki/master"


The roster file is used to define remote minions and their connection parameters. I am using Vagrant for this lab so I will reuse the Vagrant user credentials. Create a roster file with the following contents.

# salt-ssh/config/roster
  host: ""
  user: "vagrant"
  priv: "~/.vagrant.d/insecure_private_key"


The Saltfile allows you to set command line configuration option in a file instead of declaring them at runtime. Create a Saltfile with the following contents.

# salt-ssh/Saltfile
  config_dir: "/home/bradmin/salt-ssh/salt/config"
  roster_file: "/home/bradmin/salt-ssh/salt/config/roster"
  ssh_log_file: "/home/bradmin/salt-ssh/salt/logs/salt-ssh-log.txt"
  log_file: "/home/bradmin/salt-ssh/salt/logs/salt-log.txt"

For reference the final directory structure should look like the following.

# Directory Structure
├── config
│   ├── master
│   └── roster
├── salt
│   ├── cache
│   ├── files
│   ├── formulas
│   ├── logs
│   ├── pillar
│   ├── pki
│   │   └── master
│   ├── state
│   └── templates
└── Saltfile


With the base configuration done, lest run a few tests. Salt SSH is controlled using the salt-ssh command. Aside from that you can use the same command line options that are available with the regular salt command.

salt-ssh '*'

# output


The first time you execute salt-ssh it will create SSH keys. This makes the execution time take longer than normal but subsequent runs are much faster. You may also need to accept the minions SSH key fingerprint. For more details see here.

If you successfully connected lets try install a package.

salt-ssh '*' pkg.install cowsay --sudo

# output

Alright as a final test, lets execute a command on the minion.

salt-ssh '*' 'cowsay im a salty moo cow'

# output
    < im a salty moo cow >
            \   ^__^
             \  (oo)\_______
                (__)\       )\/\
                    ||----w |
                    ||     ||


Most of the issues I ran into related to running Salt as a non-root users. You can run salt-ssh with the -l debug flag to get a more detailed look at the execution and errors if any.

salt-ssh '*' 'df -h' -l debug

# output
[INFO    ] Loading Saltfile from '/home/bradmin/salt-ssh/Saltfile'
[DEBUG   ] Reading configuration from /home/bradmin/salt-ssh/Saltfile
[DEBUG   ] Reading configuration from /home/bradmin/salt-ssh/config/master
[DEBUG   ] Configuration file path: /home/bradmin/salt-ssh/config/master
[WARNING ] Insecure logging configuration detected! Sensitive data may be logged.
[DEBUG   ] LazyLoaded flat.targets
[DEBUG   ] LazyLoaded jinja.render
[DEBUG   ] LazyLoaded yaml.render
[DEBUG   ] compile template: ./config/roster
[DEBUG   ] Jinja search path: ['/home/bradmin/salt-ssh/salt/cache/files/base']
[DEBUG   ] LazyLoaded roots.envs
[DEBUG   ] Could not LazyLoad roots.init: 'roots.init' is not available.
[DEBUG   ] Updating roots fileserver cache
[PROFILE ] Time (in seconds) to render './config/roster' using 'jinja' renderer: 0.04327249526977539
[DEBUG   ] Rendered data from file: ./config/roster:
  host: ""
  user: "vagrant"
  priv: "~/.vagrant.d/insecure_private_key"

[DEBUG   ] Results of YAML rendering:
OrderedDict([('test', OrderedDict([('host', ''), ('user', 'vagrant'), ('priv', '~/.vagrant.d/insecure_private_key')]))])
[PROFILE ] Time (in seconds) to render './config/roster' using 'yaml' renderer: 0.0015110969543457031
[DEBUG   ] Matched minions: {'test': {'host': '', 'user': 'vagrant', 'priv': '~/.vagrant.d/insecure_private_key'}}
[DEBUG   ] LazyLoaded roots.envs
[DEBUG   ] Could not LazyLoad roots.init: 'roots.init' is not available.
[DEBUG   ] Updating roots fileserver cache
[DEBUG   ] LazyLoaded local_cache.prep_jid
[DEBUG   ] Adding minions for job 20180616115608058884: ['minion']
[DEBUG   ] Could not LazyLoad '' is not available.
[DEBUG   ] Performing shimmed, blocking command as follows: df -h
[DEBUG   ] Executed SHIM command. Command logged to TRACE
[DEBUG   ] Child Forked! PID: 27215  STDOUT_FD: 10  STDERR_FD: 12
[DEBUG   ] VT: Salt-SSH SHIM Terminal Command executed. Logged to TRACE
[DEBUG   ] LazyLoaded nested.output
    Filesystem      Size  Used Avail Use% Mounted on
    udev            2.0G     0  2.0G   0% /dev
    tmpfs           396M  5.4M  391M   2% /run
    /dev/vda1       9.2G  1.4G  7.3G  17% /
    tmpfs           2.0G     0  2.0G   0% /dev/shm
    tmpfs           5.0M     0  5.0M   0% /run/lock
    tmpfs           2.0G     0  2.0G   0% /sys/fs/cgroup
    tmpfs           396M     0  396M   0% /run/user/1000


Salt SSH is a nice alternative for minions where it is not possible and/or convenient to install a salt-minion. Common uses for running Salt SSH include managing IOT devices and bootstrapping minions with Salt to manage them in master mode.


Published: 2018-06-16