Running Linux on a Mid-2009 MacBook Pro

Apple and their aggressive EOL/EOS policies

I have had an old mid-2009 MacBook Pro (2.26Mhz, Core 2 Duo P7550) that got left behind at macOS 10.11 (El Capitan). I’ve already gone through dealing with end-of-support with a 2011 MBP (no AirPlay server, no longer getting macOS updates, etc…), but the 2009 is old enough that Homebrew itself has dropped support officially. While that doesn’t prevent everything from working, once something won’t install, you either need to fix it yourself or give up.

I was trying to get RDP up and running when I hit that issue, so I figured that it was time to see if I could install an operating system that was still supported on the 2009 MBP.

Ubuntu Linux attempt

I first went down the rabbit hole of Ubuntu, but the first set of instructions I found did not give me a warm fuzzy about being able to install Ubuntu and still have a working machine. I used Rufus to write an Ubuntu ISO to a USB drive and was able to live boot into Ubuntu. Of course, Linux and wifi isn’t always plug and play and the live boot did not recognize the MBP’s wifi card, so it would have been wired or nothing until I installed the OS.

Hello Manjaro

Time for a different strategy. I tried a different strategy and searched for Linux distros for a 2009 MacBook Pro. I decided to go with Manjaro XFCE and was able to write the ISO to my USB drive and have wifi working on live boot, so I swapped MacBook hard drives back to the original 5400 RPM 160GB hard drive that came with it.

Manjaro with XFCE is far snappier than even my 2011 or 2019 MBPs. This is mainly because no bloat and very little running so far, but I’ve also installed Ruby 2.8.0-dev from Manjaro in rbenv on the 2009 (P7550 Core 2 Duo) vs. from the 2019 (i7-9750H 6 core) and the install/build time was 9m31s for the 2009 vs. 11m19s for the 2019 (yes, the 11 year old Mac was faster). This is probably a Clang vs. gcc and readline + openssl installation/build difference between the two environments, but I still found it interesting that the 2009 could win the race even with a head start.

As for system load, the MBP 2009 not actively running anything on macOS 10.11 would register a consistent 2.0+ load and would take several minutes to be usable for typing in iTerm. By contrast, running XFCE, it required several tabs open in Firefox + Terminal + gvim for the load to break 1.0.

Steps to get up and running:

  • Download Manjaro XFCE iso
  • Write image to USB drive using Rufus (dd or whatever if you are adequately skilled at that… Rufus was just painfully easy to kick off from a Windows 10 box)
  • Hold down option while booting to get boot menu.
  • Select your USB drive
  • Boot and verify that Linux is sufficiently usable for your MacBook
  • Install to a hard drive (I recommend using a different hard drive that is booting macOS, but then you’ll need tools for the mounting screws at a minimum.
  • Boot and enjoy

Getting set up

Having used yum, apt, and brew, pamac was a bit of a change. For example, I had to use pamac build ruby-build and pamac build rbenv vs. an install command.

I have starship installed as well, which was relatively straightforward, but I still need to get the fonts right for unicode.

Overall experience

So far I’ve been pleasantly surprised with how useful my 2009 MacBook Pro is again. I’ve managed to type up this blog post without any lag from the processor, and the memory usage is sane as well (despite Firefox). I also miss having the pre-butterfly keyboard keys to type on for writing, so it was nice to experience those again.

Planet Fitness Cancellation Template

Update: I put this in the mail on Tuesday and got cancellation confirmation less than 48 hours later.

Also, your mileage may vary, some locations are a little more of a stickler about certified mail, but you should get a confirmation from the location within a few days if they’ve accepted.

I needed to cancel Planet Fitness in the middle of the COVID-19 pandemic and I really didn’t want to be showing up at a gym in person right now (time and unnecessary risk) just to cancel.

I called up my local Planet Fitness and was told I could either come in in person *or* send a [yes, snail mail] letter to the Planet Fitness I signed up at.

So, I’ve thrown together a cheap template to help you include everything you need to cancel (it’s just name, date of birth, and signature, according to the person on the phone.)

You can download the doc I put together at: Planet Fitness Cancellation Word Doc template

Who Else is Participating in Advent of Code 2019

Who else is doing Advent of Code 2019?

I’m using it to try to solve problems in Ruby without actually looking up syntax (looking at you, inject) and getting some practice using MiniTestinstead of RSpec which I use 99% of the time.

Day 2 was kind of fun to be able to implement a tiny virtual machine in Ruby.

Day 3 had me stumped until I just when for a naive solution in which I just created a list of all the coordinates visited instead of trying to figure out an elegant way to calculate the answer… an approach which actually paid off in the second half.

Day 4 has been the problem that I spent the most time fighting with (specifically the second part) after thinking that I had it solved. It was one of those scenarios in which I pretty much starting chasing my own tail after the solution I thought should work didn’t. I ended up with 4 state variables in addition the input variable itself.  

Prius Gas Mileage Using a Thule on a Road Trip

I’ve been getting an actual 44 MPG over the first summer of owning my Prius.

Running the heat and defrosting windows dropped my gas mileage down below 40. 


Multiple Postgres Schemas and Rails db:test:prepare

In our Rails databases, we use multiple Postgres schemas. This is partly for partitioning archive data from fresh data. On a new Rails 5 project, I started having tests fail once the models had the “archivable” concern added, which depended on an archive schema existing.

Ultimately, the problem is that the archive schema wasn’t being created in the test database, because rake db:test:prepare performs a db:test:load which was loading the db/schema.rb, which is completely ignorant of the Postgres-specific schema concept.

In order to fix this without attempting ugly hacks, the simplest solution was just to switch to using structure.sql for the Rails schema dump.

In config/application.rb, insert the following in your Application class definition:

config.active_record.schema_format = :sql

If you haven’t already added the additional schema(s) to your schema_search_path in your config/database.yml.

database: whatever_db
schema_search_path: 'main,main_archive,public'

You’ll need to run rake db:migrate to force structure.sql generation before running tests again. Also, be sure to switch from schema.rb to structure.sql if you’re committing this file.

Postgres 9.4 via Homebrew on macOS

I needed to run an old version of Postgres in order to be supported by the version of Rails I was working with, and I had a new Mac with High Sierra installed. Found out after a bit of “Why doesn’t this postgres formula link its executables?” that it was precisely because of the age of the version that it wasn’t linking. (In my old Linux days I would have just automatically added the PATH or made the link, but I also would have probably rebuilt the Linux install within a few months, anyway.)

# doh this is a keg-only formula. Had to manually link CLI regardless of install method
# run these to set up
brew tap homebrew/versions
brew install homebrew/versions/postgres94
brew services start postgresql@9.4 export
# in your shell's RC file:
# For pkg-config to find this software you may need to set:
# -= end shell RC file =-
# do your normal postgresql setup now

view raw
hosted with ❤ by GitHub


How safe is an MD5 hash of a plain password?

First of all, I hope that you’ve moved beyond MD5 hashes and hashing passwords by themselves, adding salts, etc., but I do recall systems in which an MD5 hash of a password by itself was “good enough”.

You can look up some md5 hashes on this md5 cracker page. I found many two word combinations that were crackable.

You can play with generating md5 hashes of questionable passwords (such as your name and p@ssw0rd) with this md5 Hash Generator

Bundler gave the error “Could not find {some gem} in any of the sources”, processing Gemfile in wrong directory


I was persistently getting the error:

Bundler gave the error "Could not find json-1.8.1 in any of the sources" while processing "/Users/tpowell/projects/some_other_project/Gemfile". Perhaps you forgot to run "bundle install"?

While trying to fix a bug in paperclip_database that was similar to Paperclip Gerenator – No Method Error in paperclip

I eventually found an issue referencing this problem in the rbenv github repo.:

It is recommended to not use the rbenv-bundler plugin, as it has been the source of many problems


I ended up uninstalling the rbenv-bundler plugin as a result.

But hey, kudos to rbenv-bundler author for taking the complaint in stride.

Enums in ActiveRecord 4.1 and options_for_select

Update: This StackOverflow answer provides a better way to use a drop-down with enums. I could’ve sworn I tried that, but maybe not.

I discovered something interesting about the way ActiveRecord 4.1 enums work. If you’re trying to use them with options_for_select, you may need to do some little acrobatics with your enums.

Assume your model provides:

class Car < ActiveRecord::Base
  # body_style is an integer in the database
  enum body_style: [:coupe, :convertible, :van, :suv, :sedan]

And your update route for cars_controller does this:

class CarsController < ApplicationController
  before_action :set_car, only: [:show, :edit, :update, :destroy]

  # PATCH/PUT /cars/1
  # PATCH/PUT /cars/1.json
  def update
    respond_to do |format|
      if @car.update(car_params)
        format.html { redirect_to @car, notice: 'Car was successfully updated.' }
        format.json { head :no_content }
        format.html { render action: 'edit' }
        format.json { render json: @car.errors, status: :unprocessable_entity }
    # Use callbacks to share common setup or constraints between actions.
    def set_car
      @car = Car.find(params[:id])

    # Never trust parameters from the scary internet, only allow the white list through.
    def car_params

Then, using the enumeration in a form_for block in your view requires a deviation from what Car.body_styles will give you.

The value submitted by this form snippet won’t validate because update is expecting the “string” key, and not the underlying numerical value of the table.

<%= form_for(@car) do |f| %>
  <%= f.label :body_style %>
  <%= :body_style, options_for_select(Car.body_styles) %>

Instead, a little bit of transformation needs to be done so that the description/value pair for the elements are both the text value of the enum.

<%= form_for(@car) do |f| %>
  <%= f.label :body_style %>
  <%= :body_style, options_for_select( {|k,v| [k,v]}) %>

Or, with the selected value included:

<%= form_for(@car) do |f| %>
  <%= f.label :body_style %>
  <%= :body_style, options_for_select( {|k,v| [k,v]}, @car.body_style) %>

Git wrapper for vim: fugitive.vim

Today’s browsing through “plugins that I haven’t made good enough use of” brings me to vim-fugitive:

:Gstatus pulls git status output into a vim split window. Pressing - on the line that a file is listed on either adds or removes a file from staging, and :Gcommit commits it.

:Gdiff opens a vertical split next to a file and uses vim’s diff facility to compare working copy to staged version.

:Gblame opens a vertical split next to a file with an interactive git blame output that tracks the source file as you scroll. Pressing on a line opens the commit that changed the code (or o to open in a split.)

:Glog opens the log in the quickfix window:

  • :copen to open the quickfix window to scroll through the versions
  • :ccl close the quickfix window
  • :cn go to the next error in the quickfix window (append a number to jump # steps)
  • :cp go to the previous error in the quickfix window (append a number to jump # steps)

:Gmove performs a git mv and modifies the buffer location as well.
:Gremove performs a git rm and removes the buffer as well.

Other commands of interest that I haven’t considered for my current workflow:
:Git performs any other git command not already covered.
:Gedit, :Gsplit, :Gvsplit, :Gtabedit appear to open a repository object directly… not quite ready to pick at individual objects on that level.