Self-hosting your git and your app on the same box has a nice property: a push can be the deploy. You need a bare repository and one hook.
The bare repo#
git init --bare /srv/git/myapp.git
Add it as a remote on your laptop and push a branch you deploy from, for example main.
The post-receive hook#
/srv/git/myapp.git/hooks/post-receive, made executable:
#!/bin/bash
set -e
APP=/var/www/myapp
git --work-tree=$APP --git-dir=/srv/git/myapp.git checkout -f main
cd $APP
composer install --no-dev --optimize-autoloader
php artisan migrate --force
php artisan config:cache
php artisan route:cache
php artisan view:cache
checkout -f syncs the working tree to the pushed commit. The artisan steps are the boring, repeatable part of every Laravel release.
Keep it honest#
- Run the hook as a user that owns
/var/www/myappbut cannot sudo. migrate --forceruns unattended, so only push migrations you have tested against a copy of production data.- If the hook fails, the push still lands in the repo; redeploy by pushing an empty commit or rerunning the hook on the server.
This is one step above “SSH in and pull”. When you outgrow it — zero-downtime releases, rollbacks, multiple servers — that is the point where a real pipeline earns its keep, not before.
Share this post
Keep reading
Backing Up Your Git Server So It Actually Restores
Every clone is a partial backup - but hooks, config and server state live outside the repo. Mirror, bundle, copy off-site, and rehearse the restore.
Conventional Commits for a Small Team
A prefix on every commit message - feat, fix, chore - makes the log scannable and the changelog half-written. Keep the rules short or the team routes around them.
Keeping a Changelog From Git Tags
Tags mark what shipped; the log between them writes the changelog. A small habit that pays off every release.