Simple Deploy
Table of Contents
This is a simple deploy process for static HTML files. There’s a lot of people who have written about this approach. Here’s my take. Incidentally, this is how this blog is deployed. You could also modify the process to deploy projects that use a server, which I’ll talk about that at the end.
The only thing you need is git
. And once you get it set up, you can deploy with a git push
. For things such as a personal website (like this one), it’s perfect. I don’t need to hand out permissions to the deploy process, or run quality control processes. I just want something easy to use, fast, free (as in cost), and unlikely to break or require maintenance.
Spoiler: We do this by setting up a “bare git repository” on our server with a post-receive
hook. It’s a one liner, it looks like this:
#!/bin/sh
GIT_WORK_TREE=~/foo.com git checkout -f
For those familiar with git
you may know exactly what I’m talking about, and be able to take it from here. For those who need step by step instructions, read on.
In the following when I say “laptop” I’m assuming you’re running Mac, Linux, or some other *nix. When I say “server” I mean Linux. This will probably work on Windows using git-bash but no gaurantees, as I haven’t tested.
Ok. Let’s get started.
Make Sure Git is Installed
Open a terminal on your laptop and try running the command git
. If you get “command not found”, install it by following instructions here.
Create a Git Repository For Your Project
If you don’t already have your files in a git repository, it’s easy (and free) to create one. A typical project has source files that you edit, and generated files that you deploy. We want to check both of these into git. If you’re directly editing HTML/CSS/JS, then that’s more like having just the generated side of things - that’s fine, just check in what you have. You can see for example the files I have checked in for the Hugo blog that you’re reading right now.
So go to the directory where your files are, making sure it has everything you would need to work on the project. Then type git init
. If you now type git status
it will tell you that you’re in a new repository, you haven’t added any files yet, and it will list out all the files/folders in the directory and indicate them as “new”. Type git add .
and git commit -m "initial commit"
. Now if you type git status
you should see “nothing to commit, working tree clean”. If not, check for errors in the previous commands. I can’t think of any snags you could run into at this point, so if you do then contact me.
Purchase Web Hosting
You’ll need to look up instructions specific for your webhost. I can’t tell you exactly what you need to do, but typically the process involves:
- Set up an account with some hosting provider
- Purchase a domain (.com’s are usually ~$10/year)
- Set up hosting for the domain (shared hosting is usually ~$10/month for as many sites as you want)
When you set up the hosting in step 3, it’s going to ask you what directory you want to serve the files from. This is a directory on the webhosts server, and will generally be under the home directory of a user account that belongs to you. Note the file path that you are serving the website from. Typically this will be user/domain
, so for me it is ~/bitmage.net
(tilde means “my home directory”). Now if your project directory has a place for generated files, like a public
directory, then you will want to host the files from user/domain/public
. It’s going to be an empty directory for now, but later on you will be configuring this as your destination for files to be deployed to.
Set Up SSH Keys
I’ll tell you how I do this. If you want more warnings about security and the proper way to do things, check here.
- On your laptop open a terminal.
- Check to see if you already have SSH keys by typing
ls .ssh
. If you haveid_rsa.pub
then skip to step 4. - Type
ssh-keygen
and press enter three times. You should be back at your normal prompt, and it should have drawn a box with ascii characters. Note: this is opting for no password on your key files. If your laptop gets hacked, your website gets hacked. Which was probably true anyway. - Type
cat .ssh/id_rsa.pub
and copy the text that’s output.
Ok, now for the server.
- SSH into your remote server. The command you type should look like
ssh user@hostname.com
. All linux based web hosting should support this. Check your admin panel, you might have to create a new user and set a password. - If you’ve already set up your key as trusted, you’ll get in without being prompted for a password, and you can skip to the next section. If you’re asked for your password then enter it and continue to step 3.
- Ok, remember the
id_rsa.pub
that we copied in step 4 on your laptop? We’re going to use that now. Tryls .ssh
to make sure you have a.ssh
directory. If it’s not there, you can use thessh-keygen
to generate the files with the appropriate permissions. - Edit the
.ssh/authorized_keys
file and paste the contents of your clipboard. You can do that with Vim like so:vim .ssh/authorized_keys
. Pressi
for insert mode. Right click your mouse and paste. Pressesc
. Type:wq
(for write & quit) and pressenter
. Sheesh. Why would anybody use Vim? - Type
chmod 600 .ssh/authorized_keys
and pressenter
. - The server should be configured to let us in without a password now. Log out of the server by pressing
ctrl+D
, then press the up arrow to recall your previous command (ssh user@hostname.com
) and pressenter
to run it. Were you logged in without a password?
If it’s not working, the most common culprits for troubleshooting are:
- You missed some characters when pasting your public key file, or you got some additional characters in there. This could include endlines or white space. A proper key entry should start with
ssh-rsa
and end with the hostname that it was generated onuser@computer
. It should have a single endline after that. If there are multiple keys in yourauthorized_hosts
then each one needs to be separated by a single endline. - The permissions on the
.ssh
directory should be700
, and most of the files in there should be600
with the exception of public keys, which should be644
. This is true of both your laptop and the server. If any of the file permissions are wrong, SSH tends to not fail silently, which can be frustrating. - In some rare cases the SSH daemon may be configured to always require a password, or to prefer other authentication schemas. There are other non standard configurations. Typically this won’t be the case unless you yourself set it up that way, or if you’re playing in someone else’s sandbox.
- SSH daemon might not be running on the remote computer. If that were the case you wouldn’t have gotten in in step 1. This is typically only the case if you set up the computer yourself. All standard web hosts are going to have an SSH daemon running.
Create a Bare Git Repository on Your Server
SSH into your webserver. This should be quick and passwordless since you set up your SSH keys above.
Typically on a server, I put all my git repositories in a folder called git
in my home directory. So let’s say you bought the domain foo.com
, you would type mkdir -p git/foo.com
. From now on when I say foo.com
just fill in whatever domain you bought. Next type cd git/foo.com
. Then type git init --bare
. Now this is a magical git command that most people using git have never used before. People pay for github.com repositories for good reason mostly - the access controls, pull requests, review process, and integrations are very nice and productive. But did you know you can set up a server with a single command? I was impressed the first time I did this. And it still impresses me. What a beautiful program. Thank you Linus!
Create a Post-Receive Hook To Deploy Your Files
Now comes the real magic. Git supports “hooks” which let you run a command of your choice whenever an event of your choice is detected. The hook we are interested in today is the post-receive
hook. This gets run whenever the remote server receives new files.
Please edit git/foo.com/hooks/post-receive
. I’ll tell you how to do it in vim, but please use the editor of your choice.
- Type
vim git/foo.com/hooks/post-receive
- press
i
for insert - Copy and paste the following:
#!/bin/sh
GIT_WORK_TREE=~/foo.com git checkout -f
- Note: the
GIT_WORK_TREE
above needs to be set to the same directory that your website is serving files from. We set that up in the first step “Purchase Web Hosting” above. So double check your configuration if you need to. - Press
esc
then:wq
andenter
to save the file and exit.
That’s it for the server setup!
Push Some Changes to Test
Come back to your laptop, in the directory where your project lives. You committed your files earlier, so now:
git remote add deploy user@webhost.com:git/foo.com
git push deploy master
You should see some output indicating that your files are being pushed, and the post-receive hook is being executed. When that’s done, go to foo.com
in your browser and see if your site shows up.
If not… SSH into your server and check the website directory. Did the files get deployed there? Go to the git directory and type git log
. git log --stat
will show you files modified, and git log -p
will show you exact line by line changes. Were your pushes received? See if you can follow the chain of events and find the place where it has broken down.
- You wrote code on your laptop.
- Typed (approximately)
git add
,git commit
,git push
- The server should have received the push, run the
post-receive
, deployed the files to the web directory. - When you visited
foo.com
in your web browser, the website displayed whatever files it found at the configured location
One of the most common mistakes I make is not properly selecting the directory my website needs to serve files from. Make sure it is the public
directory, or where ever your generated files are ending up.
Also if you have a .gitignore
make sure your generated files are not being exempted. If they are, they won’t get transferred! Many people prefer not to check in generated files. If that’s you, you’ll have to get creative and run some additional commands in the post-receive
hook to generate the files server side. But that also means that your build tools will have to be installed on the server. In my preferred process I choose not to do that, because I find it simpler if I only have to install those tools on my laptop. Installing software on shared hosting can be difficult, or forbidden entirely.
Conclusions
Does that seem like a lot? It probably does if these tools are new for you. And I’ve tried to explain the concepts from a beginner’s perspective. From my perspective I use Vim, SSH, and Git every day. This process takes me no more than a half hour from beginning to end. The steps are so familiar to me because I use them in many other workflows as I’m developing, troubleshooting, and configuring software. And I was going to do most of these steps anyway… setting up the webhost, setting up SSH. So it’s really just the git setup that is added for me, and that’s minor. So I think the sweet spot for this process will be if you find synergy with your other tasks. If you’re not using these tools now, maybe getting your website deployed will be motivation to learn them, and then you’ll find yourself using them for many other things. I hope this process and these tools serve you well, as they have for me.
Modifying This Process for a Dynamic Website
You can deploy dynamic websites in this way as well. The one thing that’s going to be different is that in your post-receive
hook, you will have some additional commands to run depending on your programming language of choice. I won’t go through all the options here, but Python, Ruby, Node.js, Go… will all have their own specific commands to reboot a web service.
Will you be able to run a dynamic website on shared hosting? Historically the answer was no, unless the site was written in PHP. Now some hosting providers are supporting other languages, so if yours does your life may be a little bit easier. If not, you’ll need to look at purchasing a Virtual Private Server (VPS) and spending some extra cash. Or look into more modern container based hosting or serverless webhooks. Containers and serverless will probably use a different process from what is described here - but you may find some clever way to combine the two.
If you plan to use shared hosting or a VPS, then this process should work. One piece of advice I will give you is to use your operating system’s (or shared host’s) built in facilities for running a service. A dynamic site requires that your process be kept running on the server all the time. These service facilities will automatically reboot it if it crashes, or if the server restarts. And the commands you will run will differ based on what software you used. On Ubuntu the standard service runner can be restarted with service foo restart
. Or your shared hosting might support passenger, in which case you would call passenger-config restart-app foo
. You will want to add a command that looks like that in your post-receive
, after the files had been updated. But configuring services is beyond the scope of this guide.
If it were up to me, I would typically deploy dynamic sites using a container based hosting, or using a service like Heroku. There are many complications to running stateful software that don’t come up with a static site like we’ve described here. Containers are the state of the art way to do that, and handle scalability and high availability in a way that takes a lot of work to achieve if you’re working with VMs and manually configuring servers. Heroku was using containers before we had a word for containers. So the benefits to their architecture are for most of the same reasons.
That’s it for me. Hope you find it helpful. Best of luck to you! Deploy something cool, so the world can see it!