Website infrastructure for over-engineers

Name Last Update
dist Loading commit data...
flow-typed/npm Loading commit data...
mocha Loading commit data...
public Loading commit data...
scripts Loading commit data...
src Loading commit data...
vendor Loading commit data...
.babelrc Loading commit data...
.eslintignore Loading commit data...
.eslintrc Loading commit data...
.flowconfig Loading commit data...
.gitignore Loading commit data...
.tmux Loading commit data...
.watchmanconfig Loading commit data...
.yarnrc Loading commit data...
LICENSE.md Loading commit data...
README.md Loading commit data...
gulpfile.babel.js Loading commit data...
package.json Loading commit data...
postcss.config.js Loading commit data...
schema.graphql Loading commit data...
schema.json Loading commit data...
webpack.config.js Loading commit data...
webpack.production.config.js Loading commit data...
yarn.lock Loading commit data...


This repo contains the source code and content for my website at wincent.com.

Content is authored in plain-text-friendly markup formats like Markdown and served using a dynamic stack (described below). New code can be deployed and content added or updated via git push.


  • React: Declarative, component-oriented view layer.
  • Relay: Declarative data-management.
  • GraphQL: Hierarchical data querying language, type system and server.
  • Git: Main content storage.
  • Redis: Indexing and caching.
  • memcached: Ephemeral caching.

Supporting tools and technologies:

  • Markdown: Preferred content markup.
  • Vim: Content editing.
  • Marked 2: Local content previewing.


Why not use a static site generator?

A static site generator would very much be the right tool for this job, however, building the site on a custom React/Relay/GraphQL stack was much more fun, so I did that instead.

Why the name "Masochist"?

Please see the introductory blog post, "Introducing Masochist".



Note: In the following, npm can be substituted for yarn if desired.

Webpack-based hot-loading workflow

git clone https://github.com/wincent/masochist.git
cd masochist
yarn # Or `npm install`.
yarn run update-schema
yarn run update-indices # Whenever content changes.
yarn run start

Running in production-like environment

yarn run build # builds files under `dist/`
yarn run start-prod

Running in production

export NODE_ENV=production
yarn # Or `npm install`.
gulp build
node dist/bin/updateIndices.js # Whenever content changes.
node dist/server/main.js


In __DEV__, Masochist will look for content in the current repo (ie. ".").

In production, it expects to find a content repo at "/var/masochist/content".

In both cases, you can override this with yarn config set. For example, in my local development environment, I have the Masochist Git repo checked out in one folder, and a second copy of it with the "content" branch checked out alongside it at "../masochist-pages" (see below for more details on this set-up). I can override the __DEV__ default of "." with:

yarn config set masochist:content-repo '../masochist-pages'

Deployment cheatsheet

You could do this in any number of ways but the way I'm doing it is using two local repositories as follows:

Local "masochist" repository

  • master branch checked out.
  • origin remote pointing at git.wincent.com.
  • github remote pointing at GitHub.
  • masochist remote set up to do Heroku-style deploy-on-push, pointing at an Amazon EC2 instance configured using Ansible.
$ git push masochist master # Deploy app (after initial provisioning).
$ git push masochist # Subsequent deployments.
$ git push origin # Propagate code, but no deploy.
$ git push # Shorthand for `git push origin`.
$ git push github # If you can't be bothered waiting for it to auto-replicate.

Local "masochist-pages" repository

  • content branch checked out.
  • content remote configured to do Heroku-style push-to-publish.
  • origin remote pointing at git.wincent.com.
  • github remote pointing at GitHub.
$ git push content content # First push after initial provisioning.
$ git push content # Subsequent pushes.
$ git config branch.content.merge refs/heads/content # For laziness.
$ git push # Simple.
Rollback to a prior rev $HASH
$ git push masochist +$HASH:master

Or just switch symlinks and sudo monit restart masochist.

Force a deploy without actual code changes

$ git commit -m Deploy --allow-empty
$ git push masochist