<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom">
  <title type="text">Recent Blog Posts</title>
  <id>http://bretthoerner.com/feed.atom</id>
  <updated>2011-06-24T00:00:00Z</updated>
  <link href="http://bretthoerner.com/" />
  
  <subtitle type="text">Recent blog posts</subtitle>
  <generator>Werkzeug</generator>
  <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/brett-hoerner" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="brett-hoerner" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry xml:base="http://bretthoerner.com/feed.atom">
    <title type="text">Django at Scale slides</title>
    <id>http://bretthoerner.com/2011/6/24/django-at-scale</id>
    <updated>2011-06-24T00:00:00Z</updated>
    <link href="http://bretthoerner.com/2011/6/24/django-at-scale" />
    <author>
      <name>Brett Hoerner</name>
    </author>
    <content type="html">&lt;p&gt;Last night I gave a talk on scale at &lt;a class="reference external" href="http://twitter.com/awpug"&gt;AWPUG&lt;/a&gt;. I'll admit I had no real idea what I was going to talk about until about two days before, so... your milage with these slides may vary. Most things worth discussing in a group are applicable to more than yourself and by definition reusable. Thankfully Disqus has released a lot of code related to problems we've had a scale, so I pointed out a lot of those in my presentation. Aside from apps you should investigate there are a few quick do's and don'ts, but nothing earth shattering.&lt;/p&gt;
&lt;p&gt;I included my notes in the slides. If you click through to SlideShare you can download a plain ol' PDF.&lt;/p&gt;
&lt;center&gt; &lt;div style="width:425px" id="__ss_8413788"&gt; &lt;strong style="display:block;margin:12px 0 4px"&gt;&lt;a href="http://www.slideshare.net/bretthoerner/django-at-scale" title="Django at Scale"&gt;Django at Scale&lt;/a&gt;&lt;/strong&gt; &lt;iframe src="http://www.slideshare.net/slideshow/embed_code/8413788" width="425" height="355" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"&gt;&lt;/iframe&gt; &lt;div style="padding:5px 0 12px"&gt; View more &lt;a href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/bretthoerner"&gt;bretthoerner&lt;/a&gt; &lt;/div&gt; &lt;/div&gt; &lt;/center&gt;</content>
  </entry>
  <entry xml:base="http://bretthoerner.com/feed.atom">
    <title type="text">Austin</title>
    <id>http://bretthoerner.com/2011/5/30/austin</id>
    <updated>2011-05-30T00:00:00Z</updated>
    <link href="http://bretthoerner.com/2011/5/30/austin" />
    <author>
      <name>Brett Hoerner</name>
    </author>
    <content type="html">&lt;p&gt;Austin is a relatively liberal (or libertarian, depending who you ask) city in the heart of Texas, you might think of it as &amp;quot;everything you've heard about Texas, but the opposite.&amp;quot; It may be the capitol, but it's the third smallest city of Texas' big six (Houston, Dallas, San Antonio, Austin, Fort Worth, El Paso). It's an affluent city with a relatively low cost of living and is within 1% the per capita degrees of Seattle and San Francisco. If you've never been here you might think of it like Texas' Portland or Boulder.&lt;/p&gt;
&lt;a class="reference external image-reference" href="http://www.flickr.com/photos/eschipul/419796506/"&gt;&lt;img alt="http://farm1.static.flickr.com/124/419796506_db7f02aedd.jpg" src="http://farm1.static.flickr.com/124/419796506_db7f02aedd.jpg" /&gt;&lt;/a&gt;
&lt;div class="section" id="indoors"&gt;
&lt;h2&gt;Indoors&lt;/h2&gt;
&lt;p&gt;Downtown Austin &lt;a class="reference external" href="http://www.google.com/search?q=austin+museum&amp;amp;ie=utf-8&amp;amp;oe=utf-8"&gt;full of museums&lt;/a&gt;. Tastes tend to differ too much here, I'll let you decide which sound interesting.&lt;/p&gt;
&lt;p&gt;You should check out &lt;a class="reference external" href="http://www.waterloorecords.com/"&gt;Waterloo Records&lt;/a&gt; and &lt;a class="reference external" href="http://www.bookpeople.com/"&gt;Book People&lt;/a&gt; which are right next to each other west of downtown. They're also both by the original (and flagship) Whole Foods, if you're into that kind of thing.&lt;/p&gt;
&lt;p&gt;The state capitol and University of Texas campus (38,000 undergras and 12,000 postgrads) are both interesting to see (and easy to find), but are a mix of indoor/outdoor.&lt;/p&gt;
&lt;p&gt;The &lt;a class="reference external" href="http://drafthouse.com/"&gt;Alamo Drafthouse&lt;/a&gt; is an awesome local chain of theaters that serve food and alcohol to you while you watch. Not super unique these days, right? Well, check out their &lt;a class="reference external" href="http://drafthouse.com/events/austin"&gt;signature event&lt;/a&gt; list, first. They do quote-a-longs, sing-a-longs, shoot-a-longs (get a cap gun, help shoot bad guys), dance-a-longs, and more. Most events are adult only so you aren't surrounded by small children.&lt;/p&gt;
&lt;a class="reference external image-reference" href="http://www.flickr.com/photos/epyonmx/1424450316/"&gt;&lt;img alt="http://farm2.static.flickr.com/1064/1424450316_d78fb74dc4.jpg" src="http://farm2.static.flickr.com/1064/1424450316_d78fb74dc4.jpg" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div class="section" id="outdoors"&gt;
&lt;h2&gt;Outdoors&lt;/h2&gt;
&lt;p&gt;Texas is hot as shit in the summer. This shouldn't be news to you. There's a reason many outdoor activities revolve around water.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.ci.austin.tx.us/zilker/zilkermap.htm"&gt;Zilker Park&lt;/a&gt;: A huge park just Southwest of Downtown, lots of various activies and events depending on time of year.&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://en.wikipedia.org/wiki/Barton_Springs_Pool"&gt;Barton Springs Pool&lt;/a&gt;: Located in Zilker Park, fed by spring water.&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://en.wikipedia.org/wiki/Mount_Bonnell"&gt;Mount Bonnell&lt;/a&gt;: A short drive from downtown brings you up to a great view of downtown and the hill country.&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.zilkerboats.com/"&gt;Kayaking on Lady Bird&lt;/a&gt;: There are many locations along the river that borders downtown where you can rent a kayak (or paddle boat, etc) to play with on the relatively still water.&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.tubesanmarcos.com/"&gt;Tube San Marcos&lt;/a&gt;: This one is a bit of a drive, but you basically float a slow river (preferablly with your cooler floating next to you).&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://en.wikipedia.org/wiki/Mexican_Free-tailed_Bat#Migration"&gt;See the Congress Bridge bats&lt;/a&gt;: In Summer a colony of bats live under the Congress Bridge (just South of downtown). People lay on the hill under the bridge and wait for them to fly out sundown.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Austin is located in Texas' Hill Country. There are many popular hiking and bike trails, but I'm not your man for information on either. A quick search should turn up lots of results.&lt;/p&gt;
&lt;a class="reference external image-reference" href="http://www.flickr.com/photos/kevharb/5080022076/"&gt;&lt;img alt="http://farm5.static.flickr.com/4043/5080022076_1aca7c0327.jpg" src="http://farm5.static.flickr.com/4043/5080022076_1aca7c0327.jpg" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div class="section" id="food"&gt;
&lt;h2&gt;Food&lt;/h2&gt;
&lt;p&gt;Austinites &lt;a class="reference external" href="http://www.grist.org/article/food-what-us-citydwellers-really-spend-on-food-and-drink"&gt;spend the most money&lt;/a&gt; dining out per capita. We're bound to have some good eats.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.yelp.com/biz/uchi-austin"&gt;Uchi&lt;/a&gt;: Some of the best Japanese / Sushi in the nation. Really. ($$$)&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.yelp.com/biz/home-slice-pizza-austin"&gt;Home Slice&lt;/a&gt;: The best NYC style pizza I've ever had (I've been to NYC four times, for what it's worth).&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.yelp.com/biz/franklin-barbecue-austin"&gt;Franklin Barbecue&lt;/a&gt;: It's Texas. This is the place (inside Austin) to go.&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.yelp.com/biz/frank-austin"&gt;Frank&lt;/a&gt;: Hot dogs, cold beer.&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.yelp.com/biz/moonshine-patio-bar-and-grill-austin"&gt;Moonshine&lt;/a&gt;: Your typical &amp;quot;new twist&amp;quot; on American food, very good though. ($$)&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.yelp.com/biz/magnolia-cafe-austin-2"&gt;Magnolia Cafe&lt;/a&gt;: Local 24 hour diner with multiple locations. As seen on TV.&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.yelp.com/biz/curras-grill-austin"&gt;Curra's Grill&lt;/a&gt;: Interior Mexican food, get some pork.&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.yelp.com/biz/bouldin-creek-coffee-house-and-cafe-austin"&gt;Bouldin Creek&lt;/a&gt;: Vegan / Vegetarian cafe, all day breakfast.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are many, many more. Yelp is a pretty good resource here. One thing I've mostly left out is Austin's huge trailer food &amp;quot;culture.&amp;quot; Hit South Congress or East 6th for whole &amp;quot;trailer parks&amp;quot; full of amazing food. No joke.&lt;/p&gt;
&lt;p&gt;We also have tons of great coffee shops, I could name a few but I don't drink the stuff.&lt;/p&gt;
&lt;a class="reference external image-reference" href="http://twitter.com/#!/beaktor/status/71988170186694656"&gt;&lt;img alt="http://media.bretthoerner.com/2011/05/franklin.jpg" src="http://media.bretthoerner.com/2011/05/franklin.jpg" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div class="section" id="music-and-nightlife"&gt;
&lt;h2&gt;Music and Nightlife&lt;/h2&gt;
&lt;p&gt;Austin is the self proclaimed &amp;quot;&lt;a class="reference external" href="http://www.google.com/search?q=live+music+capital+of+the+world&amp;amp;ie=utf-8&amp;amp;oe=utf-8"&gt;live music capital of the world&lt;/a&gt;&amp;quot;. Did you roll your eyes? It's cool. But if we're far from it, I wonder why nobody else has taken the crown? If someone else is really #1, and we're #2 or #3 in the middle of Texas with a population of 800,000, so be it.&lt;/p&gt;
&lt;p&gt;The point stands, though: we have lots of great live music.&lt;/p&gt;
&lt;p&gt;Venues and shows change far too often to list here, check out &lt;a class="reference external" href="http://showlistaustin.com/"&gt;Show List Austin&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As for non-music nightlife, you have to see &lt;a class="reference external" href="http://en.wikipedia.org/wiki/6th_Street_%28Austin%29"&gt;6th Street&lt;/a&gt; on a weekend night, preferably 10PM-2AM. It's one of the most densly packed areas of bars you'll ever find. The streets around 6th also have bars and are often better for actually lounging in with friends.&lt;/p&gt;
&lt;p&gt;East 6th (&amp;quot;East&amp;quot; in Austin terms almost always means &amp;quot;cross under I-35&amp;quot;) is also a fun place to go find a bar. It's much more quiet and much more hipster than downtown. You have to go to &lt;a class="reference external" href="http://www.yelp.com/biz/rio-rita-lounge-austin"&gt;Rio Rita&lt;/a&gt; and have some habenero infused vodka.&lt;/p&gt;
&lt;a class="reference external image-reference" href="http://www.flickr.com/photos/nashcook/5769133419/"&gt;&lt;img alt="http://farm3.static.flickr.com/2739/5769133419_d82ef6b0b1.jpg" src="http://farm3.static.flickr.com/2739/5769133419_d82ef6b0b1.jpg" /&gt;&lt;/a&gt;
&lt;/div&gt;
</content>
  </entry>
  <entry xml:base="http://bretthoerner.com/feed.atom">
    <title type="text">Redis at Disqus</title>
    <id>http://bretthoerner.com/2011/2/21/redis-at-disqus</id>
    <updated>2011-02-21T00:00:00Z</updated>
    <link href="http://bretthoerner.com/2011/2/21/redis-at-disqus" />
    <author>
      <name>Brett Hoerner</name>
    </author>
    <content type="html">&lt;p&gt;We've been running Redis in production for a bit over three months now on projects ranging from critical (paying premium users) to experimental (in-house). Just as &lt;a class="reference external" href="http://devblog.bu.mp/how-we-use-redis-at-bump"&gt;Will at Bump&lt;/a&gt; was inspired by someone else, I was inspired by Will to do a write up on what we're up to.&lt;/p&gt;
&lt;div class="section" id="analytics"&gt;
&lt;h2&gt;Analytics&lt;/h2&gt;
&lt;p&gt;Our entire &lt;a class="reference external" href="http://disqus.com/admin/addons/packages/"&gt;premium analytics service&lt;/a&gt; runs on Redis. These stats are, as you'd expect, time-based. In order to squeeze this problem into a key/value store, we just append date-string suffixes to the keys: &amp;quot;&lt;cite&gt;comments_by_day:disqus:disqus:2011-01-01&lt;/cite&gt;&amp;quot;. If we split on &amp;quot;&lt;cite&gt;:&lt;/cite&gt;&amp;quot; we have 4 pieces: the stat name, the account, the site and the date. (I'll go into the purpose of the account piece shortly.) Having the stats split by day means it's easy to calculate totals (or averages, unions, etc.) over arbitrary user-defined time periods. For example, a user could select the time range &amp;quot;Jan 1, 2011 - Feb 4, 2011&amp;quot;, and we can give them accurate stats simply by summing the results of a &lt;a class="reference external" href="http://redis.io/commands/mget"&gt;mget&lt;/a&gt; of each day in the range.&lt;/p&gt;
&lt;img alt="http://media.bretthoerner.com/2011/02/disqus-analytics.png" src="http://media.bretthoerner.com/2011/02/disqus-analytics.png" /&gt;
&lt;div class="section" id="why-redis"&gt;
&lt;h3&gt;Why Redis?&lt;/h3&gt;
&lt;p&gt;Now, that's all nice and easy, but it's not much of a selling point for Redis over any other key/value store (or even a good RDBMS). The real advantage Redis offers is powerful support for data structures and the operations you'd expect to use on them. The big selling point for us was the availability of sets (sorted and not). We need to track sets of unique users that do certain activities in certain time periods. An example of this would be the set of (unique) users who commented in a single day. Can you store sets pretty easily in other key/value stores? &lt;a class="reference external" href="http://dustin.github.com/2011/02/17/memcached-set.html"&gt;Sure you can.&lt;/a&gt; The difference for us is that our sets can easily contain tens of thousands of elements each. The benefit of a store like Redis is that sets are &amp;quot;first class&amp;quot;, and you can ask the server to do operations like unions for you. Imagine if a user were to request stats for an arbitrary 17-day period: We would just do a &lt;a class="reference external" href="http://redis.io/commands/zunionstore"&gt;zunionstore&lt;/a&gt; of all 17-day sets and get the cardinality of that new set, which is the total number of unique users who posted in that time period. If Redis didn't support sets or set operations, we'd have to fetch all 17-day sets ourselves then calculate it locally. The network I/O alone would kill performance.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="sharding"&gt;
&lt;h3&gt;Sharding&lt;/h3&gt;
&lt;p&gt;So what was the &amp;quot;account&amp;quot; bit in the key all about? Sharding is one of the pains of planning ahead when using Redis. Since we use (and depend on) things like set operations, we can't just arbitrarily split our data across different nodes. What if Jan 1 was on Node1 and Jan 2 on Node2? Doing a set union when the data isn't in memory takes us right back to doing network I/O of entire sets, killing performance. The first obvious step is to shard on sites (so my blog and Disqus' blog might be on different Redis nodes). No harm there, an easy win. But what if we're talking about Big Newspaper Inc., who have one Disqus account but 20 distinct Disqus sites? We could easily provide them stats per site with the current sharding scheme, but that's no fun. We wanted to offer &amp;quot;network-wide&amp;quot; stats. That is, the ability to see things like total comments, upvotes, replies, and even unique users across &lt;em&gt;all&lt;/em&gt; of their sites at once, and for any arbitrary time period. That means all of a user's sites need to be hosted on the same Redis node. This is actually pretty trivial once you decide to do it (we just take the modulo of the owning user's ID against the number of nodes we have to decide which node to read/write from/to), and the bit in the key isn't strictly necessary, but we like to have it in there just incase we need to reason about them later.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="sessions"&gt;
&lt;h2&gt;Sessions&lt;/h2&gt;
&lt;p&gt;In short, we attempted to migrate our sessions from PostgreSQL to Redis but failed. Redis started as an in-memory database but added a virtual memory backend in (if I recall correctly) the 2.0 branch. While the VM backend helped, we found that it still wouldn't stay within the bounds we set, and would continually grow no matter what we set. We did &lt;a class="reference external" href="http://code.google.com/p/redis/issues/detail?id=394"&gt;report the issue&lt;/a&gt; but never came to a good solution in time. For example, we could give Redis an entire 12GB server and set the VM to 4GB, and given enough time (under high load, mind you) it would climb well above 12GB and start to swap, more or less killing our site. Since we didn't have a need for the powerful Redis datastructures here, we eventually decided to go with Membase because its main purpose is a simple disk-backed key/value store.&lt;/p&gt;
&lt;p&gt;We look forward for Redis 2.4's &amp;quot;diskstore&amp;quot; which aims to solve the issues the VM backend has had.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="aggregators"&gt;
&lt;h2&gt;Aggregators&lt;/h2&gt;
&lt;p&gt;We have many other counters and sets for all sites and users in Disqus network, outside of the complex and rich premium analytics data. There's nothing too fancy here, but again Redis has been our choice (often migrating away from PostgreSQL) when we need a high write capacity. As most (all?) large sites eventually find, our master database can be a bottleneck, and using SQL to increment rows that are already essentially key/value stores can be a waste. When (and only when!) it's more natural, we move denormalized counters and other data to Redis.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="experimentation"&gt;
&lt;h2&gt;Experimentation&lt;/h2&gt;
&lt;p&gt;Lately we've been pushing the limits further with some in-house (maybe soon to be user-facing) projects that do all of the above at much larger and faster rates. I've been using Redis as storage for output from Hadoop jobs and we've been pushing data to other nodes to collect statistics on reads. If you imagine the full network size of Disqus being rather large in writes, you'd have to see a few services crumble when you try to do a write-per-request. ;)&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="conclusion"&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;All in all, I'd say we're big fans. Aside from the VM issues (which should only affect a very small percentage of users, and should be resolved in 2.4) it's been a pleasure to administrate even for our very small team. I wouldn't call us technically conservative by any means, but we have a decent amount of PostgreSQL expertise in house and we don't throw in new moving parts for no reason. When it's the right tool for the job, it's great. Huge props to Salvatore Sanfilippo, Pieter Noordhuis and others.&lt;/p&gt;
&lt;/div&gt;
</content>
  </entry>
  <entry xml:base="http://bretthoerner.com/feed.atom">
    <title type="text">Two months using Android</title>
    <id>http://bretthoerner.com/2010/6/29/two-months-using-android</id>
    <updated>2010-06-29T00:00:00Z</updated>
    <link href="http://bretthoerner.com/2010/6/29/two-months-using-android" />
    <author>
      <name>Brett Hoerner</name>
    </author>
    <content type="html">&lt;p&gt;I've had the HTC Incredible for about two months now.  It's been a great experience overall.  Verizon's network has been very good (especially compared to AT&amp;amp;T) in Austin. I even had a week to use it in San Francisco and didn't experience any problems (a first!).&lt;/p&gt;
&lt;p&gt;From what I can tell, the only iPhone 4G hardware advantages that I care about are the polished aluminum build and the display. The 3GS has 163 ppi and the 4G has 326, that's clearly a huge and much needed boost.  The Incredible has 252 ppi, so it's not exactly like I'm living with the weak 3GS display, but more is always better (when a resolution independent UI is used, as both systems do).&lt;/p&gt;
&lt;p&gt;Software-wise I'm still sold on Android.  Even when I was using iOS I hardly used any apps.  A browser, a twitter client, a music player, a password manager, and a maps application cover 99% of my use cases.  I'm curious what else people are actually using (aside from games, which I don't care about).  Just like the desktop, the web is where it's at for me.&lt;/p&gt;
&lt;p&gt;Given the apps I use, I find the Android OS vastly superior.  In my &lt;a class="reference external" href="http://bretthoerner.com/blog/2010/may/12/htc-incredible/"&gt;previous entry&lt;/a&gt; I explained that I prefer Android's pinch to zoom in the browser, and that definitely still stands.  I've also learned that applications can extend the &amp;quot;Share Page&amp;quot; button, so I have an app (&amp;quot;plugin&amp;quot;?) to quickly and easily mark things in &lt;a class="reference external" href="http://readitlaterlist.com/"&gt;Read It Later&lt;/a&gt;.  The fast and available network doesn't hurt my web experience, either.&lt;/p&gt;
&lt;p&gt;My music and Twitter apps are basically the same between platforms.  I don't really ask much of them and I'm not sure what they could do to really stand out for me.&lt;/p&gt;
&lt;p&gt;Maps on Android, as I said before, is amazing.  It was a huge selling point among friends that are using iPhones but up for a new phone soon.  Sadly it sounds like they didn't update the maps app in iOS4 (via same disappointed friends).&lt;/p&gt;
&lt;p&gt;Finally, I'm still loving the Android notification system and exposed filesystem (mostly for Dropbox syncing files to use in other apps).&lt;/p&gt;
</content>
  </entry>
  <entry xml:base="http://bretthoerner.com/feed.atom">
    <title type="text">On Emacs</title>
    <id>http://bretthoerner.com/2010/6/11/on-emacs</id>
    <updated>2010-06-11T00:00:00Z</updated>
    <link href="http://bretthoerner.com/2010/6/11/on-emacs" />
    <author>
      <name>Brett Hoerner</name>
    </author>
    <content type="html">&lt;p&gt;Or &amp;quot;that other OS.&amp;quot;&lt;/p&gt;
&lt;p&gt;When I was visiting the DISQUS office a few weeks ago my coworkers and I made it a point to tease each other about our awful tool choices (especially editors).  It's also come up among some other friends recently, so I think now is as good a time as any to try and explain why I use Emacs (and think you should, too).&lt;/p&gt;
&lt;p&gt;A little history: I've been using Emacs now for about a year and a half.  Before that I used TextMate for about 2 years, and before that it was Vim for about 2 years.  I should note that when I say &amp;quot;used&amp;quot; I mean as my day-to-day, minute-to-minute editor.  I still make use of all 3 pretty regularly.&lt;/p&gt;
&lt;p&gt;The most important aspect of Emacs is that it can (often easily) do pretty much whatever you want (or need) it to do.  Some people take this to extremes, with browsers and more inside of Emacs.  I won't defend that here because I believe in the right tool for the job.  What does matter is that you can often recreate any other editor feature within Emacs, and more often than not someone has already done it for you.&lt;/p&gt;
&lt;p&gt;Where people often go &amp;quot;wrong&amp;quot; is that they assume customization should be done after you master the basics.  That's the first step toward the anti-Emacs club.  I think the tutorial should go into &lt;cite&gt;init.el&lt;/cite&gt; and basic customization as soon as opening and saving are out of the way.  If you use Emacs without customizing it, you're doin' it wrong.  If you do something that pains you two days in a row, you're doin' it wrong.&lt;/p&gt;
&lt;p&gt;Here's an example: Emacs teaches you commands like C-n and C-p to move between lines.  If you don't like that you are free (no, encouraged!) to not use those and bind different keys (in this case the arrow keys are enabled by default).  The same goes for saving, copying, pasting, closing a buffer, etc.  If you really can't stand the key combinations then &lt;em&gt;change them&lt;/em&gt;!  I've set my Emacs up with a very OS X muscle-memory friendly configuration, &lt;cite&gt;Command-S,C,X,Z,F,G,W,Q&lt;/cite&gt; all do what you'd expect.  This isn't a sin against Emacs, this &lt;em&gt;is&lt;/em&gt; Emacs.&lt;/p&gt;
&lt;p&gt;Once you've given yourself a pretty basic TextMate like editor that you can at least use, you start the slow day-over-day growth of your own config (and thus your own editor).  You can see &lt;a class="reference external" href="http://github.com/bretthoerner/dotfiles-emacs"&gt;my config&lt;/a&gt; for some examples.  When you miss something from another editor, add it.  When you see something new in another editor, you can probably find it and add it.  When something (anything) annoys the hell out of you, change it.&lt;/p&gt;
&lt;p&gt;Can you do all of this in Vim?  Sure, kinda.  I just couldn't get very far with Vimscript before banging my head against a desk.  I feel like the Emacs &amp;quot;add-on&amp;quot; (they really aren't add-ons) community is larger, faster, and more engrained in the culture.  Elisp &lt;em&gt;is&lt;/em&gt; the editor.&lt;/p&gt;
&lt;p&gt;I'd like to point out that I'm pretty bad with Elisp, you don't need to become an expert either.  Elisp is also a pretty bad Lisp in my opinion, but it's hard to point out another popular editor written entirely with customizability in mind in a language that's usable.&lt;/p&gt;
&lt;p&gt;If you're a programmer I think you'll enjoy being able to program your own tools.  And what do you use more than your text editor?  You're missing out.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: Since this was mostly about the ideology of Emacs I hope and plan to do a little screencast comparing my current setup to another editor (probably TextMate) to explain what I've done with my own setup and the crazy features Emacs has that nothing else seems to.&lt;/em&gt;&lt;/p&gt;
</content>
  </entry>
  <entry xml:base="http://bretthoerner.com/feed.atom">
    <title type="text">HTC Incredible</title>
    <id>http://bretthoerner.com/2010/5/12/htc-incredible</id>
    <updated>2010-05-12T00:00:00Z</updated>
    <link href="http://bretthoerner.com/2010/5/12/htc-incredible" />
    <author>
      <name>Brett Hoerner</name>
    </author>
    <content type="html">&lt;p&gt;I recently made the switch from an iPhone 3G to the HTC Incredible (on Verizon).  The following is simply a list of what I like and don't like, in no specific order.&lt;/p&gt;
&lt;div class="section" id="the-bad"&gt;
&lt;h2&gt;The Bad&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;dl class="first docutils"&gt;
&lt;dt&gt;&lt;strong&gt;Screenshots&lt;/strong&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first last"&gt;They aren't built in to Android yet.  You have to plug in via USB and use dev tools.  I didn't do this often, and haven't actually needed it yet.  This is scheduled for an upcoming release.&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;li&gt;&lt;dl class="first docutils"&gt;
&lt;dt&gt;&lt;strong&gt;Bookmarklets&lt;/strong&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first last"&gt;They don't seem to work yet, also scheduled for an upcoming release.&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;li&gt;&lt;dl class="first docutils"&gt;
&lt;dt&gt;&lt;strong&gt;Updates&lt;/strong&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first last"&gt;I'm worried about the speed at which HTC will update my version of Android OS.  With the iPhone you're guaranteed everyone will be updated on day 1 of a new release.  Only time will tell, but it's been a sore point of Android in the past.&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;li&gt;&lt;dl class="first docutils"&gt;
&lt;dt&gt;&lt;strong&gt;Podcasts&lt;/strong&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first last"&gt;One of the few things I think iTunes does right is Podcast syncing.  I haven't found an alternative that's on par, though I haven't looked hard.  This is less of a pain since I moved because I'm always at home.  Mobile Podcasts are best for a commute.&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="the-neutral"&gt;
&lt;h2&gt;The Neutral&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;dl class="first docutils"&gt;
&lt;dt&gt;&lt;strong&gt;Flash&lt;/strong&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first last"&gt;Flash Lite came installed, I was able to watch embedded YouTube clips.  I disabled it.  &amp;quot;Real&amp;quot; Flash 10 is coming to Android, I highly doubt I'll care, but it's a choice I wouldn't have otherwise.&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;li&gt;&lt;dl class="first docutils"&gt;
&lt;dt&gt;&lt;strong&gt;Cost&lt;/strong&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first last"&gt;$200 for the phone, and I believe my service plan costs are exactly the same as the iPhone on AT&amp;amp;T.&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;li&gt;&lt;dl class="first docutils"&gt;
&lt;dt&gt;&lt;strong&gt;Big name apps&lt;/strong&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first last"&gt;Which is to say, the ones I care about.  They're all there.  Google (everything), Bank of America, Amazon, Pandora, Dropbox, Evernote, Meebo, PayPal, Twitter, Yelp.&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;li&gt;&lt;dl class="first docutils"&gt;
&lt;dt&gt;&lt;strong&gt;Multitasking&lt;/strong&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first last"&gt;Coming to the iPhone, so I'll call it even.  Really, really great for Pandora, IM, etc.&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;li&gt;&lt;dl class="first docutils"&gt;
&lt;dt&gt;&lt;strong&gt;Multitouch&lt;/strong&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first last"&gt;Yeah, it has it.  It may be the first Android phone that &lt;a class="reference external" href="http://www.youtube.com/watch?v=HWPsM2EdXPw"&gt;gets it right&lt;/a&gt;.  Again, it's on iPhone so we'll call it even.&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="the-good"&gt;
&lt;h2&gt;The Good&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;dl class="first docutils"&gt;
&lt;dt&gt;&lt;strong&gt;Verizon&lt;/strong&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first last"&gt;My calls don't drop.  3G is faster and with more, better coverage.  Fuckin' A.&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;li&gt;&lt;dl class="first docutils"&gt;
&lt;dt&gt;&lt;strong&gt;Fast&lt;/strong&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first last"&gt;Network aside, the hardware is fast as hell.  1Ghz processor, 512MB RAM (that's 2x the 3GS, I assume the 4G will bump up to this).&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;li&gt;&lt;dl class="first docutils"&gt;
&lt;dt&gt;&lt;strong&gt;Open Platform&lt;/strong&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first last"&gt;No &lt;a class="reference external" href="http://daringfireball.net/2010/04/why_apple_changed_section_331"&gt;section 3.3.1&lt;/a&gt;. I develop because I &lt;em&gt;enjoy&lt;/em&gt; it, and I have no interest someone telling me what editor I'm allowed to use.
Another benefit is not being forced into one choice for much of anything.  For example, you can &lt;a class="reference external" href="http://www.youtube.com/watch?v=mRUoWUhcRlE"&gt;swap out the default keyboard&lt;/a&gt; and many other pieces.  The idea of competing keyboard software is just awesome to me.&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;li&gt;&lt;dl class="first docutils"&gt;
&lt;dt&gt;&lt;strong&gt;Syncing&lt;/strong&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first last"&gt;Built-in over-the-wire syncing of Google, Facebook, Twitter and Flickr accounts.  I have not hand entered or computer-synced a single contact to this phone and yet it already has everyone I care about in it.  The same goes for my calendar.  Change the cloud and it changes the phone.&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;li&gt;&lt;dl class="first docutils"&gt;
&lt;dt&gt;&lt;strong&gt;Notifications&lt;/strong&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first last"&gt;I prefer the Android way.  They stay in the top bar until you clear them (or respond), rather than a pop-up that is instantly lost.  I can keep my &amp;quot;new &amp;#64;reply&amp;quot; notification up for hours until I'm ready to check it.&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;li&gt;&lt;dl class="first docutils"&gt;
&lt;dt&gt;&lt;strong&gt;Speech-to-text&lt;/strong&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first last"&gt;It's everywhere.  I haven't used it much, but it seems handy for driving.&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;li&gt;&lt;dl class="first docutils"&gt;
&lt;dt&gt;&lt;strong&gt;Exposed filesystem&lt;/strong&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first last"&gt;Being able to sync Dropbox to a directory just like on a computer and then use other apps to easily access those files is just awesome.&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;li&gt;&lt;dl class="first docutils"&gt;
&lt;dt&gt;&lt;strong&gt;iTunes-less sync&lt;/strong&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first last"&gt;Related to the exposed filesystem, when I wanted some music on my phone I plugged it in and it mounted the 16GB MicroSD card.  I dragged a bunch of files (&lt;em&gt;gasp&lt;/em&gt;) to the volume, unmounted, and the Music app found it all automatically.&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;li&gt;&lt;dl class="first docutils"&gt;
&lt;dt&gt;&lt;strong&gt;Hardware back button&lt;/strong&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first last"&gt;And other buttons.  I'm surprised I like these, actually.  But the back button is especially handy, it brings some of the best of the web to every app on the phone.&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;li&gt;&lt;dl class="first docutils"&gt;
&lt;dt&gt;&lt;strong&gt;Google Voice&lt;/strong&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first last"&gt;Automatically use it for all outgoing calls, or have it ask you each time.&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;li&gt;&lt;dl class="first docutils"&gt;
&lt;dt&gt;&lt;strong&gt;Browser zooming&lt;/strong&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first last"&gt;This is hard to explain, but basically the browser will squish text content when you double tap to zoom.  I always liked how the iPhone would snap to content, but this seems like a step forward.  Snapping to desktop-sized content still usually means it's unreadable, and this extra squishing to bring it into readable font-size has been really, really handy.&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;li&gt;&lt;dl class="first docutils"&gt;
&lt;dt&gt;&lt;strong&gt;Improved Google apps&lt;/strong&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first last"&gt;Google is able (allowed?) to give extra love to their apps on Android.  Maps is improved in a &lt;em&gt;lot&lt;/em&gt; of ways, which is great considering how much I use it.  The same goes for most others (and more of their apps are available, period).&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
</content>
  </entry>
  <entry xml:base="http://bretthoerner.com/feed.atom">
    <title type="text">A sudden interest in Java (the platform)</title>
    <id>http://bretthoerner.com/2008/11/23/a-sudden-interest-in-the-java-platform</id>
    <updated>2008-11-23T00:00:00Z</updated>
    <link href="http://bretthoerner.com/2008/11/23/a-sudden-interest-in-the-java-platform" />
    <author>
      <name>Brett Hoerner</name>
    </author>
    <content type="html">&lt;p&gt;I'll admit that I didn't have much interest in Java until Sun opened it up (thus creating the &lt;a class="reference external" href="http://openjdk.java.net/"&gt;OpenJDK&lt;/a&gt;).  Like many Python developers I've been pretty &amp;quot;spoiled&amp;quot;, and reading Java code has generally left a bad taste in my mouth.  In short: it's way too verbose for me, and I definitely prefer dynamic to static.  I'm not saying I think anything less of Java developers, Java libraries, or Java programs - I just personally would prefer to use another language.&lt;/p&gt;
&lt;p&gt;Java (the platform) has a lot of goodies, though.  Instead of dreaming up a new language and writing your own rinky-dink VM, if you choose to target the JVM you start out with one of the largest (&lt;em&gt;the&lt;/em&gt; largest?) sets of libraries available (assuming you make it easy to call Java from your language).  You also get one of the world's best virtual machines, and thus you share a whole eco-system of tools like debuggers (I hear &lt;a class="reference external" href="http://code.google.com/p/jswat/"&gt;JSwat&lt;/a&gt; is great), &lt;a class="reference external" href="http://www.devx.com/Java/Article/33943"&gt;DTrace&lt;/a&gt;, and more.&lt;/p&gt;
&lt;p&gt;As of today you can already run a bunch of well known languages on the JVM - &lt;a class="reference external" href="http://www.jython.org/Project/"&gt;Jython&lt;/a&gt; (Python), &lt;a class="reference external" href="http://jruby.codehaus.org/"&gt;JRuby&lt;/a&gt; (Ruby), &lt;a class="reference external" href="http://www.mozilla.org/rhino"&gt;Rhino&lt;/a&gt;) (JavaScript), &lt;a class="reference external" href="http://code.google.com/p/kahlua/"&gt;Kahlua&lt;/a&gt; (Lua) in addition to &amp;quot;new&amp;quot; JVM-targeted languages like &lt;a class="reference external" href="http://clojure.org/"&gt;Clojure&lt;/a&gt; (a Lisp), &lt;a class="reference external" href="http://groovy.codehaus.org/"&gt;Groovy&lt;/a&gt; (similar to Ruby) and &lt;a class="reference external" href="http://www.scala-lang.org/"&gt;Scala&lt;/a&gt; (a better Java).&lt;/p&gt;
&lt;p&gt;It looks like there will be &lt;a class="reference external" href="http://tech.puredanger.com/java7"&gt;a lot of new features in Java 7&lt;/a&gt;, some of which were specifically added to support more / better / faster languages (other than Java) on the JVM.  The most important one (for dynamic languages) is &lt;a class="reference external" href="http://jcp.org/en/jsr/detail?id=292"&gt;JSR 292&lt;/a&gt; - support for a new bytecode called &lt;cite&gt;invokedynamic&lt;/cite&gt; which, in short, seems to allow languages implementations to stop pretending like they have static type information when they really don't.  From my loose understanding that means part of the implementation should be easier to write &lt;em&gt;and&lt;/em&gt; the code should run faster.&lt;/p&gt;
&lt;p&gt;Wouldn't it be cool to write an app in Jython but be able to call directly into a JRuby library that did what you needed in the best way?  Or for all of the different parts in your larger app to be able to use the same DB interface or memcached library, so you don't have to worry about differences between the one you use for Python and the one you use for Ruby?  At that point it seems like you really can use the best tool for the job, without a huge context switch of learning tons of new libraries, debug techniques, etc.&lt;/p&gt;
&lt;p&gt;I know I'll be keeping my eye on all of this.  For now I'm playing around with Clojure and from there I'll probably see what Django is like on Jython - seeing as that's what I'm most familiar with.&lt;/p&gt;
</content>
  </entry>
  <entry xml:base="http://bretthoerner.com/feed.atom">
    <title type="text">Using the Nginx memcached module with Django</title>
    <id>http://bretthoerner.com/2008/10/27/using-nginx-memcached-module-with-django</id>
    <updated>2008-10-27T00:00:00Z</updated>
    <link href="http://bretthoerner.com/2008/10/27/using-nginx-memcached-module-with-django" />
    <author>
      <name>Brett Hoerner</name>
    </author>
    <content type="html">&lt;p&gt;I decided to play around with the &lt;a class="reference external" href="http://wiki.codemongers.com/NginxHttpMemcachedModule"&gt;Nginx memcached module&lt;/a&gt; recently.  It's a very interesting and simple mod that checks &lt;cite&gt;memcached&lt;/cite&gt; for pages before falling back to your backend (or whatever you tell it to do, actually).&lt;/p&gt;
&lt;p&gt;The simplicity does have a couple of drawbacks,&lt;/p&gt;
&lt;p&gt;Cached bits are pulled out of &lt;cite&gt;memcached&lt;/cite&gt; and &lt;cite&gt;nginx&lt;/cite&gt; doesn't magically know their content-type.  You can (should) provide a default content-type that will be used for your typical dynamic requests such as &lt;cite&gt;/foo&lt;/cite&gt; and &lt;cite&gt;/bar/&lt;/cite&gt;.  If you want to cache non-[X]HTML content, you have to do some &lt;a class="reference external" href="http://www.igvita.com/2008/02/11/nginx-and-memcached-a-400-boost/"&gt;redirection hacking&lt;/a&gt; so that &lt;cite&gt;nginx&lt;/cite&gt; thinks it knows what it's serving - I don't go into that here as I'm only caching my dynamic HTML requests.&lt;/p&gt;
&lt;p&gt;The second issue is that you have to handle caching (and invalidation) inside of your app.  This brings with it all of the normal cache invalidation issues.  Since this is a very simple blog I was able to get away with a quick hack, and I haven't figured out how to best handle it on larger sites.  On the other hand, you control the &lt;cite&gt;memcached&lt;/cite&gt; timeouts, meaning even complex sites could use this with only time-based invalidation if they decided X seconds of dirty cache were alright for Y URLs.&lt;/p&gt;
&lt;div class="section" id="nginx-conf"&gt;
&lt;h2&gt;nginx.conf&lt;/h2&gt;
&lt;p&gt;I think this part is pretty self explanatory.  I only check &lt;cite&gt;memcached&lt;/cite&gt; on GET requests, and I have chosen a key prefix of &lt;cite&gt;NG:&lt;/cite&gt; so that I don't pollute the hash table (as the URL is used for the rest of the key).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="s"&gt;www-data&lt;/span&gt; &lt;span class="s"&gt;www-data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;worker_processes&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;pid&lt;/span&gt; &lt;span class="s"&gt;/var/run/nginx.pid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;error_log&lt;/span&gt; &lt;span class="s"&gt;/var/log/nginx/error.log&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;events&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kn"&gt;worker_connections&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;http&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kn"&gt;upstream&lt;/span&gt; &lt;span class="s"&gt;backend&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="n"&gt;127.0.0.1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;8000&lt;/span&gt; &lt;span class="s"&gt;weight=1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;# pass Host header on to the backend&lt;/span&gt;
  &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;# set X-Forwarded-For header so the backend knows who&lt;/span&gt;
  &lt;span class="c1"&gt;# actually made the request&lt;/span&gt;
  &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;# don&amp;#39;t send version in response headers&lt;/span&gt;
  &lt;span class="kn"&gt;server_tokens&lt;/span&gt; &lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kn"&gt;include&lt;/span&gt; &lt;span class="s"&gt;/usr/local/conf/mime.types&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;# fallback if none matches&lt;/span&gt;
  &lt;span class="kn"&gt;default_type&lt;/span&gt; &lt;span class="s"&gt;application/octet-stream&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;# about: http://www.baus.net/on-tcp_cork&lt;/span&gt;
  &lt;span class="kn"&gt;tcp_nopush&lt;/span&gt; &lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;sendfile&lt;/span&gt; &lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kn"&gt;gzip&lt;/span&gt; &lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;gzip_min_length&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;gzip_proxied&lt;/span&gt; &lt;span class="s"&gt;any&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;gzip_types&lt;/span&gt; &lt;span class="s"&gt;text/css&lt;/span&gt; &lt;span class="s"&gt;text/plain&lt;/span&gt; &lt;span class="s"&gt;application/atom+xml&lt;/span&gt; &lt;span class="s"&gt;application/x-javascript&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;gzip_vary&lt;/span&gt; &lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;bretthoerner.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;root&lt;/span&gt; &lt;span class="s"&gt;/a/bretthoerner.com/root&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt; &lt;span class="sr"&gt;[^/]$&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;# catches paths that don&amp;#39;t end in / (typically media)&lt;/span&gt;
        &lt;span class="c1"&gt;# if there is no matching static file in the root&lt;/span&gt;
        &lt;span class="c1"&gt;# proxy the request to the backend&lt;/span&gt;
        &lt;span class="kn"&gt;if&lt;/span&gt; &lt;span class="s"&gt;(!-f&lt;/span&gt; &lt;span class="nv"&gt;$request_filename&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://backend&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="kn"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;# don&amp;#39;t check memcached on POST, etc&lt;/span&gt;
      &lt;span class="kn"&gt;if&lt;/span&gt; &lt;span class="s"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request_method&lt;/span&gt; &lt;span class="s"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;GET)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://backend&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="c1"&gt;# pages fetched from memcached don&amp;#39;t know their&lt;/span&gt;
      &lt;span class="c1"&gt;# own mime type&lt;/span&gt;
      &lt;span class="kn"&gt;default_type&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;text/html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="kn"&gt;charset=utf-8&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="c1"&gt;# use same prefix as backend does&lt;/span&gt;
      &lt;span class="kn"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;$memcached_key&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;NG:&lt;/span&gt;&lt;span class="nv"&gt;$uri&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="c1"&gt;# memcached location&lt;/span&gt;
      &lt;span class="kn"&gt;memcached_pass&lt;/span&gt; &lt;span class="n"&gt;localhost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;11211&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="c1"&gt;# 404 for cache miss&lt;/span&gt;
      &lt;span class="c1"&gt;# 502 for memcached down&lt;/span&gt;
      &lt;span class="kn"&gt;error_page&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt; &lt;span class="mi"&gt;502&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;@cache_miss&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="s"&gt;@cache_miss&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://backend&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;media.bretthoerner.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kn"&gt;root&lt;/span&gt; &lt;span class="s"&gt;/a/bretthoerner.com/media&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="django-middleware-for-setting-cache"&gt;
&lt;h2&gt;Django middleware for setting cache&lt;/h2&gt;
&lt;p&gt;This is where the cache is set.  On each &lt;cite&gt;Django&lt;/cite&gt; response I check that,&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The request is a GET&lt;/li&gt;
&lt;li&gt;The request is not to &lt;cite&gt;/admin&lt;/cite&gt;&lt;/li&gt;
&lt;li&gt;The request is a success&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then you simply set the cache where &lt;cite&gt;nginx&lt;/cite&gt; will look,&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NginxMemcacheMiddleWare&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_full_path&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt; \
           &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;/admin&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_anonymous&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; \
           &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;

        &lt;span class="c"&gt;# settings.NGINX_CACHE_PREFIX == &amp;#39;NG&amp;#39;, just like nginx.conf&lt;/span&gt;
        &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NGINX_CACHE_PREFIX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="django-signal-for-invalidating-cache"&gt;
&lt;h2&gt;Django signal for invalidating cache&lt;/h2&gt;
&lt;p&gt;Cache invalidation is freakin' hard, especially on a more complicated site.  As I said previously, this is mostly a hack, it isn't the sort of thing that could scale (not in performance, but code and development).  I'm trying to think of a better way to handle it, but describing and maintaining dependencies in a code-base is tough.&lt;/p&gt;
&lt;p&gt;Anyway, the following should also be pretty self explanatory.  When an object is saved, the URL for the object itself is invalidated (that's the easy part).  Now the tough question is - what else needs to change?  For my simple blog I've just hardcoded these values because I only have one model I really care about (&lt;cite&gt;Entry&lt;/cite&gt;).  I invalidate the homepage and all archive pages, so that the next request to any will have the latest title / body for all entries.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;delete_memcache_keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;get_absolute_url&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_absolute_url&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Entry&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pub_date&lt;/span&gt;

        &lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="s"&gt;&amp;quot;/blog/&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="s"&gt;&amp;quot;/blog/&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;/&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;date_format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Y&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                     &lt;span class="s"&gt;&amp;quot;/blog/&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;/&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;date_format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Y/b&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                     &lt;span class="s"&gt;&amp;quot;/blog/&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;/&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;date_format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Y/b/d&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NGINX_CACHE_PREFIX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


&lt;span class="n"&gt;signals&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post_save&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delete_memcache_keys&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="benchmarks-and-conclusion"&gt;
&lt;h2&gt;Benchmarks and conclusion&lt;/h2&gt;
&lt;p&gt;So how much faster is it?  Well, there's a little hitch when you use a basic blog to test this stuff out - it's already fast as hell.  I'm using &lt;cite&gt;sqlite&lt;/cite&gt; and am pretty confident the entire 150K database is in memory.  A typical request does maybe 1 simple query and returns the pre-rendered entry field from the database.  What that all means is that the largest cost of requesting a page (on &lt;em&gt;this&lt;/em&gt; site) is traffic over the wire, e.g. waiting for the bits.&lt;/p&gt;
&lt;p&gt;A more complex app would be a much different story, although I don't have one to try it on.  I will say that if I added a simple &lt;cite&gt;time.sleep(.1)&lt;/cite&gt; (100ms) to my index view - in order to simulate more work that would have to be done on a real site - I went from ~13 req/s to ~27 req/s.  (Keep in mind I'm on a small VPS and I was only doing 3 concurrent requests at a time because I don't have many pre-forked &lt;cite&gt;Apache&lt;/cite&gt; children created, and I wanted to keep both tests 1:1)  Still, a pretty big deal, no?&lt;/p&gt;
&lt;/div&gt;
</content>
  </entry>
  <entry xml:base="http://bretthoerner.com/feed.atom">
    <title type="text">Kickstarting a better Ubuntu server environment</title>
    <id>http://bretthoerner.com/2008/10/27/kickstarting-a-better-ubuntu-environment</id>
    <updated>2008-10-27T00:00:00Z</updated>
    <link href="http://bretthoerner.com/2008/10/27/kickstarting-a-better-ubuntu-environment" />
    <author>
      <name>Brett Hoerner</name>
    </author>
    <content type="html">&lt;p&gt;I decided to clean up and post my notes on how I setup my &lt;cite&gt;Ubuntu&lt;/cite&gt; server environment (I happen to use &lt;a class="reference external" href="http://www.slicehost.com"&gt;Slicehost&lt;/a&gt;, but it shouldn't matter).&lt;/p&gt;
&lt;p&gt;I began with an &amp;quot;empty&amp;quot; &lt;cite&gt;Ubuntu&lt;/cite&gt; 8.04 machine and the root password, at the end I have:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Disabled root login via &lt;cite&gt;ssh&lt;/cite&gt;&lt;/li&gt;
&lt;li&gt;Created your own user who can login via &lt;cite&gt;ssh&lt;/cite&gt; public-key authentication&lt;/li&gt;
&lt;li&gt;Updated your system with the latest &lt;cite&gt;Ubuntu&lt;/cite&gt; packages&lt;/li&gt;
&lt;li&gt;Installed various packages that most people expect on a Linux server&lt;/li&gt;
&lt;li&gt;Setup simple mail forwarding (I forward bretthoerner.com to my Gmail account)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This assumes you already have a domain pointed at your server and that you have &lt;cite&gt;ssh&lt;/cite&gt; keys stored locally in &lt;cite&gt;~/.ssh&lt;/cite&gt;.&lt;/p&gt;
&lt;div class="section" id="initial-login"&gt;
&lt;h2&gt;Initial login&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="c"&gt;# Use your root password when prompted&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ssh root@DOMAIN
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="replace-bin-sh-symlink-with-a-more-fully-featured-shell"&gt;
&lt;h2&gt;Replace &lt;cite&gt;/bin/sh&lt;/cite&gt; symlink with a more fully featured shell&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ln -sf /bin/bash /bin/sh
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="create-your-user"&gt;
&lt;h2&gt;Create your user&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="c"&gt;# Add USER (using bash, with a home, in groups USER, users, and sudo)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;useradd -G users,sudo -m -c &lt;span class="s2"&gt;&amp;quot;FULL NAME&amp;quot;&lt;/span&gt; -s /bin/bash USER
&lt;span class="c"&gt;# Configure sudo to be password-less for users in group sudo&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;visudo
&lt;span class="c"&gt;# Uncomment %sudo ALL=NOPASSWD: ALL&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="setup-ssh-public-key-login"&gt;
&lt;h2&gt;Setup &lt;cite&gt;ssh&lt;/cite&gt; public-key login&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="c"&gt;# Login as user&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;su - USER

&lt;span class="c"&gt;# Configure `ssh` public key login&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;mkdir ~/.ssh
&lt;span class="nv"&gt;$ &lt;/span&gt;chmod 700 ~/.ssh
&lt;span class="nv"&gt;$ &lt;/span&gt;vim ~/.ssh/authorized_keys
&lt;span class="c"&gt;# Paste in your public key (from local ~/.ssh/id_[rd]sa.pub)&lt;/span&gt;

&lt;span class="c"&gt;# Exit out of user&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;span class="c"&gt;# Exit out of root&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;

&lt;span class="c"&gt;# Test login from your local machine&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ssh USER@DOMAIN &lt;span class="c"&gt;# should work without a password&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="disable-root-login"&gt;
&lt;h2&gt;Disable root login&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="c"&gt;# Test sudo from your user before you disable root login&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo whoami &lt;span class="c"&gt;# should print &amp;#39;root&amp;#39;&lt;/span&gt;

&lt;span class="c"&gt;# Disable root ssh and password-based logins&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo vim /etc/ssh/sshd_config
&lt;span class="c"&gt;# Change &amp;quot;PermitRootLogin yes&amp;quot; to &amp;quot;PermitRootLogin no&amp;quot;&lt;/span&gt;
&lt;span class="c"&gt;# Change &amp;quot;#PasswordAuthentication yes&amp;quot; to &amp;quot;PasswordAuthentication no&amp;quot;&lt;/span&gt;

&lt;span class="c"&gt;# Reload the ssh config&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo /etc/init.d/ssh reload

&lt;span class="c"&gt;# Remove root&amp;#39;s password&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo passwd -d root
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="update-the-local-package-repository-and-packages-on-your-system"&gt;
&lt;h2&gt;Update the local package repository and packages on your system&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo apt-get update
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo apt-get dist-upgrade -y
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="install-packages-you-d-expect-to-have-on-linux"&gt;
&lt;h2&gt;Install packages you'd expect to have on Linux&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="c"&gt;# Favorite editor and shell niceness&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo apt-get install -y bash-completion &lt;span class="nb"&gt;command&lt;/span&gt;-not-found &lt;span class="se"&gt;\&lt;/span&gt;
                          emacs-snapshot-nox exuberant-ctags vim-nox

&lt;span class="c"&gt;# Postfix&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo apt-get install -y postfix procmail
&lt;span class="c"&gt;# Just hit enter through the setup for now&lt;/span&gt;

&lt;span class="c"&gt;# Linux basics&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo apt-get install -y dnsutils file info logrotate lsof &lt;span class="se"&gt;\&lt;/span&gt;
                          mailx mlocate openssl rsync screen unzip

&lt;span class="c"&gt;# Developer basics&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo apt-get install -y autoconf build-essential cdecl colordiff &lt;span class="se"&gt;\&lt;/span&gt;
                          git-core git-svn libtool make patch subversion
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="re-configure-postfix"&gt;
&lt;h2&gt;Re-configure &lt;cite&gt;Postfix&lt;/cite&gt;&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo dpkg-reconfigure postfix
&lt;/pre&gt;&lt;/div&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;General Type: &lt;cite&gt;Internet Site&lt;/cite&gt;&lt;/li&gt;
&lt;li&gt;System mail name: &lt;cite&gt;DOMAIN&lt;/cite&gt;&lt;/li&gt;
&lt;li&gt;Root receipt: &lt;cite&gt;USER&lt;/cite&gt;&lt;/li&gt;
&lt;li&gt;Destinations: &lt;cite&gt;localhost&lt;/cite&gt;&lt;/li&gt;
&lt;li&gt;Force synchronous updates: &lt;cite&gt;no&lt;/cite&gt;&lt;/li&gt;
&lt;li&gt;Local networks: &lt;cite&gt;127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128&lt;/cite&gt;&lt;/li&gt;
&lt;li&gt;Use procmail for local delivery: &lt;cite&gt;yes&lt;/cite&gt;&lt;/li&gt;
&lt;li&gt;Mailbox size limit: &lt;cite&gt;0&lt;/cite&gt;&lt;/li&gt;
&lt;li&gt;Local address extension character: &lt;cite&gt;+&lt;/cite&gt;&lt;/li&gt;
&lt;li&gt;Internet protocols to use: &lt;cite&gt;all&lt;/cite&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="edit-the-postfix-config"&gt;
&lt;h2&gt;Edit the Postfix config&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo vim /etc/postfix/main.cf
&lt;span class="c"&gt;# Add the following&lt;/span&gt;
&lt;span class="nv"&gt;virtual_alias_domains&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; DOMAIN
&lt;span class="nv"&gt;virtual_alias_maps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;hash&lt;/span&gt;:/etc/postfix/virtual
&lt;span class="nv"&gt;smtpd_helo_required&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; yes
&lt;span class="nv"&gt;strict_rfc821_envelopes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; yes
&lt;span class="nv"&gt;disable_vrfy_command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; yes
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="create-virtual-users-and-aliases-that-forward-to-you"&gt;
&lt;h2&gt;Create virtual users and aliases that forward to you&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo vim /etc/postfix/virtual
&lt;span class="c"&gt;# Add one USER@VIRTUAL_DOMAIN EMAIL_TO_FORWARD_TO per line, e.g.:&lt;/span&gt;
user@example.com user@gmail.com

&lt;span class="nv"&gt;$ &lt;/span&gt;sudo postmap /etc/postfix/virtual

&lt;span class="nv"&gt;$ &lt;/span&gt;sudo vim /etc/aliases
&lt;span class="c"&gt;# Append the following line so that root mail goes to USER@DOMAIN&lt;/span&gt;
root: USER

&lt;span class="nv"&gt;$ &lt;/span&gt;sudo newaliases

&lt;span class="c"&gt;# Restart postfix for changes to take effect&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo /etc/init.d/postfix restart
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That's it.  I'll follow this up with more information on how I setup Apache, mod_wsgi, Django, nginx, etc.&lt;/p&gt;
&lt;/div&gt;
</content>
  </entry>
  <entry xml:base="http://bretthoerner.com/feed.atom">
    <title type="text">Configs for Nginx and Apache with mod_wsgi</title>
    <id>http://bretthoerner.com/2008/10/9/configs-for-nginx-and-apache-mod-wsgi</id>
    <updated>2008-10-09T00:00:00Z</updated>
    <link href="http://bretthoerner.com/2008/10/9/configs-for-nginx-and-apache-mod-wsgi" />
    <author>
      <name>Brett Hoerner</name>
    </author>
    <content type="html">&lt;p&gt;I cleaned up my configuration files for &lt;cite&gt;nginx&lt;/cite&gt; and &lt;cite&gt;Apache&lt;/cite&gt; (running &lt;cite&gt;mod_wsgi&lt;/cite&gt; and &lt;cite&gt;Django&lt;/cite&gt;).  It's pretty straight forward stuff.  I use &lt;cite&gt;nginx&lt;/cite&gt; to serve static files and proxy all other requests (like this page) back to &lt;cite&gt;Apache&lt;/cite&gt;.&lt;/p&gt;
&lt;div class="section" id="nginx-conf"&gt;
&lt;h2&gt;nginx.conf&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="s"&gt;www-data&lt;/span&gt; &lt;span class="s"&gt;www-data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;worker_processes&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;pid&lt;/span&gt; &lt;span class="s"&gt;/var/run/nginx.pid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;error_log&lt;/span&gt; &lt;span class="s"&gt;/var/log/nginx/error.log&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;events&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kn"&gt;worker_connections&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;http&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;# don&amp;#39;t send version in response headers&lt;/span&gt;
  &lt;span class="kn"&gt;server_tokens&lt;/span&gt; &lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kn"&gt;include&lt;/span&gt; &lt;span class="s"&gt;/etc/nginx/mime.types&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;# fallback if none matches&lt;/span&gt;
  &lt;span class="kn"&gt;default_type&lt;/span&gt; &lt;span class="s"&gt;application/octet-stream&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;# about: http://www.baus.net/on-tcp_cork&lt;/span&gt;
  &lt;span class="kn"&gt;tcp_nopush&lt;/span&gt; &lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;sendfile&lt;/span&gt; &lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kn"&gt;gzip&lt;/span&gt; &lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;gzip_min_length&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;gzip_proxied&lt;/span&gt; &lt;span class="s"&gt;any&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;gzip_types&lt;/span&gt; &lt;span class="s"&gt;text/css&lt;/span&gt; &lt;span class="s"&gt;text/plain&lt;/span&gt; &lt;span class="s"&gt;application/atom+xml&lt;/span&gt; &lt;span class="s"&gt;application/x-javascript&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;gzip_vary&lt;/span&gt; &lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;DOMAIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;root&lt;/span&gt; &lt;span class="s"&gt;/a/DOMAIN/root&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;# pass Host header on to the backend&lt;/span&gt;
      &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="c1"&gt;# set X-Forwarded-For header so the backend knows who&lt;/span&gt;
      &lt;span class="c1"&gt;# actually made the request&lt;/span&gt;
      &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="c1"&gt;# if there is no matching static file in the root&lt;/span&gt;
      &lt;span class="c1"&gt;# proxy the request to the backend&lt;/span&gt;
      &lt;span class="kn"&gt;if&lt;/span&gt; &lt;span class="s"&gt;(!-f&lt;/span&gt; &lt;span class="nv"&gt;$request_filename&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://localhost:8000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;media.DOMAIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kn"&gt;root&lt;/span&gt; &lt;span class="s"&gt;/a/DOMAIN/media&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="apache2-conf"&gt;
&lt;h2&gt;apache2.conf&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="nb"&gt;Listen&lt;/span&gt; &lt;span class="m"&gt;127.0.0.1&lt;/span&gt;:8000

&lt;span class="c"&gt;# should be name clients would use reach this host&lt;/span&gt;
&lt;span class="c"&gt;# and not just the address you happen to bind to&lt;/span&gt;
&lt;span class="nb"&gt;ServerName&lt;/span&gt; DOMAIN:80
&lt;span class="nb"&gt;ServerRoot&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/etc/apache2&amp;quot;&lt;/span&gt;

&lt;span class="nb"&gt;User&lt;/span&gt; ${APACHE_RUN_USER}
&lt;span class="nb"&gt;Group&lt;/span&gt; ${APACHE_RUN_GROUP}
&lt;span class="nb"&gt;PidFile&lt;/span&gt; ${APACHE_PID_FILE}
&lt;span class="nb"&gt;LockFile&lt;/span&gt; &lt;span class="sx"&gt;/var/lock/apache2/accept.lock&lt;/span&gt;

&lt;span class="c"&gt;# nginx doesn&amp;#39;t support backend keep-alive yet&lt;/span&gt;
&lt;span class="nb"&gt;KeepAlive&lt;/span&gt; &lt;span class="k"&gt;Off&lt;/span&gt;

&lt;span class="c"&gt;# prefork-mpm settings&lt;/span&gt;
&lt;span class="nb"&gt;StartServers&lt;/span&gt;          &lt;span class="m"&gt;5&lt;/span&gt;
&lt;span class="nb"&gt;MinSpareServers&lt;/span&gt;       &lt;span class="m"&gt;5&lt;/span&gt;
&lt;span class="nb"&gt;MaxSpareServers&lt;/span&gt;       &lt;span class="m"&gt;5&lt;/span&gt;
&lt;span class="nb"&gt;MaxClients&lt;/span&gt;            &lt;span class="m"&gt;5&lt;/span&gt;
&lt;span class="nb"&gt;MaxRequestsPerChild&lt;/span&gt;   &lt;span class="m"&gt;5000&lt;/span&gt;

&lt;span class="nb"&gt;DefaultType&lt;/span&gt; application/octet-stream

&lt;span class="nb"&gt;ErrorLog&lt;/span&gt; &lt;span class="sx"&gt;/var/log/apache2/error.log&lt;/span&gt;
&lt;span class="nb"&gt;LogLevel&lt;/span&gt; &lt;span class="k"&gt;warn&lt;/span&gt;

&lt;span class="c"&gt;# log x-forwarded-for because the request will always&lt;/span&gt;
&lt;span class="c"&gt;# come from nginx, not the actual user&lt;/span&gt;
&lt;span class="nb"&gt;LogFormat&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;%{X-Forwarded-For}i %l %u %t \&amp;quot;%r\&amp;quot; %&amp;gt;s %b \&amp;quot;%{Referer}i\&amp;quot; \&amp;quot;%{User-Agent}i\&amp;quot;&amp;quot;&lt;/span&gt; combined
&lt;span class="nb"&gt;CustomLog&lt;/span&gt; &lt;span class="sx"&gt;/var/log/apache2/access.log&lt;/span&gt; combined

&lt;span class="nb"&gt;LoadModule&lt;/span&gt; wsgi_module &lt;span class="sx"&gt;/usr/lib/apache2/modules/mod_wsgi.so&lt;/span&gt;

&lt;span class="c"&gt;# BASELINE is an empty virtual environment that doesn&amp;#39;t&lt;/span&gt;
&lt;span class="c"&gt;# fallback to the global site-packages directory.  Using it&lt;/span&gt;
&lt;span class="c"&gt;# as the Python home ensures that all WSGI apps run out of&lt;/span&gt;
&lt;span class="c"&gt;# their own virtualenv, so nothing pollutes anything else.&lt;/span&gt;
&lt;span class="nb"&gt;WSGIPythonHome&lt;/span&gt; &lt;span class="sx"&gt;/a/BASELINE&lt;/span&gt;

&lt;span class="nb"&gt;NameVirtualHost&lt;/span&gt; &lt;span class="m"&gt;127.0.0.1&lt;/span&gt;:8000

&lt;span class="c"&gt;# default virtualhost so that _real_ sites are only&lt;/span&gt;
&lt;span class="c"&gt;# served when specifically requested&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;VirtualHost&lt;/span&gt; &lt;span class="s"&gt;127.0.0.1:8000&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nb"&gt;ServerName&lt;/span&gt; &lt;span class="m"&gt;127.0.0.1&lt;/span&gt;
    &lt;span class="nb"&gt;DocumentRoot&lt;/span&gt; &lt;span class="sx"&gt;/var/www&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/VirtualHost&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;VirtualHost&lt;/span&gt; &lt;span class="s"&gt;127.0.0.1:8000&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nb"&gt;ServerName&lt;/span&gt; DOMAIN
    &lt;span class="nb"&gt;DocumentRoot&lt;/span&gt; &lt;span class="sx"&gt;/a/DOMAIN/root&lt;/span&gt;
    &lt;span class="nb"&gt;WSGIScriptAlias&lt;/span&gt; / &lt;span class="sx"&gt;/a/DOMAIN/etc/wsgi.py&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/VirtualHost&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="wsgi-py"&gt;
&lt;h2&gt;wsgi.py&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;site&lt;/span&gt;

&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addsitedir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;/a/DOMAIN/lib/python2.5/site-packages/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;DJANGO_SETTINGS_MODULE&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;PROJECT.settings&amp;#39;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;django.core.handlers.wsgi&lt;/span&gt;

&lt;span class="n"&gt;application&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;django&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;handlers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wsgi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WSGIHandler&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
</content>
  </entry>
</feed>

