How to deploy your website from Git
Adding SSH keys, connecting a remote and choosing pull-based or push-to-deploy — what a Git deployment setup needs on hosting.
Deploying from Git means the server pulls, or receives, the exact commit you tested rather than a folder of files copied by hand — which removes an entire category of deployment mistake where a file gets missed, overwritten, or uploaded out of order. Setting it up takes a bit more up-front work than uploading over SFTP, but the result is a deploy you can repeat exactly, and roll back exactly, every single time.
Start with SSH access
Git deployment depends on SSH, both for connecting to the server and, if the repository lives on a service like GitHub or GitLab, for that service authenticating back to wherever the code is fetched from. If you have not set this up yet, setting up SSH keys covers generating a key pair with ssh-keygen and adding the public half where it needs to go.
Two ways to structure the deployment
| Approach | How it works | Best for |
|---|---|---|
| Pull-based | You connect to the server yourself, over SSH, and run git pull inside the project folder. | Small teams, manual releases, simplicity. |
| Push-to-deploy | The server holds a bare Git repository with a post-receive hook; pushing to it from your machine triggers a checkout automatically. | Teams wanting git push to be the entire deploy step. |
Setting up a pull-based deployment
This is the simpler of the two and a reasonable starting point. Connect over SSH and clone the repository into a folder outside your public web root:
cd ~/apps
git clone git@github.com:yourname/yourproject.git website
Then point your domain's document root at the correct subfolder inside the checkout — for a plain PHP project that might be the checkout itself, and for a framework like Laravel it is specifically the public folder inside it, as covered in deploying a Laravel application.
To deploy an update from then on:
cd ~/apps/website
git pull origin main
composer install --no-dev
php artisan migrate --force # if applicable
A short shell script that runs git pull, the dependency install, any migrations and any cache-clearing commands in the right order removes the risk of forgetting a step under pressure. Keep the script itself in the repository so it is versioned along with everything else.
Setting up push-to-deploy
This method uses a bare repository on the server plus a hook that runs on every push. First, create the bare repository:
mkdir -p ~/repo/website.git
cd ~/repo/website.git
git init --bare
Then add a post-receive hook that checks the pushed code out into your live folder:
#!/bin/sh
GIT_WORK_TREE=/home/youraccount/public_html git checkout -f main
Save that as ~/repo/website.git/hooks/post-receive and make it executable:
chmod +x ~/repo/website.git/hooks/post-receive
From your own machine, add the server as a remote and push to it:
git remote add production ssh://youraccount@yourserver/~/repo/website.git
git push production main
Every push to that remote now checks the latest commit out directly into public_html. This is a genuinely satisfying setup once it works, but it also means a broken commit goes live the instant it is pushed — pair it with testing on a staging environment before you push to production, not as a replacement for it.
A bare repository's hidden data, and the working copy's own .git folder in a pull-based setup, must not sit inside a folder a browser can request files from. A .git folder left reachable by URL can expose your entire commit history, including anything accidentally committed to it in the past — credentials, old configuration, anything. Keep the repository and the deployed working copy in separate locations, or add a server-level rule blocking direct requests to any path containing .git.
Handling secrets and per-environment config
Never commit real credentials to the repository, even a private one. Keep .env or an equivalent configuration file out of version control — list it in .gitignore — and create it once directly on the server instead, so a deploy never overwrites production settings with whatever happened to be in a developer's local copy. See setting environment variables for the options available on hosting without root access.
Rolling back a bad deploy
The whole benefit of deploying from Git is that a rollback is also just a Git command. For a pull-based setup:
git log --oneline -5
git checkout <previous-commit-hash>
Confirm the site is stable on the previous commit, then investigate what went wrong with the one you rolled back from at your own pace, rather than under pressure with the site down. If a deploy also ran a database migration, check the migration is safe to leave in place before you roll code back — rolling back the application while a schema change stays applied is a common source of a rollback that looks successful but is not.
Related reading
Generate a key pair, add the public half to your hosting account, and connect over SSH without a password from then on.
How to deploy your website with rsyncThe rsync flags that matter, a dry run before the real one, and how to stop it overwriting files it should have left alone.
How to set up a staging environmentA subdomain, its own database, and a safe way to copy data across — what a staging environment needs before you can trust it.
How to call an external API from PHPcURL, an authentication header and a decoded JSON response — the three parts of calling any external API from a PHP script.