There's an entire industry built around deploying websites. CI/CD pipelines, build servers, deployment platforms, GitHub Actions, Vercel, Netlify, Render. Some of them are genuinely useful. Some of them exist to sell you a solution to a problem you could solve with a shell script and a git hook.
For a static site, the deployment problem is: "I changed a file locally, and I want it to appear on my server." That's it. Git already knows how to move files between machines. You just need to tell it to put them in the right place when they arrive.
You need git on your server (you probably already have it):
sudo apt update
sudo apt install git
Create a bare repository. This is the thing that receives your pushes:
sudo mkdir -p /var/repo/website.git
cd /var/repo/website.git
sudo git init --bare
Now the magic part. Create a post-receive hook. This is a script that runs every time the repo receives a push:
nano /var/repo/website.git/hooks/post-receive
Put this in it:
#!/bin/bash
GIT_WORK_TREE=/var/www/html git checkout -f main
Make it executable:
chmod +x /var/repo/website.git/hooks/post-receive
That's the entire deployment pipeline. Two lines of bash. When you push to this
repo, git checks out the latest version of main into your web root.
Nginx serves it. Website updated.
On your local machine, add the server as a remote:
git remote add production ssh://your-username@your-server-ip:/var/repo/website.git
Then whenever you want to deploy:
git push production main
Done. No build step. No waiting for a pipeline. No "your free tier has run out of build minutes." Just your files, on your server, updated the moment you push.
It's almost always permissions. Make sure the web directory is owned by the right user:
sudo chown -R www-data:www-data /var/www/html
And make sure the hook is actually executable. I've forgotten this step more times than I'd like to admit.
You can. This site actually uses GitHub Actions now for convenience. But the post-receive hook version has zero dependencies on any third-party service. No GitHub outage, no Actions quota, no YAML files that take longer to debug than the actual code. For a simple static site, a shell script is plenty. Sometimes the simplest tool is the right one.