Uploading files by FTP is slow and error-prone. A Git deploy puts each release on the server straight from version control, so you always know what is live and can roll back. This guide shows two reliable ways to deploy a website to a VPS with Git over SSH.
Before you start
- A Linux VPS with Git installed (
apt install gitordnf install git) and a deploy user, for exampledeploy. - Your site in a Git repository, locally and/or on GitHub, GitLab or Bitbucket.
- SSH key login to the server working.
Option 1: pull from GitHub with a deploy key
Best when your code already lives on a Git host.
- On the server, as the deploy user, create a key for the server itself:
ssh-keygen -t ed25519 -f ~/.ssh/deploy_key -N "" - Add the contents of
~/.ssh/deploy_key.pubto the repository as a read-only Deploy key (GitHub: Settings → Deploy keys → Add deploy key). - Tell SSH to use it, in
~/.ssh/config:Host github.com IdentityFile ~/.ssh/deploy_key IdentitiesOnly yes - Clone into the web root:
git clone git@github.com:you/site.git /var/www/site - Deploy each update with:
cd /var/www/site && git pull --ff-only
A deploy key gives access to one repository only, which is safer than putting your personal key on the server.
Option 2: push to deploy with a bare repository
Here the server hosts its own repository and deploys whenever you push.
- Create a bare repository on the server:
git init --bare /home/deploy/site.git - Create the hook
/home/deploy/site.git/hooks/post-receive:#!/bin/bash TARGET=/var/www/site GIT_DIR=/home/deploy/site.git BRANCH=main while read oldrev newrev ref; do if [ "$ref" = "refs/heads/$BRANCH" ]; then git --work-tree="$TARGET" --git-dir="$GIT_DIR" checkout -f "$BRANCH" echo "Deployed $BRANCH to $TARGET" fi done - Make it executable and make sure the deploy user owns the target:
chmod +x /home/deploy/site.git/hooks/post-receive && mkdir -p /var/www/site && chown deploy: /var/www/site - On your computer, add the server as a remote and push:
git remote add production deploy@203.0.113.10:/home/deploy/site.gitgit push production main
Add build steps to the hook as needed, for example composer install --no-dev, npm ci && npm run build, or systemctl restart myapp (allowed through a narrow sudo rule).
Keep secrets and uploads out of Git
- Store
.envand config files with passwords only on the server and list them in.gitignore. - Keep user uploads (for example
wp-content/uploads) outside the deployed tree or ignored, socheckout -fnever touches them. - Block web access to
.gitif the repository sits in the web root: in Nginx,location ~ /\.git { deny all; }.
Roll back a bad release
With option 1, check out the previous commit:
git log --oneline -5 && git checkout <commit>With option 2, revert locally and push again (git revert HEAD && git push production main), so history stays accurate.
Troubleshooting
- Permission denied (publickey) on clone: the deploy key is not added to the repository, or SSH is not using it. Test with
ssh -T git@github.com. - Hook does not run: it must be executable and named exactly
post-receivewith no extension. - git pull refuses: local changes: someone edited files on the server. Commit them in the repo instead, or discard with
git checkout -- .after checking.
Git deployment FAQ
Is git pull on the server safe?
Yes for simple sites, as long as .git is not web-accessible and secrets are not committed. For zero-downtime releases use a release-folder tool or CI/CD.
Can I use Git deployment on cPanel hosting?
Yes, cPanel → Files → Git Version Control can clone repositories and deploy with a .cpanel.yml file.
Do I need CI/CD?
Not to start. Add a pipeline once you want automatic tests and builds before each deploy.
