After the rise of cloud hosting with their multiple environments and slick UIs it’s easy to forget why we started using them in the first place… Git. What if I told you can put Git on any old server? That you can still host sites yourself and deploy code to production with a simple
git push? Yes, you can have (host) your cake (git) and eat (deploy) it too.
1. Create the user
A requirement of this method is that the Git user and the Unix user who owns the web accessible directory are the same account. With this structure in place we can work our magic.
We will connect to this user when we push changes up from our local. Create this user like you would any other user on a Unix machine:
sudo adduser <username>
ssh groups to this user:
usermod -a -G ssh <username>; usermod -a -G sudo <username>;
2. Give remote user your public key
SSH to the remote server as the
<username> user. In their home directory:
mkdir ~/.ssh && chmod 700 ~/.ssh; touch ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys;
On your local machine copy the contents of your public key which is usually found at:
Back on the server paste your public key into authorized_keys:
3. Create a bare Git repo
A bare git repo just means that only the “.git” folder exists in the repo’s folder, not the tracked files. The bare git repo becomes the central “host” for the files. This host will then export the tracked files to our web accessible destination directory.
Create a bare repo in your remote user’s home directory:
mkdir .git; cd .git; git init --bare;
Note: Though you’ve created this repo in
~/ it will not try to track the contents of your home directory.
4. Push to the bare repo
Add the bare repo “host” as the remote on your local repo:
git remote add origin <username>@<server>:/home/<username>/.git
In the example above
<server> is either the server’s IP address or a domain that points to it.
5. On push export to destination directory
On the remote server add a
#!/bin/bash while read old new ref do if [[ $ref =~ .*/master$ ]]; then echo "Deploying master..." git --work-tree=/home/<username> --git-dir=$PWD checkout -f else echo "$ref received but not deployed." fi done
This script will export Git’s working tree (the tracked files) to
<username> with your destination directory. In my case my repo contains a
docroot folder which will be web accessible via Apache, so I have no issue exporting the tracked files directly into my user’s home directory.
Note: As mentioned at the start of the tutorial, the destination directory has to be owned by the Git user in order for Git to have permission to create files and folders. This is why we’re exporting within the user’s home directory.
Save file with
Control/Command + O and exit nano with
Control/Command + X then make the script executable:
chmod +x ~/.git/hooks/post-receive
Save the file and we’re almost done!
Making a deployment
To test things out simply add a dummy commit to your local repo, then
git push. Now when the base repo receives the push it exports the latest tracked files to the
/home/<username> destination directory. Visit your website’s URL to see the changes!
File System permissions
We’re only human and you’ve likely uploaded files and folders already that are owned by the wrong users and groups. The
post-receive hook will only be able to modify files and folders under the control of
<username>. So if you
git push and something’s not updating then chances are that file/folder is owned by another user.
Find incorrectly owned directories with:
Then change the owner and groups of folders and their children with:
chown -R <user>:<group> <directory-path>
This is my method. If you have any improvements please let me know.