Using git while on-the-run
First published: 07 Jul 2025
This post describes how to use a custom git configuration, including SSH and GPG keys, stored on an external drive such as a USB stick. This is nothing fancy but comes in handy when the user does not want to store their keys persistently on the machine.
This setup originates from situations where I would like to work on my personal git repositories, yet I don't want to store my git configuration, my SSH key for authentication or my GPG key for signing commits on the device that I'm currently working with. This is for example often the case at work where I use a company laptop, virtual machines and servers on which I do not want my key material to persist on the system nor to conflict with the existing configuration.
This is of course intended for machines that are trusted enough to load key material onto. By no means do I recommend plugging such a USB stick into non-trusted devices.
Generating and storing the secret keys#
For those who haven't got working pairs of SSH and GPG keys already, I recommend looking up the best practices on generating them. The following resources are a good starting point:
- GPG: Riseup, Marcus Holtz and Mike Ross
- SSH: Brandon Checketts
I store my git configuration file, my SSH key used for git authentication, my GPG subkey to sign commits on LUKS filesystems that are unlocked by using a passphrase. Here is how I structure the files on them:
/mnt/usb/
├── alias.sh
├── git/
│ └── config
├── keys/
│ ├── gpg/
│ │ ├── public.asc
│ │ ├── public.gpg
│ │ ├── revocation.asc
│ │ ├── subkey.asc
│ │ └── subkey.gpg
│ └── ssh/
│ ├── known_hosts
│ ├── skywhi_ed25519
│ └── skywhi_ed25519.pub
└── signing_keyring/
├── gpg.conf
└── pubring.kbx
Working on a new device#
The usage of environment variables, as opposed to aliases, will not only apply
the new settings to git but will also propagate them throughout the shell
session to other programs such as make and custom scripts. The environment
variables that we will set are GIT_CONFIG_GLOBAL, GIT_SSH_COMMAND and
GNUPGHOME.
GIT_CONFIG_GLOBALtells git to use a custom configuration file,GIT_SSH_COMMANDspecifies the SSH command used by git to authenticate to remote repositories.GNUPGHOMEpoints towards a GPG keyring and configuration file used when git calls gpg to sign commits.
$ man git | grep -A1 'GIT_CONFIG_GLOBAL'
GIT_CONFIG_GLOBAL, GIT_CONFIG_SYSTEM
Take the configuration from the given files instead from global or
system-level configuration files. If GIT_CONFIG_SYSTEM is set, the system
config file defined at build time (usually /etc/gitconfig) will not be
read. Likewise, if GIT_CONFIG_GLOBAL is set, neither $HOME/.gitconfig nor
$XDG_CONFIG_HOME/git/config will be read. Can be set to /dev/null to skip
reading configuration files of the respective level.
$ man git | grep 'GIT_SSH_COMMAND'
GIT_SSH, GIT_SSH_COMMAND
If either of these environment variables is set then git fetch and git push
will use the specified command instead of ssh when they need to connect to
a remote. The command-line parameters passed to the configured command are
determined by the ssh variant. See ssh.variant option in git-config(1) for
details.
$GIT_SSH_COMMAND takes precedence over $GIT_SSH, and is interpreted by the
shell, which allows additional arguments to be included. $GIT_SSH on the
other hand must be just the path to a program (which can be a wrapper shell
script, if additional arguments are needed).
Usually it is easier to configure any desired options through your personal
.ssh/config file. Please consult your ssh documentation for further details.
$ man gpg | grep -A1 'GNUPGHOME$'
GNUPGHOME
If set directory used instead of "~/.gnupg".
The SSH command itself carries two options that make the setup more self-contained:
-o IdentitiesOnly=yesmakes SSH authenticate with the key from the drive only, instead of first offering whatever identities a runningssh-agenton the host may hold.-o UserKnownHostsFilekeeps theknown_hostsfile on the drive as well: the trusted host keys travel with the setup, and no trace of the visited remotes is left in the host's~/.ssh/known_hosts.
Instead of exporting these variables into the current shell I prefer to spawn a
subshell with the new environment variables. Exiting the subshell brings back
the untouched parent shell: there is nothing to restore and no restore logic to
get wrong. The function gitshell defined in /mnt/usb/alias.sh takes care of
this.
gitshell() {
# Exit on nested instances
if [ -n "${GIT_USB_SHELL:-}" ]; then
echo 'gitshell: already inside a gitshell, exit it first' >&2
return 1
fi
# Resolve the directory containing this script, in bash or zsh
local KEY_DIR
if [ -n "${BASH_SOURCE:-}" ]; then
KEY_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
else
KEY_DIR="$( cd "$( dirname "${(%):-%x}" )" && pwd )"
fi
# Fail when keys are not found
local file
for file in "${KEY_DIR}/git/config" \
"${KEY_DIR}/keys/ssh/skywhi_ed25519" \
"${KEY_DIR}/keys/ssh/known_hosts" \
"${KEY_DIR}/signing_keyring/pubring.kbx"; do
if [ ! -e "$file" ]; then
echo "gitshell: '$file' not found" >&2
return 1
fi
done
# Set all variables and call the subshell
GIT_CONFIG_GLOBAL="${KEY_DIR}/git/config" \
GIT_SSH_COMMAND="ssh -o IdentitiesOnly=yes \
-o UserKnownHostsFile='${KEY_DIR}/keys/ssh/known_hosts' \
-i '${KEY_DIR}/keys/ssh/skywhi_ed25519'" \
GNUPGHOME="${KEY_DIR}/signing_keyring" \
GIT_USB_SHELL=1 \
"${SHELL:-bash}"
}
The variable GIT_USB_SHELL tracks if the current shell was called this way.
This can be used to customize $PS1 for example.
# In ~/.bashrc or ~/.zshrc
if [ -n "${GIT_USB_SHELL:-}" ]; then
PS1="(gitshell) ${PS1}"
fi
So whenever I need to do some git operations using the configuration files on
the drive /dev/sdb, I use the following commands:
# Open and mount the LUKS-encrypted device /dev/sdb
$ sudo cryptsetup open /dev/sdb cool-pendrive
$ sudo mount /dev/mapper/cool-pendrive /mnt/usb
# Load the function and start the subshell
$ . /mnt/usb/alias.sh
$ gitshell
# Do some git things in the subshell...
(gitshell) $ git commit -m 'My commit' # Will be signed with my GPG key
(gitshell) $ git push # And pushed with my SSH key
# Leave the subshell
(gitshell) $ exit
# Unmount the external drive
$ sudo umount /mnt/usb
$ sudo cryptsetup close cool-pendrive
Edit : Reworked the Bash script and SSH options.