These are some of the most commonly encountered problems or frequently asked questions which we receive from users. They aren’t intended as a substitute for reading the rest of the documentation, especially the usage docs, so please make sure you check those out if your question is not answered here.
While Fabric can be used for many shell-script-like tasks, there’s a slightly unintuitive catch: each run or sudo call has its own distinct shell session. This is required in order for Fabric to reliably figure out, after your command has run, what its standard out/error and return codes were.
Unfortunately, it means that code like the following doesn’t behave as you might assume:
def deploy(): run("cd /path/to/application") run("./update.sh")
If that were a shell script, the second run call would have executed with a current working directory of /path/to/application/ – but because both commands are run in their own distinct session over SSH, it actually tries to execute $HOME/update.sh instead (since your remote home directory is the default working directory).
A simple workaround is to make use of shell logic operations such as &&, which link multiple expressions together (provided the left hand side executed without error) like so:
def deploy(): run("cd /path/to/application && ./update.sh")
Fabric provides a convenient shortcut for this specific use case, in fact: cd.
You might also get away with an absolute path and skip directory changing altogether:
def deploy(): run("/path/to/application/update.sh")
However, this requires that the command in question makes no assumptions about your current working directory!
This message is typically generated by programs such as biff or mesg lurking within your remote user’s .profile or .bashrc files (or any other such files, including system-wide ones.) Fabric’s default mode of operation involves executing the Bash shell in “login mode”, which causes these files to be executed.
Because Fabric also doesn’t bother asking the remote end for a tty by default (as it’s not usually necessary) programs fired within your startup files, which expect a tty to be present, will complain – and thus, stderr output about “stdin is not a tty” or similar.
There are multiple ways to deal with this problem:
Because Fabric executes a shell on the remote end for each invocation of run or sudo (see also), backgrounding a process via the shell will not work as expected. Backgrounded processes may still prevent the calling shell from exiting until they stop running, and this in turn prevents Fabric from continuing on with its own execution.
The key to fixing this is to ensure that your process’ standard pipes are all disassociated from the calling shell, which may be done in a number of ways:
Use a pre-existing daemonization technique if one exists for the program at hand – for example, calling an init script instead of directly invoking a server binary.
Run the program under nohup and redirect stdin, stdout and stderr to /dev/null (or to your file of choice, if you need the output later):
run("nohup yes >& /dev/null < /dev/null &")
(yes is simply an example of a program that may run for a long time or forever; >&, < and & are Bash syntax for pipe redirection and backgrounding, respectively – see your shell’s man page for details.)
Use tmux, screen or dtach to fully detach the process from the running shell; these tools have the benefit of allowing you to reattach to the process later on if needed (among many other such benefits).
While Fabric is written with bash in mind, it’s not an absolute requirement. Simply change env.shell to call your desired shell, and include an argument similar to bash‘s -c argument, which allows us to build shell commands of the form:
/bin/bash -l -c "<command string here>"
where /bin/bash -l -c is the default value of env.shell.
The -l argument specifies a login shell and is not absolutely required, merely convenient in many situations. Some shells lack the option entirely and it may be safely omitted in such cases.
A relatively safe baseline is to call /bin/sh, which may call the original sh binary, or (on some systems) csh, and give it the -c argument, like so:
from fabric.api import env env.shell = "/bin/sh -c"
This has been shown to work on FreeBSD and may work on other systems as well.
Due to a bug of sorts in our SSH layer, it’s not currently possible for Fabric to always accurately detect the type of authentication needed. We have to try and guess whether we’re being asked for a private key passphrase or a remote server password, and in some cases our guess ends up being wrong.
The most common such situation is where you, the local user, appear to have an SSH keychain agent running, but the remote server is not able to honor your SSH key, e.g. you haven’t yet transferred the public key over or are using an incorrect username. In this situation, Fabric will prompt you with “Please enter passphrase for private key”, but the text you enter is actually being sent to the remote end’s password authentication.
We hope to address this in future releases by modifying a fork of the aforementioned SSH library.
Currently, no, it’s not – the present version of Fabric relies heavily on shared state in order to keep the codebase simple. However, there are definite plans to update its internals so that Fabric may be either threaded or otherwise parallelized so your tasks can run on multiple servers concurrently.