Subscribe now

Deploying Middleman [07.18.2017]

Deploying Middleman apps is really easy, and today we are going to look at how to do deploy to both GitHub Pages and S3.

GitHub Pages

Middleman is a great solution when you need a simple site for an open source project, and GitHub Pages is the perfect place to host that kind of site. The first thing we are going to want to do is add the mgd gem. That's short for Middleman GitHub Deploy -- not 'Miller Time'. Luckily you'll be having a beer in no time because this is fast.

First lets add mgd to our Gemfile and run bundle, then mgd:

# Gemfile
gem 'mgd', '~> 0.2.0'

Next we go to our repository's site. The default URL is: so for our Rudiment project on our DailyDrip github account, it's

We can see our page is up, but there is a tiny problem: our styles aren't loading. Our links are relative, and pointing to /stylesheets/style.css which resolves as Notice how we are missing rudiment/. The file is hosted properly at

To overcome this, we need to set our site_url and an http_prefix.

# config.rb

# Work around for GitHub Pages namespace
# First we set the local site url to be blank
set :site_url, ""
configure :build do
  # second we set an http prefix for our build
  set :http_prefix, '/rudiment'

Updating our deployed site is simple, we can do that just by committing and pushing our changes then running mgd. Our latest changes will be pulled from master, built, and pushed to GitHub Pages. It is a bit strange that mgd clones instead of using your local copy, but as long as you are aware of the behavior it is fine.

Custom Domains With GitHub Pages

To setup our custom domain, the first thing we need to do is tell GitHub about the domain. We'll need to create a CNAME file in the source folder containing the string

# source/CNAME

Next we'll setup a CNAME at our DNS provider pointing to I'm a big fan of DNSimple, so that's what I'm using here, but this is different for every domain registrar or DNS provider, so you'll need to lookup their instructions to do it. Should be simple.

You can check your CNAME with dig like this: dig After it propagates, you'll see the CNAME pointing at GitHub.

We deploy these changes and now when we go to we can see our built page hosted on GitHub Pages. Of course the first thing you'll notice is again our CSS isn't loading. This is because it's name spaced, and we'll just need to remove that config section we added earlier, and re-deploy.

# config.rb

# REMOVE all the below stuff

# Work around for GitHub Pages namespace
# First we set the local site url to be blank
# set :site_url, ""
# configure :build do
#   # second we set an http prefix for our build
#   set :http_prefix, '/rudiment'
# end

Again, we can update the page just by committing and pushing our changes then running mgd. Our changes are all deployed and on our custom domain now!

Amazon S3

Deploying to AWS's S3 is also really simple. Setup is very similar to the GitHub Pages process. We add some gems, bundle, add some configuration items, setup our bucket and sync. First lets add middleman-s3_sync and mime-types to our Gemfile and run bundle. We need to explicitly add mime-types because of a bug in the fog gem.

# Gemfile
gem 'middleman-s3_sync', '~> 4.0.3'
gem 'mime-types', '~> 3.1'

Next up we'll set some options in our config, these are taken from the middleman-s3_sync readme. I modified the aws_access_key_id and aws_secret_access_key to use environment variables that I store in .ruby-env. You can manage environment variables however you feel is easiest, I just like RVM as it's built in there.

Different choices here won't affect the build or deploy process at all -- just be sure not to commit those strings to a repository. That can be an exspensive mistake in a public repo as coin miners troll them looking for AWS credentials to steal.

# config.rb

# S3 Sync Details
activate :s3_sync do |s3_sync|
  s3_sync.bucket                     = '' # The name of the S3 bucket you are targeting. This is globally unique.
  s3_sync.region                     = 'us-west-2'     # The AWS region for your bucket.
  s3_sync.aws_access_key_id          = ENV['AWS_SECRET_ACCESS_KEY']
  s3_sync.aws_secret_access_key      = ENV['AWS_ACCESS_KEY_ID']
  s3_sync.delete                     = false # We delete stray files by default.
  s3_sync.after_build                = false # We do not chain after the build step by default.
  s3_sync.prefer_gzip                = true
  s3_sync.path_style                 = true
  s3_sync.reduced_redundancy_storage = false
  s3_sync.acl                        = 'public-read'
  s3_sync.encryption                 = false
  s3_sync.prefix                     = ''
  s3_sync.version_bucket             = false
  s3_sync.index_document             = 'index.html'
  s3_sync.error_document             = '404.html'

I've created a bucket named It's important to name your bucket the same thing as the domain name you'll be pointing at it. Additionally I granted public read privileges during the creation process. You don't need to worry about setting up website hosting and default index or error documents. The S3 Sync process will do all of that for us! Next up we want to run the S3 Sync.

middleman s3_sync

Now we just visit our bucket endpoint to see our live site! In our case it is:

I also created a CNAME pointing to so now the same site is live on


Deploying a Middleman site is really easy, and today we looked at how to do that in two environments: GitHub Pages and AWS's S3. After deploying both we setup custom domains pointing at each. We also looked at how to generate a Middleman site when it's being hosted at some where other than a root directory. I really hope you're enjoying Middleman, it's a great project and we'll dive deeper into it soon. See you next time!