I need to be able to locally run a script that will connect to various servers and run commands on them. What is the best way to accomplish this?


Personally, I would use Capistrano. It's friendly and written in Ruby and they already did all of the heavy lifting for you.

From Wikipedia:

Capistrano is a utility and framework for executing commands in parallel on multiple remote machines, via SSH.


you can run a command using ssh hostname command. If you have an entire script you need to execute, first use scp to transfer it to the remote host, then ssh to execute it.

scp script.sh host:script.sh
ssh host ./script.sh

What about using configuration management like puppet or chef? This is maybe a little over the top for only one script, but if you need several such scripts it might be worth to consider.


I would use ssh with key authentication, I believe ssh has a way to make sure that certain accounts can only log in from certain IP's so I would limit it to that because you might not want to set a passphrase on the keys (you could use a key manager to avoid that but it has limitations too)

  • Key based authentication, sure. However writing a script that starts with ssh user@host leaves me dropped into the host, blocking execution of the script. I need to remotely execute code. – Josh K Aug 17 '10 at 17:10
  • @Josh The next argument to ssh is the command, so ssh user@host ls / will list the root directories on that host. See Keith's answer – Michael Mrozek Aug 17 '10 at 17:56

A quickie bash 'for' loop might be easiest, perhaps something like:

for h in host1 host2 host3
   echo $h
   ssh user@$h "ps -ef"

Of course, cfengine/puppet/chef/capistrano are better configuration management options. If you wanted to interactively send commands to the various shells, clusterm (http://sourceforge.net/projects/clusterm/) is a solid choice too.


Puppet and Chef are "pull" systems and I've found that a complementary "push" system implemented using Capistrano, Fabric, or ssh(1) in a for-loop is necessary. Of course, that means public keys in place for authentication, too; fortunately, those can be managed by Puppet or Chef.


I have been pretty happy with a shell script called dssh.sh that utilizes ssh to communicate with many machines simultaneously. It can execute the same command across lots of machines simultaneously and wait for them all to exit before returning. To download and learn more about it, the best reference I have found is the BASH Cures Cancer blog.


I was having the exact same question a year ago, and after asking some people I started with Fabric. It was recommended to me over Puppet and Chef for it's simplicity.

You can easily mix local commands and remote commands in the same function. Also, being able to use Python instead of Bash is a notable upgrade that will help if you need to do lots of helper code besides the local/remote commands. Even if sometimes you need a trick to make simple things work, I still recommend Fabric.

Here's a nice introduction to Fabric: http://www.slideshare.net/panopticdev/fabric-a-capistrano-alternative The author claims that Fabric is better than Capistrano for simple systems.

Here I'll give you a small real-life example: transferring latest git repo code (stored in code_dir) to a remote machine and compiling:

# Use: fab -H <username>@<test_computer_ip> compile_on_test_computer
def compile_on_test_computer():
  # create a remote dir for incoming updates
  run("mkdir -p %s/update" % env.remoteDir)
  # create a local dir for outgoing updates
  lrun("mkdir -p %s/update" % env.localDir)

  # archive latest code, be it committed or not
  with lcd("%s/code_dir" % env.localDir):
    lrun("uploadStash=`git stash create` && git archive -o ../update/code_outgoing.zip $uploadStash || git archive -o ../update/code_outgoing.zip HEAD")

  # transfer source code to test computer, unzip and compile
  put("%s/update/code_outgoing.zip" % (env.localDir), 
  "%s/update/code_incoming.zip" % (env.remoteDir),
  with cd("%s" % env.remoteDir):
    run("unzip -uo update/code_incoming.zip -d code_dir")
    run("cd code_dir && ./compile_and_install_all.sh")

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.