How to deploy your Jekyll site on Scalingo
Tags: jekyll, staticwebapps, tutorial, ruby
Published: 2022-11-04 18:48:46


[Jekyll](https://jekyllrb.com/) is a static site generator based on Ruby. [Scalingo](https://scalingo.com/) is a hosting provider (similar to Heroku, Platform-as-a-Service). One of the great things about Scalingo is that they are an European company with a data center in France.

At [Ravioli](https://getravioli.de), we're moving all our apps to EU-based hosting providers to ensure the privacy of customers. This is necessary because the USA has very lax privacy laws, and it's easy for the US government to access customer data that is residing on data centers owned or operated by US legal entities. So, even if you host your app on AWS's Frankfurt server, you compromise your users' privacy.

If you're still reading this after my privacy detour, thanks! Let's get to the juicy part.

Scalingo doesn't offer a native way to serve static applications such as Jekyll or Hugo. Rather, [they recommend](https://doc.scalingo.com/platform/app/static-files-hosting) that you run an Express server that serves the static assets.

Sounds easy enough. Why do we need a wordy blog post for this?

Because there are two gotchas which make deploying your Jekyll site not that straight-forward. The good news is that I have the solution right here for you.

Like Heroku, Scalingo works with buildpacks. Buildpacks tell Scalingo which environment to install before deploying and running your app. We are basically running an Express app that just serves the static files generated by Jekyll. Therefore, we need to choose the `nodejs` buildpack.

When you do that, you'll quickly realize that your build fails. The reason is simple: The `nodejs` build pack doesn't include Ruby, but before deploying we need Ruby to generate the static files for our website. The Jekyll command for that is `bundle exec jekyll build`.

Of course, we could run the command before and check in the static build output (usually the `_site` folder) to our version control. But this would be a major pain in the ass.

We need a buildpack that somehow contains Node and Ruby. It's our lucky day! Scalingo offers [Multi Buildpacks](https://doc.scalingo.com/platform/deployment/buildpacks/multi). To combine multiple build packs, add a file called `.buildpacks` to your root with this content:

```bash
https://github.com/Scalingo/ruby-buildpack
https://github.com/Scalingo/nodejs-buildpack.git
```

The order is important. The `nodejs` buildpack needs to come last, because we're deploying a Node application.

Our `package.json` should look like this:

```js
{
  "scripts": {
    "start": "node server.js",
    "build": "bundle exec jekyll build"
  },
  "dependencies": {
    "express": "^4.18.2"
  }
}
```

The `nodejs` buildpack runs the `build` task before deploying. That's where we build our Jekyll static files. And then it uses the `start` task to run the Express server.

Speaking of Express, our second gotcha comes to mind. In [their documentation](https://doc.scalingo.com/platform/app/static-files-hosting), Scalingo suggests this setup for `server.js`:

```js
var express = require('express');

var app = express();
var directory = '/' + (process.env.STATIC_DIR || 'dist')
app.use(express.static(__dirname + directory));

var port = process.env.PORT || 3000;
app.listen(port, function () {
  console.log('Listening on', port);
});

```

With Jekyll, this works ok for the index page, but not for any other page. That's because it expects the `.html` extension. So, `/about` won't work, but `/about.html` will. But that's a bit too 1995 for me. Fortunately, a simple modification to `server.js` fixes this problem:

```js
app.use(express.static(__dirname + directory), { extensions:['html'] });
```

That's it. Using an Express server and leveraging Scalingo's Multi Buildpacks, you should be able to deploy any static website on Scalingo.

This static website that you're reading is generated with Hugo and ironically still runs on Netlify. I'll make sure to migrate it over to Scalingo one of these days.