Skip to content

Optionally avoid using ssh if going to localhost #98

bitprophet opened this Issue Aug 19, 2011 · 53 comments
Fabric member


run()/sudo() would intelligently see that you're going to localhost and just run local() instead. This would probably be an optional thing.

Comments from Jeff on IRC:

and yea, I mean there's always going to be overhead with ssh vs straight pipes offhand I don't think it would be terrifically difficult to update run/sudo (especially in master now that they've been refactored) to call/return local() intelligently I'm not positive that I'd want that semi magical behavior in core (even with it off by default with an optin to enable it, tho that would help) but even so, it'd be an interesting experiment. and if it is as simple as I'm thinking I honestly can't come up with a good reason not to (again provided it is not the default behavior)

Originally submitted by Nick Welch (mackstann) on 2009-11-11 at 01:39pm EST


  • Duplicated by #364: Allow for local operation to bypass SSH layer
  • Related to #26: Implement "dry run" feature
@bitprophet bitprophet was assigned Aug 19, 2011
Fabric member

James Pearson (xiong.chiamiov) posted:

As also mentioned on irc, I don't normally run ssh server on a desktop machine, so I can't actually ssh to localhost.

on 2009-11-11 at 03:13pm EST

Fabric member

Travis Swicegood (tswicegood) posted:

I've just implemented something similar this evening in the form of a new fabric.operations function called do. It looks at env.run_as to see if it equals "local", and in doing so switches out to the local method instead of the run (or sudo if sudo=True is passed in as a kwarg). It also handles prefixing local commands with sudo in the event they're running local.

This is sort of a different way around this problem which works without changing the behavior of run or sudo. These changes are available in my repository.

on 2010-01-11 at 12:22am EST

Fabric member

Morgan Goose (goosemo) posted:

I really don't see this being plausible. What's the point in doing run as local. One of the requirements of Fabric is sshd running on the machine, remote or loopback. The other problem being that only changing local doesn't take into account put, get, rsync_project, and others that would all still need ssh. Trying to implement those, would just really cause more issues, since it's now in the realm of making fabfiles translate to bash.

on 2011-03-13 at 11:14pm EDT

Fabric member

Jeff Forcier (bitprophet) posted:

While I'm also not 100% convinced this is a great idea, it's clearly something a number of users feel the need for -- another request has been lodged as #364 with another explanation of the use case.

I've also added the dry-run ticket as related to this one, because (I assume -- if any of the requesting users can verify this that'd be great) the main use case for this feature is for testing/dry-running.

on 2011-06-23 at 11:26am EDT

Fabric member

As noted in #538, if we're ever able to fully normalize the three runners so they can be used interchangeably, we'll need to make sure that shell escaping works consistently across them. Right now we don't shell escape local, though that's at least partly because it's not using a shell wrapper.


If anyone is wondering "why would anyone do this?", the answer is that if you have a deployment pipeline, it can be helpful to run the same exact deployment script, no matter which environment, rather than having a special setup script for localhost vs. everything else.

brodul commented Aug 18, 2012

+1 for the feature





Spaxe commented Dec 10, 2012




To hold you over, you can just make sure you have the OpenSSH server running. First do sudo apt-get install ssh to make sure you have it installed (even if you think you do). Then do sudo service ssh start|stop|restart as needed. Learned from this thread.



My use case is simple: I want to use the same django-deploy script to configure ec2 instances both with cloud-init through CloudWatch (the case for running local commands) and using the regular fab deploy_django -H foo@bar.



This would be really useful. One use case I have is using vagrant shell provisioner to configure particular vm using fabric and without the need to ssh localhost.

grjones commented May 19, 2013


I was surprised not to see this in Fabric already.


FYI: Implementation of this feature gets more complex when you think about fabric functions like reboot().



Should be part of core already !



It would perfectly make sense: from an abstract point of view, local is just a special case of run, where no SSH machinery is involved.

One more thing to point out (maybe obvious): Fabric should be smart enough to decide if a run should be converted to local AFTER reading /etc/hosts.

I mean: if we have = [ 'mywebserver' ]

and in /etc/hosts we have: mywebserver

then, any run calls should actually be local calls.

Taking this concept a step further, we should also treat run as a local call when the remote host resolves to an IP which is assigned to a network interface of the local machine.
fabfile: = [ 'mywebserver' ]

/etc/hosts: mywebserver

ip addr:




+1 👍

maxired commented Mar 1, 2014






xarg commented Apr 24, 2014

Fabric 2 will use pyinvoke/invoke so this should be pretty easy to do there. I would wait for Fabric 2 for a non-hacky way to do this.



sbward commented May 20, 2014





👍 Please implement this, especially as mac computers aren't automatically set up to have SSH tunnels configured for remote access to the localhost server.

dtbog commented Jul 24, 2014

+1 :)

tolbkni commented Jul 28, 2014

+1 please



guchio commented Aug 14, 2014





We're using Fab to build debian packages and this adds extra complexity

Friz-zy commented Oct 8, 2014

guys, hello all
i try to create clone of fabric with difference:

  • run() function works in the same way with subprocess.popen under localhost as under ssh connect to remote host
  • Factory uses openssh or any another ssh client (you should modified config for this), so you can use all power of ssh sockets
  • Factory uses gevent library for asynchronous executing

You can take a look if you need this feature


I may be missing something in this discussion, but here is what I did to use the same code with fab run command on both localhost and remote machines.

  1. I set env.use_ssh_config = True in my
  2. ssh-copy-id localhost

This doesn`t solve your issue if you are not running ssh server on your local machine

bes89 commented Apr 16, 2015


laike9m commented Jun 2, 2015



+1 Please implement this feature :)



Could be very useful to bootstrap Docker images using existing Fabric scripts. This feature would avoid to install an SSH server on the container, which is against the Docker best practices

F481 commented Jul 9, 2015


sbilly commented Jul 25, 2015


cscetbon commented Aug 8, 2015


foxx commented Aug 30, 2015

Further to the answer provided by @AntoniosHadji, here are the complete instructions to make this work;

# Generate new SSH key for local usage
ssh-keygen -f ~/.ssh/id_rsa -N ''

# Add server keys to users known hosts (eliminates 'are you sure' messages);
ssh-keyscan -H localhost > ~/.ssh/known_hosts

# Allow user to ssh to itself
cat ~/.ssh/ >> ~/.ssh/authorized_keys

Actually, this can be done using cuisine. You need to change all run executions to reference function, which can be done easily with an import, and change the mode to local:

from cuisine import run, mode_local

print run("echo Hello")

For simple use cases, this works for me:

from fabric.api import run, local
# ...
# in task:
  if is None or == 'localhost':
    run = local


I want my fabfile to run remotely or locally when ssh isn't an option. This includes local wrappers for get/put/exists etc.


👍 I have fabfiles that run both locally and remotely and I've ended up hacking my own wrapper functions for run/local/get to deal with all of subtle differences such as output capture and error handling.


What if you have a ssh connection doing dynamic port forwarding and binding on (still technically localhost) on port 2223. I can see how this could cause issues, to that end matching on localhost and resolving to rather than also supporting the entire class might be a good idea to handle.


@blade2005 Yep, the whole 127...* range point to your localhost(except and but when you are actually pointing to your localhost you won't use port right?
So I believe that we can safely assume that 127.*.*.* == localhost and ssh can be avoided but 127.*.*.*:* point to a forwarded port and ssh is needed.


Honestly, this feature would probably make more sense as a 3rd party plugin built on fabric, similar to the cuisine library. Then we would just import wrapped functions for run/get/put/etc, which would know wether to run locally or remotely based on an env variable. At least this way, somebody could get this started for everybody to use.

I implemented something locally, and its a lot more work than just switching between local/run. You have to consider prefixes, changed directories, sudo users, etc.

Fabric member
bitprophet commented Jun 20, 2016 edited

Was briefly thinking about this in the context of another 2.0 related ticket, and realized that there's more that comes up besides just "run becomes a rebinding of local":

  • Any sort of truly-mixed-mode task using both local and run, or either of put/get, becomes inherently problematic: operations with clearly defined 'local' and 'remote' "ends" now both point locally.
    • I'd assume that to be a minority use case (if it's one at all) but it still needs to be figured out, even if it's "calling any operation but run or sudo raises DoesntMakeAnySenseError" or whatever.
    • put/get could presumably just turn into shutil.copy or similar
    • local would presumably not be changed (though when printing what's happening, probably still want it differentiated from what run-except-locally is prefixed with...?)
    • Touched on above, the various context-manipulating methods/contextmanagers like prefix, cd etc all need similar questions answered.
  • That aside, locally running sudo commands at all, is a potentially enormous footgun and probably wants additional safety checks.
    • Unless it, too, becomes just another binding to local, which is another possibility. Though not a large one, any sudo commands that even work locally (i.e. one is deploying to, and deploying from, Linux) would presumably need to remain privileged locally (e.g. apt/yum and friends, firewall tinkering, etc).
  • sudo also (as noted above by Jon) needs to grow possibility of configuring distinct local-vs-remote config vectors since the sudo user, password etc is likely to differ between the two sides.
    • Though since I'm thinking of all this in the context of Fab 2, the expected per-host config overrides would probably solve that part of things at least - the localhost context would simply be handed the appropriate values. (Plus, as a dedicated "for running remote things locally" Context subclass it could do other things too, if needed).
@bitprophet bitprophet added this to the 2.0 milestone Jun 20, 2016
@bitprophet bitprophet referenced this issue in pyinvoke/invoke Jun 20, 2016

Convenient sudo() wrapper #294

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.