Deploying to your server with git push
If you’re using git for your project’s version control, it might interest you that you can deploy your latest changes to the server where you’re hosting your application by using just git on your local computer. I set this up for the site you’re reading, so I figured I’d put up a write-up of how I did it.
The heart of the matter are git hooks. Hooks are optional scripts that git can run for you in response of certain events happening. The main event we’re after, on the server end, is called
post-receive. It is triggered after a git push.
For this guide, I assume the following:
- You have git installed on your local computer, and the server you deploy to
- Your server is running a Linux distribution, or at least has a bash shell.
- You have some experience using a (bash) terminal
- You are able to connect to your server via SSH, from the machine you’re working on
Setting up the repository on the server
SSH into your server. I recommend doing so with a normal user, rather than
$ ssh youruser@yourserver
Navigate to where you want to store your git repository, and create a directory. For convention’s sake, I like adding
.git as part of the folder name, as this will be more familiar later on.
$ mkdir myproject.git $ cd myproject.git
Next, set up a “bare” git repository in this folder. It needs to be bare to be able to push to it normally.
A bare repository does not include the working directory; instead, it contains essentially all you’d find in the
.git folder of your local version.
Please note that this step is still valid if you already have an existing repository you’d like to deploy with.
$ git init --bare
We do want to get a working directory containing all the repository’s files as well, so the next step is cloning the repository. We’re still on the server, and have not changed directories.
$ git clone . files
You likely recognise the
git clone command. Here we’re using
. to refer to the location of the repository we’re cloning. We’re in the very folder we are cloning from, so
. refers to it.
files is the name of the folder we’re cloning into. Git will now warn you that you are cloning an empty repository; this is expected, as we’ve just initialized a bare one without adding anything to it.
Setting up hooks
$ cd hooks $ vim post-receive
vim with the editor of your choice. In any case, ensure the file
post-receive contains the following content:
#!/bin/bash GIT_WORK_TREE=/home/youruser/myproject.git/files GIT_DIR=/home/youruser/myproject.git/files/.git git pull origin master exit
Replace the paths with the full path to the directory you’re in. These set git’s environment variables to the exact things they need to be, instead of relying on it being right in the context of the script being run. (Note: this may possibly be improved upon, but I’ve yet to experiment with doing this more simply. At present though, this setup works.)
After setting the environment variables,
git pull origin master is run. In this case,
origin is the bare repository we’ve set up,
Save the file. If you ended up using vi or vim and need some quick relevant help:
Upon opening vi or vim, it’s in “normal mode” and won’t let you type. Press
i to enter insert mode, then type the contents.
Once you’re done typing up the file, press the escape key to leave insert mode. Now press
: to open up a little command prompt, followed by
q, and lastly the return/enter key. These are two commands, “write” and “quit”, so it’ll save the file and exit the editor.
Since this file will be executed by git, we need to ensure it’s executable.
$ chmod +x post-receive
Now we need to set up a second hook, this time in the working copy (
files). This one is run whenever a
post-merge event happens, a result of
$ cd files $ vim .git/hooks/post-merge $ chmod +x .git/hooks/post-merge
The contents of
post-merge can be anything you like. I like having it to check for a
deploy.sh file that’s part of my repository, so I can commit a deploy script along with the repository itself.
#!/bin/bash if [ -x "deploy.sh" ]; then echo "Running deploy script." ./deploy.sh else echo "deploy.sh was not found or is not executable, not doing anything." fi
This checks for the existence of a
deploy.sh file in the working directory (here
files/), while checking if it’s executable as well. For more info on available tests you can do in bash, check the manual on test in bash.
If the file exists and can be executed, it executes it; otherwise, it prints a message indicating the issues.
Save and quit, and don’t forget the
You’re now mostly done on the server side.
Setting up a new remote on your local machine
Navigate to your local git repository and add a new remote with git:
$ cd path/to/my/repository $ git remote add production youruser@yourserver:/home/youruser/myproject.git
production is the name for the new remote git repository. Normally, when you’ve cloned a repository, it only has the automatically added
production is the new one being added.
youruser is the user you log in as when you SSH to your server,
yourserver is the host name or IP address of your server. After the colon you specify the full absolute path to the
myproject.git directory you set up on the server.
After running this, your
.git/config file should contain the following, among other things:
[remote "production"] url = youruser@yourserver:/home/youruser/myproject.git fetch = +refs/heads/*:refs/remotes/production/*
From this point, you’re essentially ready to go. You can now push to your server as follows:
$ git push production master
You will then be prompted for the same login details like you would when SSH-ing into your server. This could be a password, or a password to access your private/public key pair.
On the first such push after initializing the empty bare repository on the server, the
post-merge script will not be run, because nothing actually gets merged yet. If you need to run the deploy script this time, you can either run it manually on the server, or create a new commit which you then push.
Include a deploy script in your repository
If you’re using my version of the
post-merge script, you’ll want to add a file called
deploy.sh in your git repository’s main directory.
This is another bash script in which you can write anything you like. For example, this website uses Hugo as a static site generator. So, upon pushing the markdown files, I want to run the
hugo command to generate the HTML forming the webpage you’re reading right now. My deploy.sh file looks as follows:
#!/bin/bash # This script is run by a git hook on my server when pushed to. echo "Wiping public/ folder..." rm -r public echo "Updating static site with hugo..." hugo echo "Done."
Don’t forget to make this file executable:
$ chmod +x deploy.sh
Don’t forget to point your web server to the appropriate new directory after setting this up.
Anyway, that’s it it. Please let me know on Mastodon or in the comments if you’ve got any questions or improvements.