I never want to see exciting tech in production. As the guy who gets
woken up at 4am when it breaks, I’d much rather have boring.
For years, I’ve been optimizing for boring. I try not to invent new
standards, usually there’s already something good enough out there. Why
write a new library, style guide, protocol or data schema? Worst case,
I’d rather copy as much of the spirit of an old standby into a new realm
than start from scratch.
In that spirit, I’ve long been putting up snippets up as Github gists
with titles like, “The Most Boring nginx.conf”. The spirit of the
projects has always been the same: I want the simplest template of
reasonable defaults for a thing, and I may as well make it public.
Now at first glance it might sound like I’d put up the most minimal
config possible, because what’s more boring than nothing? Sadly, this is
rarely the case. Instead, I actually include all the default value for
important parameters in as defaults. “Why!?” I’ve been asked by friends
and coworkers who’ve suffered through my needlessly long configs. Two
reasons: explicitness & reference. First, I often see folks putting
default values in as if they needed to be specified. This leads to cargo
cult configs (I truly hope you’ve never experienced old sendmail
configs, which consist entirely of remembrances of use cases lost),
which will eventually cascade into a Revolution in which Everything Must
Be Rewritten!
Second is as reference to default values. Sometimes defaults change.
Sometimes, expectations are made of these default values. And while they
may be the default, ultimately you are responsible for software being
configured this way. You are specifying this behaviour, and so I
encourage being explicit.
Of course, if you look at my configs, you’ll occasionally note that I
have more than a single “default” left in comments. Usually these are
Debian or Red Hat packager settings that vary from the vendor defaults.
I list them because many people accidentally mistake them for official
defaults. And why wouldn’t you? If you install with a system package,
it’s settings truly are a default.
So I’ve discussed the two values I always consider in my configs: vendor
and packager defaults. But often that is not enough.
Consider a common usage of Nginx: that of a front-end proxy for web
applications. For that we want TLS settings, vhost settings, and
potentially many features that aren’t on by default. For this we need to
have an understanding of the browser landscape, HTTP semantics
(especially about caching), and expectations about the app to be
proxied.
Worst of all, we have the atrocious default values for TLS that ship
with almost anything built on OpenSSL. You see, OpenSSL rarely updates
their recommended ciphers, and even if they did they likely wouldn’t
match browser best practices. For that sort of thing, it’s important to
refer to expert third parties.
Finally, some things just aren’t clear. Sometimes performance tuning is
off by default, even when it is appropriate in most cases. Sometimes
there are bugs outstanding that require workarounds. For this sort of
thing, we can only confidently refer to the source.
Which is to say, in making the most boring configs, I comb through the
vendor docs, common packager settings, expert guidance, and the source
to figure out what exactly the best general value should be.
Then I actually combine it into a config. Of course, the file should
start with a description and some links to the official doc for the
settings in question. I always stick a comment saying that the file is
config managed and shouldn’t be changed by hand. Because I do so hope we
aren’t savages here.
Then I go through all the settings, set into logical group (often
alphabetical inside those groups) and every single value has a comment
with a justification. It might be “vendor default” or “debian default”,
or it might be enabling a straightforward feature. But anything which
might be mistaken for an interesting value should include a link to an
explanatory reference or ticket.
You might wonder why I’m not doing this in chef, or puppet, or ansible
or any of the many tools I use every day. Would templates make this
clearer? The problem is that to do so would make some folks feel
isolated. These are reference texts, and exist as a place outside config
management modules where we can share our common ends, even if we don’t
share means. And ultimately, all this stuff should just be in the
vendor’s defaults, right?
I’ve described the why and the how, so if you’re still interested in
what these actually are, you can find them at:
http://github.com/josephholsten/boring