I don’t like doing something by hand if it can be automated, so I set up my Jekyll and static websites to be automatically deployed with a
The Jekyll deployment instructions were used as a starting point.
Deployment Environment Details
If you use the default Digital Ocean Docker droplets, make sure you configure a firewall. The default installations are wide open on the public internet with no firewall. Since Docker likes opening up a tonne of ports for “convenience”, do your homework to tighten things up unless you like being hacked.
The website configuration and document root directories are Docker volumes because they need to persist across container lifespans.
The PostgreSQL data directory is also a Docker volume, because it needs to persist across container lifespans.
LetsEncrypt is installed on the Docker host, and the SSL certificate and key directories are mounted as a Docker volume for containers that need access to them.
Docker Compose is used to describe the environment and dependencies between containers, so that all services can be started in dependency order with a
docker-compose up -d command, and shut down with a
docker-compose down command.
Automated deployment of Jekyll sites
The concept is that a Git remote is added to the Git repository containing the Jekyll site or existing static HTML content.
This is done on any machine that will be triggering deployments.
Deployment is a
git push to this remote, which triggers the actual work via a Git repository hook on the receiving side.
Setting up the source repository
A remote was added to the Git repository on my laptop, called
deploy, referencing the receiving repository on the Digital Ocean
$ git remote add deploy deploy@<SERVER>:~/<SITE>.git
Setting up the receiving repository
As a best practice, a low privilege user named
deploy was created on
<SERVER>, given write access only to the temporary directories and HTML document root directories.
Password login was disabled for this user, only SSH authorized key logins permitted.
The receiving repository was then created :
$ ssh deploy@<SERVER> $ mkdir <SITE>.git $ cd <SITE>.git $ git --bare init $ rm -f hooks/*.sample
hooks/post-receive was added to this repository:
$ touch hooks/post-receive $ chmod +x hooks/post-receive
This script gets called after a push to this repository.
-e option to your hash-bang command-line in the script is recommended, so that the script exits if any command returns a non-zero exit code.
trap to ensure cleanup happens no matter how the script terminates is also recommended. For example,
trap cleanup EXIT will call a shell function named
cleanup whenever the script exits, however it exits.
The script does the following:
- Cleans up any temporary directories that may have been left over
<SITE>.gitinto a temporary directory
jekyll buildto produce the static HTML content
- Moves the existing static HTML content for the site to a backup
<SITE>/previousdirectory, for quick roll-backs.
- Moves the newly generated static HTML content to a
- Ensures some well-known directories I depend on in the Nginx configuration are in place
- Ensures a
<SITE>/rootsymlink points to
- Cleans up all temporary directories that were created
Nginx is configured to serve up content from
<SITE>/root, whatever that symlink points to.
Performing a deploy
From a machine with the source repository, execute
git push deploy master. That’s it.
Triggering a deploy if the script failed
If there were bugs in the
post-receive script, running it manually to finish the deploy is simple:
$ ssh deploy@<SITE> <SITE>.git/hooks/post-receive
Automated deployment of static content sites
This is identical to Jekyll sites in every way, with the exception that Jekyll is not called by the
post-receive script, since the static content is already available.
All the script has to do is move it into place.