Using OpenPGP on Remote Systems

Posted on 1 2026

As the number of servers in this setup grows, you will increasingly find yourself needing GPG operations on remote machines. Signing Git commits. Decrypting configuration files. Running scripts that need access to encrypted credentials. The naive solution is to copy your private key to each server. This is the wrong solution. Every copy of a private key is an additional attack surface, and a server is inherently more exposed than a desktop that only you use.

The right solution is GPG agent forwarding. Your private keys stay on your desktop, or better still on a Yubikey, and the GPG agent running locally handles all cryptographic operations. The remote server makes requests to the forwarded agent over the SSH tunnel, gets its answers, and never sees the private key at all.

How it works

The GPG agent communicates via a Unix domain socket. OpenSSH, since version 6.7, can forward Unix domain sockets over an SSH connection in the same way it forwards TCP ports. By pointing the remote server’s GPG socket at your local agent’s socket, any GPG operation on the server is transparently handled by your local agent, with any passphrase prompts appearing on your desktop rather than in the SSH session.

The local agent exposes two sockets: the main agent socket used by your local GPG, and an extra socket specifically intended for this kind of remote forwarding. The extra socket is more restricted than the main socket, which is the appropriate level of access for a remote system.

Local configuration

Verify the extra socket exists

GnuPG 2.4.x creates the extra socket automatically. Find its location:

gpgconf --list-dirs agent-extra-socket

The output will be something like:

/run/user/1000/gnupg/S.gpg-agent.extra

Note this path. You will need it for the SSH client configuration.

Confirm allow-loopback-pinentry

Add the following to ~/.gnupg/gpg-agent.conf if not already present:

allow-loopback-pinentry

This allows the pinentry dialog to be handled by the local agent even when GPG is invoked remotely. Without it, passphrase prompts may fail or appear in unexpected places. Reload the agent after adding it:

gpgconf --reload gpg-agent

Remote server configuration

On each server you want to forward to, add the following to /etc/ssh/sshd_config:

StreamLocalBindUnlink yes

This tells the SSH server to remove an existing socket file before creating the forwarded one. Without it, if the socket already exists from a previous session, the forward will fail silently. Restart the SSH daemon after making this change:

sudo systemctl restart sshd

Suppress remote gpg-agent autostart

When GPG runs on the remote server and cannot find an agent, it tries to start one. This would create a new agent socket, overwriting your forwarded one. Prevent this by adding --no-autostart to GPG invocations on the remote, or by setting it in the remote ~/.gnupg/gpg.conf:

no-autostart

Alternatively, disable the GPG agent socket units on the remote server entirely, since the server should not be running its own agent:

systemctl --user disable --now gpg-agent.service gpg-agent.socket \
    gpg-agent-extra.socket gpg-agent-ssh.socket

Ensure the socket directory exists

On systems where systemd manages /run/user/<uid>, the gnupg subdirectory may not exist at login time. Add the following to ~/.bashrc on the remote server:

gpgconf --create-socketdir

This ensures the directory exists before SSH tries to create the socket in it.

SSH client configuration

With the local and remote sides prepared, configure the forward in your local ~/.ssh/config. The RemoteForward directive maps the remote agent socket to your local extra socket:

Host server.yourdomain.net
    RemoteForward /run/user/1000/gnupg/S.gpg-agent /run/user/1000/gnupg/S.gpg-agent.extra

Replace /run/user/1000/ with the correct path for your user ID. Find your local UID with id -u and the remote user’s UID by logging in and running id -u there.

If you connect to multiple servers with different user IDs, or want to limit forwarding to specific hosts, use the Match directive:

# Forward GPG agent to specific servers only
Match Host server1.yourdomain.net,server2.yourdomain.net
    User yourusername
    RemoteForward /run/user/1000/gnupg/S.gpg-agent /run/user/1000/gnupg/S.gpg-agent.extra

You can also use the inline flag for occasional one-off connections:

ssh -R /run/user/1000/gnupg/S.gpg-agent:/run/user/1000/gnupg/S.gpg-agent.extra \
    user@server.yourdomain.net

Distributing your public keyring to remote servers

Agent forwarding provides access to your private key operations, but GPG on the remote server still needs your public keys in its keyring to verify and encrypt. The private key never leaves your desktop; the public key is safe to copy anywhere.

Export your public key and copy it to each server:

gpg --armor --export $GPGKEY | ssh user@server.yourdomain.net \
    "gpg --import && gpg --import-ownertrust"

Set ultimate trust on the remote:

ssh user@server.yourdomain.net \
    "echo '$(gpg --export-ownertrust)' | gpg --import-ownertrust"

Verify it is in place on the remote:

ssh user@server.yourdomain.net "gpg --list-keys"

Testing the forward

Connect to the server using your configured SSH host:

ssh server.yourdomain.net

On the remote, check that the forwarded socket exists:

ls -la /run/user/$(id -u)/gnupg/S.gpg-agent

Test that GPG can reach your local agent:

gpg --list-secret-keys

This should list your keys, with the primary key marked as # (not available locally) and the subkeys available via the forwarded agent.

Test a signing operation:

echo "test" | gpg --clearsign

A passphrase prompt should appear on your local desktop via the Qt pinentry dialog, not in the SSH session. After entering it, the signed output should appear in the terminal.

Using with Yubikey

If your subkeys are stored on a Yubikey rather than on the desktop, agent forwarding works identically. The Yubikey is physically connected to your desktop, your local GPG agent handles all operations, and the remote server is entirely unaware of the hardware token. This is the most secure configuration: the private key never leaves the Yubikey, let alone travels to a remote server.

The Yubikey setup is covered in the next section.

Agent forwarding, like SSH agent forwarding, should only be used with servers you trust. A malicious or compromised server with root access could use the forwarded socket to request signing operations from your local agent. Limit forwarding to servers you control.