Deploy with Git

Terje Rutgersen · 2026 · Guide

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.

The setup

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.

Pushing to it

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.

If things don't update

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.

Why not use GitHub Actions

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.