How To?

Secure SSH Agent Forwarding for Git Deployments on VPS

Quick summary

If you want a VPS to access a private Git repository without copying your private SSH key to the server, SSH agent forwarding can help. The signing operation stays with your local SSH agent, but a VPS you do not fully trust may still request signatures through that agent.

  • The private key file is not transferred to the VPS, but processes that can access the forwarded agent socket may send signing requests.
  • Apply ForwardAgent only to the single server alias that needs it.
  • If the VPS is only a network gateway, ProxyJump usually provides a narrower access model.
  • Test the connection first with SSH and then with git ls-remote; do not solve an unexplained failure by copying the key to the server.

A deployment script running on your VPS may be unable to fetch code from a private Git repository. The first solution that comes to mind is often copying your local private SSH key to the server. Although this can work, you then become responsible for another copy of the key, its permissions, its rotation and the consequences if the VPS is compromised.

SSH agent forwarding offers another approach. Your SSH client forwards access to a socket belonging to the agent running on your local computer through the session to the VPS. The VPS can pass signing requests to that agent without seeing the private key itself. This meets the goal of not copying the key, but it does not make the VPS trusted automatically.

How SSH agent forwarding works

An SSH agent is a background process that handles cryptographic signing operations made with a private key. It does not normally give the remote system a copy of the key file. After you add a key to the agent, your SSH client sends the signing requests needed for a remote connection to that agent.

When forwarding is enabled, the remote session will usually contain an environment variable named SSH_AUTH_SOCK. It points to a temporary Unix socket on the remote system. A signing request sent through that socket travels over the SSH connection to your local agent, and the signed response returns to the VPS.

The private key itself is not copied to the VPS in this flow. However, a compromised or malicious VPS may send signing requests to the agent while the SSH session remains open. It cannot read the key, but it may still try to perform actions as the Git account or another service that accepts that key.

Caution

Agent forwarding prevents you from giving the private key file to an untrusted server; it does not guarantee safe use of an untrusted server. Use it only with VPSs that you manage, keep updated and understand in terms of access boundaries.

Choose the right access model

The decision depends on where the Git operation runs. If the VPS is only a jump point, ProxyJump is often the narrower model. You run Git on your own computer, while the SSH connection reaches the target Git server through the VPS. No agent socket is opened on the VPS, and the VPS does not need to request signatures as your Git account.

If the Git command really runs on the VPS, such as when a deployment script executes git fetch or git clone there, two common options are available:

  • Agent forwarding: The local agent is used temporarily. The private key is not placed on the VPS, but you accept the forwarding risk for the duration of the open session.
  • Deploy key or separate machine identity: You create a separate, limited key for the VPS. The identity is stored on the server, while its scope and rotation are managed separately.

Agent forwarding can be practical for a one-time maintenance task or a controlled deployment session. For continuously running automation, a separate machine identity with access limited to the relevant repository is usually a more predictable design than forwarding a personal key. Check your Git service’s documentation for repository-scoped or read-only key options.

If several people access the same system, also consider an SSH key management and access sharing approach. It helps you design access around identities, scope and rotation instead of personal keys and shared accounts.

When is ProxyJump more suitable?

If the Git client on your local computer can reach the target repository through the VPS, you can use ProxyJump in your SSH configuration and make the VPS only a gateway. ProxyJump is available in sufficiently recent OpenSSH clients; check your client version with ssh -V and consult your distribution documentation if the option is unknown. For example, your local ~/.ssh/config could contain:

Host vps-bastion
    HostName vps.example.net
    User deploy
    IdentityFile ~/.ssh/id_ed25519
    IdentitiesOnly yes

Host git-through-vps
    HostName git.example.com
    User git
    ProxyJump vps-bastion
    IdentityFile ~/.ssh/id_ed25519
    IdentitiesOnly yes

You can use the git-through-vps alias from your local Git client. The Git operation takes place on your computer, not on the VPS. ProxyJump changes the connection path; by itself, it does not expose your agent socket to a remote VPS shell.

When using this method, verify the target Git server’s host key in your local known_hosts file. Verifying the bastion VPS host key does not prove the identity of the target Git server.

Prerequisites for agent forwarding

Before changing any configuration, check three things: Is a local agent running? Does it contain the correct key? Is your SSH client configured to forward the agent only to the intended VPS? Also check the versions of the OpenSSH client and server, since supported configuration options can vary between distributions and releases.

Check the local agent and key

On Linux or macOS, you can inspect the current agent socket and loaded identities with:

ssh -V
echo "$SSH_AUTH_SOCK"
ssh-add -l

If ssh-add -l returns a key, at least one identity is loaded in the agent. If the list is empty, add the key you intend to use:

ssh-add ~/.ssh/id_ed25519
ssh-add -l

Replace the filename with your own key. The command does not print the private key contents. Your operating system or key may ask for its passphrase.

On Windows, OpenSSH Agent, WSL and other SSH clients may use different agent implementations. Run the equivalent checks in the terminal environment that will make the SSH connection.

If the agent contains many identities, the SSH server may try more keys than necessary and reach a Git service’s authentication limit. In that case, IdentitiesOnly yes can restrict identity selection for the relevant connection. It does not disable agent forwarding.

Limit forwarding to the target VPS

Instead of enabling forwarding for every SSH connection, attach it to a specific host alias:

Host git-deploy-vps
    HostName vps.example.net
    User deploy
    ForwardAgent yes
    IdentityFile ~/.ssh/id_ed25519
    IdentitiesOnly yes

Here, git-deploy-vps is only an SSH alias. Defining ForwardAgent yes under Host * may expose your agent socket to every server you connect to by mistake. To see the effective settings and the order in which they are applied, run:

ssh -G git-deploy-vps | grep -Ei 'forwardagent|hostname|user|identityfile|identitiesonly'

The output should show forwardagent yes, the expected host name, the expected user and the intended identity settings. This command does not connect to the server or change configuration.

Example scenario

Assume your local development computer has a key named ~/.ssh/id_ed25519_work that can access only a company repository, while a VPS using the deploy account fetches code during each deployment. In this scenario, forwarding is enabled only for the git-deploy-vps alias. The private key is not copied to the VPS, and forwarding is not enabled when you connect to other servers.

Diagnose the VPS before changing it

After the local checks pass, connect through the alias:

ssh git-deploy-vps

First check whether the agent socket is visible in the remote session:

printf '%s\n' "$SSH_AUTH_SOCK"
ssh-add -l

If SSH_AUTH_SOCK is empty, or ssh-add -l says it cannot connect to the agent, forwarding may be disabled in the client configuration, refused by the SSH server or absent from the remote session environment. Do not copy the key to the server at this point. Check the local configuration and the SSH daemon policy first.

An administrator should verify that the server’s SSH daemon configuration does not disable forwarding through AllowAgentForwarding. If a change is needed, make a backup of the current configuration and define how you will restore it before editing. For example, on Linux you can test the relevant configuration after a change with:

sudo sshd -t -f /etc/ssh/sshd_config

Do not reload the SSH service until the test succeeds. The reload command depends on the distribution and service manager, so use the documented command for your system. If the change breaks access, restore the existing file from its backup and test the daemon configuration again. Avoid changing unrelated authentication or network settings in the same step.

Test SSH, then test the repository

Once the forwarded socket is visible, test Git authentication at the SSH layer. If your Git service uses the SSH user git and the host name git.example.com, run:

ssh -T git@git.example.com

The first connection may ask you to verify the host key. Confirm its fingerprint through an independent, trusted channel before accepting it. A successful authentication message varies by provider, and the absence of an interactive shell does not by itself indicate failure.

Next, test access to a specific repository without downloading its data:

git ls-remote git@git.example.com:team/project.git

This command lists repository references. If it succeeds, the SSH identity, repository path and access permission work together. If you receive Permission denied (publickey), inspect which identities are being attempted with verbose SSH output:

ssh -vT git@git.example.com

The output does not contain the private key contents, but it can show whether the agent is being used and how authentication proceeds. When sharing diagnostic output, remove unnecessary usernames, host names and repository paths.

Check whether Git uses another SSH configuration

If the standalone ssh command succeeds but Git fails, check whether Git is using a different client or configuration. Run these commands in the environment where Git runs, such as the repository directory on the VPS:

git config --show-origin --get core.sshCommand
env | grep '^GIT_SSH'

A defined core.sshCommand may select a separate SSH setup that does not use forwarding. The narrowest correction is to select the intended SSH command for that repository only. For example, if the Git operation runs on the VPS, run this in the VPS repository directory:

git config core.sshCommand "ssh -F ~/.ssh/config"

This tells Git to use the standard SSH configuration file in that environment. If your organization requires another SSH client, adjust the value according to its documented standard instead of deleting it at random. After the change, run git ls-remote again in the same repository to verify the result.

Understand the security boundary

The main risk of agent forwarding is not that the private key can be read. It is that the agent’s signing ability can be used during the session. A malicious process on the VPS may use SSH_AUTH_SOCK to send signing requests to your agent. The impact depends on the key’s permissions, the Git account’s access scope and how long the connection remains open.

For that reason, do not forward personal keys with broad access or keys that reach several production systems. Create a separate work key, limit it to the required repository or environment where possible, and remove it from the agent when the task is complete:

ssh-add -d ~/.ssh/id_ed25519_work
ssh-add -l

If you need to remove every identity from the agent, use ssh-add -D. This also removes identities used by other projects, so check the effect on active sessions before using it in a shared working environment.

Recent OpenSSH versions and supported agents may provide more advanced controls, such as destination constraints. Do not treat these features as a security boundary until you have confirmed support from the client, agent and key type. The simpler and more portable protections are still limiting forwarding to one host and using a separate, low-privilege key.

For key rotation, file permissions and team access, also review the VPS SSH hardening guide covering FIDO2, SSH certificates and rotation options. Agent forwarding does not replace key management; it is only a way to use a private key for a specific connection without copying it to the server.

Tip

Separate a deployment script from the interactive SSH session used to start it manually. Making continuous automation depend on a local agent and an open developer connection can cause the deployment to stop working when that session closes. For a persistent workflow, evaluate a separate machine identity, a CI/CD secret mechanism or a repository-scoped deploy key.

Follow this troubleshooting order

  1. Check the local environment: Use ssh-add -l to confirm that the correct key is loaded, and echo "$SSH_AUTH_SOCK" to confirm that the local socket exists.
  2. Check the SSH settings: In ssh -G git-deploy-vps, look for ForwardAgent yes, the correct user and the correct host name.
  3. Check the remote socket: In the VPS session, confirm whether SSH_AUTH_SOCK is set and whether ssh-add -l can reach the agent.
  4. Separate host-key checks: Confirm that the Git service host key is verified in known_hosts on the machine where the Git SSH client runs.
  5. Test the SSH layer: Use ssh -vT to inspect authentication, then test repository permission with git ls-remote.
  6. Inspect Git settings: Make sure core.sshCommand or GIT_SSH is not selecting an unexpected client or configuration file.

If the agent socket is not visible on the server and you cannot change the server policy, ask the system administrator to verify AllowAgentForwarding instead of trying to force the setup. Copying the key during a failed attempt turns an unresolved configuration problem into a permanent private-key risk.

Frequently Asked Questions

Does agent forwarding store the private key on the VPS?

No. In the normal flow, the private key file is not transferred to the VPS. The VPS can still send signing requests through the agent during an open forwarding session, which is why the method is unsuitable for untrusted servers.

Does ForwardAgent yes apply to every server?

It applies to the host scope where you define it. If you place it under Host *, the scope becomes broad. A safer choice is to define it beneath one server alias.

Can a cron job on the VPS use agent forwarding?

Usually not. Cron does not automatically inherit the environment or agent socket from an interactive SSH session. A CLI job started by cron is also a separate process from PHP-FPM web workers; it does not occupy an FPM worker, and it cannot use the forwarded agent unless the socket is explicitly made available. A persistent job should not depend on a local computer’s open session. Design it around a separate machine identity.

Can I run git clone on the VPS with ProxyJump?

No. ProxyJump lets your local SSH client reach the target through the VPS. If you want to run git clone on the VPS, the VPS needs its own authentication method.

Actionable checklist

  • Decide whether the Git operation runs locally or on the VPS.
  • If the VPS is only a gateway, prefer ProxyJump to agent forwarding.
  • If forwarding is required, use a separate, low-privilege SSH key.
  • Define ForwardAgent yes only under one VPS host alias.
  • Verify the local agent, remote SSH_AUTH_SOCK, SSH authentication and git ls-remote in that order.
  • Remove the temporary identity from the agent when the task is complete.
  • For regular deployment automation, plan a repository-scoped machine identity or CI/CD secret model instead of a personal agent.

Your next step is to identify where the current deployment command runs. If it runs on the VPS, use forwarding as a short-lived, narrowly scoped option. If it is regular automation, design key scope and rotation around a separate machine identity.

↑