Skip to content
Tutorials

Using Git over SSH on your server

Pull private repositories onto a Cloud Server with a read-only deploy key, deploy safely, and keep .git out of your web root.

Hannah ClarkeAug 10, 2026 · 4 min read

A git pull on the server is the simplest deployment there is, and for many small projects it is all you need. Done carelessly, though, it can expose your whole repository history to the web or leave a personal access token in a config file. This guide sets up Git on a Cloud Server the safe way.

Install Git

Ubuntu images do not always include it:

apt update && apt install -y gitgit --version

Create a deploy user

Do not deploy as root. A dedicated user limits what a compromised app or a bad hook can touch:

adduser --disabled-password --gecos "" deploymkdir -p /var/www/app && chown deploy:deploy /var/www/app

Authenticate with a deploy key

For a private repository, use a deploy key: an SSH key that belongs to the server and grants access to one repository only, read-only by default. Never use your personal password or your own SSH key on a server.

Generate it as the deploy user:

sudo -u deploy ssh-keygen -t ed25519 -C "[email protected]" -f /home/deploy/.ssh/id_ed25519 -N ""cat /home/deploy/.ssh/id_ed25519.pub

Add that public key to the repository's deploy keys in your Git host (GitHub, GitLab, Bitbucket and Gitea all support them). Leave write access off unless the server genuinely needs to push.

Test the connection:

sudo -u deploy ssh -T [email protected]

The first time, confirm the host fingerprint. GitHub replies with a greeting naming the repository the key belongs to.

Tip: an empty passphrase (-N "") is normal for a deploy key, because nobody is there to type it. That is exactly why it should be read-only and limited to a single repository: if the server is ever compromised, the key cannot rewrite your code.

Several repositories, several keys

Most Git hosts only allow a deploy key on one repository. For a second repository, generate a second key and map each to a host alias in /home/deploy/.ssh/config:

Host github-api    HostName github.com    User git    IdentityFile ~/.ssh/id_ed25519_api Host github-web    HostName github.com    User git    IdentityFile ~/.ssh/id_ed25519_web

Then clone with git@github-api:example/api.git.

Clone outside the web root

The common mistake is cloning straight into the directory your web server serves. That puts .git inside the web root, and on many configurations it is downloadable: your entire history, including any credentials that were ever committed, even if they were later removed.

Clone the repository so that only its public folder is served:

sudo -u deploy git clone [email protected]:example/app.git /var/www/app
server {    server_name example.com;    root /var/www/app/public;   # serve the public folder, not the repo root}

If you cannot avoid serving the repository root, block .git explicitly:

location ~ /\.git {    deny all;    return 404;}

Check from outside that it is not reachable:

curl -s -o /dev/null -w '%{http_code}\n' https://example.com/.git/config

You want 404 or 403, never 200.

Deploy with a pull

sudo -u deploy git -C /var/www/app pull --ff-only

--ff-only refuses to create a merge commit on the server if someone edited files there by hand, so you notice drift instead of silently merging it. A tiny script makes deploys repeatable:

#!/usr/bin/env bashset -euo pipefailcd /var/www/appgit pull --ff-onlycomposer install --no-dev --optimize-autoloader   # or npm ci && npm run buildsudo systemctl reload php8.1-fpm

Keep secrets out of the repository

  • Never put a token in a remote URL. git remote -v prints it, and it lands in your shell history.
  • Keep environment files such as .env out of Git with .gitignore, and create them on the server by hand.
  • If a secret was ever committed, rotate it. Removing it from the latest commit does not remove it from history.

Next steps

Hannah ClarkeHannah runs Infraista's network and edge from London, from routing and DDoS filtering to the odd 3 a.m. traceroute.

Try it yourself

Cloud Servers start at $3.25/mo, billed by the hour.