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.

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 -vprints it, and it lands in your shell history. - Keep environment files such as
.envout 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
- Set up your own SSH access first: SSH keys.
- Running bigger pipelines? Run your own CI runners on Cloud Servers.
- Deploy containers instead with Managed Kubernetes.
Try it yourself
Cloud Servers start at $3.25/mo, billed by the hour.