So you’ve got a local git repository, and you want to deploy it to your own server. Maybe the repo is kept in GitHub, maybe not. Maybe your server is on DigitalOcean, or AWS, or in your basement. It doesn’t much matter.
The goal: when you run git push
, deploy the latest master
branch to the server. Let’s set it up.
How to Deploy a Git Repo
There are lots of ways to do this. On one end of the spectrum is copying files with scp
or rsync
or even FTP. On the other, more modern side, there’s full-blown continuous deployment systems like CircleCI.
This approach sits in the middle. You don’t need to set up or sign up for any services. This is 100% DIY. But it’s using git to do the deploy, so it’s a step above just copying files, because you’re deploying a specific commit (not just whatever files are laying around).
Here’s a rough diagram of what will happen with this setup:
On the server…
- We’ll create a “bare” git repository, to receive the deployed files (
bare_project.git
) - The bare repo will not have our actual project files, which isn’t terribly useful, so…
- A
post-receive
hook in there will check out the files into a “worktree” (a separate folder,deployed_project
in the diagram) - We can customize the
post-receive
hook to do extra stuff too: install packages, run a build, restart a service, etc.
On your computer…
- Add the server as a “remote” to the git repo, using the path to the bare repo on the server
- Just
git push
and off it goes
Step by Step
1. Create the Bare Repo
Create a bare git repo on the server. Doesn’t matter where this goes, as long as you have permission to write to it. Your home directory would work fine. (e.g. /home/you/your_project.git
). The “.git” at the end is not required, but it’s a good reminder that this directory is not a regular project.
ssh you@your-server
git init --bare /path/to/bare_project.git
(Try ls /path/to/bare_project.git
and see what it contains)
2. Create the post-receive Hook
Create the file /path/to/bare_project.git/hooks/post-receive
and add this:
#!/bin/sh
# Check out the files
git --work-tree=/var/www/deployed_project --git-dir=/path/to/bare_project.git checkout -f
This file needs to be executable so use chmod
to make that happen. (don’t skip this step!)
chmod +x /path/to/bare_project.git/hooks/post-receive
3. Configure your local repo to push to the server
We’ll add a “remote” to your local repo. A remote is an endpoint that git can push to. The address can look like a URL (ssh://you@your-server/path/to/files
) or it can be in the SSH format (you@your-server:/path/to/files
)
When I set this up for myself, I put the bare repo in my home directory, so the path was me@my-server:my-project.git
(no absolute path needed, since it’s right in the home directory).
Run this within your own local repo:
git remote add live 'you@your-server:/path/to/bare_project.git'
git push --set-upstream live master
The name “live” can be whatever you want (“prod”, “production”, “deploy”, etc.).
The second command is what binds your master
branch to the live
remote, so when you run git push
, git knows where to push.
(You can verify that the remote was added correctly by running git remote -v
)
Try it Out!
Run git push
inside your local repo. Assuming everything is working right, you should see git push up the files, and it shouldn’t print any errors.
Then, log in to your server and make sure the project files were checked out in the /var/www/deployed_project
location (wherever you put them).
Run Tasks After Deploy with Git
Your project is deployed now. Awesome!
But maybe there’s more you wanted to do, like restart a server, or run npm install
to sync up packages, or some other thing.
The post-receive
script you already have is the perfect place to do this.
Just beware that the “working directory” that the script runs in might not be where you think. Be sure to cd
to the right place first. And the PATH
might not be what it would normally be when you’re logged in, so referring to executables by their full path can be a good idea too.
Here’s an example of running npm install
and restarting a service after each deploy:
#!/bin/sh
# Check out the files
git --work-tree=/var/www/deployed_project --git-dir=/path/to/bare_project.git checkout -f
# Install packages
cd /var/www/deployed_project
npm install
# Restart the web server
# (requires sudoers to allow this command without a password)
sudo /bin/systemctl restart my-project.service
If the npm command fails, login and run which npm
, then use that full path in the command, like /usr/local/bin/npm install
.
Run sudo commands from a post-receive hook
If the sudo
command fails with an error about “no interactive terminal” or some such, that’s because it’s trying to ask for a password. That won’t work. But you can add a rule to the sudoers file that will let you run just this one command without a password.
Log in to your server over SSH and run sudo visudo
. On my Ubuntu 18.04 system, I have a line that looks like:
%sudo ALL=(ALL:ALL) ALL
That allows everyone in the sudo
group to use sudo
with a password. Add another line below it to make an exception for this command:
%sudo ALL=(ALL:ALL) ALL
%sudo ALL=(ALL:ALL) NOPASSWD: /bin/systemctl restart my-project.service
The command there must exactly match the one in your post-receive
script. Save, quit, and give it another shot.