Oliver Schrenk bio photo

Oliver Schrenk

I'm a software consultant living in Amsterdam. I enjoy working with Git, Scala, and all things immutable and functional. In my free time I delve in all things sci-fi, play boardgames and learn about coffee.

Email Twitter Github Stackoverflow

So, I recently obtained a Linksys 1900AC router. While it is a powerul router, it really lacks some features I like to see on my router. As a OS X user it would have been nice to see Time Machine support but alas there is nothing. I bought it because there was an implicit promise that you can put OpenWRT on it.

Turns on only with the most recent OpenWRT version is officially supported. I tried version 15.05 RC2 and added TimeMachine support only to see the router degrade my network performance after one week. It actually bothered my roommate so much that she disconnected the “blue box” and put the “beige” box back. Thanks Netflix!

Well, so my overpriced router is standing in the corner without seeing actually usage. But now, 15.05, code name “Chaos Calmer” has officially been released. Hopefully they fixed my performance issues.

Time to give OpenWRT another go but this time by compiling it from source!

Going down the wrong route

Well, I was lying. We are actually not going to compile OpenWRT on OS X, even if the official wiki wants you to believe that is actually possible.

The one piece of information they are mising is that in order to compile OpenWRT you need a file sensitive file system! Which im guessing 99,9% of OS X users don’t have as it is not the default. Now I guess you could create a disk image with a file sensitive HFS file system, but let’s try something else.

Let’s not pollute the main system with dependencies you just need to make your outer work. For this kind of things Docker is amazing and its finally (kind of) working on OS X.

Installing Docker

Unfortunately Docker can’t run natively on OS X, but at least it runs “seamlessly” using a daemon on a virtual machine. I quote “seamlessly” because I had my fair share of troubles of making it work.

Let’s install VirtualBox and Docker

brew cask install virtualbox
brew install docker
brew install docker-machine
docker-machine create --driver virtualbox dev

It will prompt you to set your environment so that docker knows on which machine it should execute. I am running the fish shell, so it prompted me to

eval (docker-machine env dev)

Now you should have a running machine named dev.

You can

# list your machines
docker-machine ls

# start `dev` machine
docker-machine start dev

# stop `dev` machine
docker-machine stop dev

# reread the instructions on how to set up you shell for `dev` machine
docker-machine env dev

Running the container

Now that Docker is up and running we can create the Docker image.

I’m not the first person coming up with the idea of using a Docker image for compiling OpenWRT. In fact George Jiglau create one and Andreas Kohn forked it and based it on OpenWRT 15.05.

mkdir -p ~/Docker/openwrt
cd ~/Docker/openwrt
wget https://raw.githubusercontent.com/ankon/docker-openwrt-buildroot/f67c9dbc3821766ef30d94acbf124a1eb86c94f6/Dockerfile
docker build .

It will create the Docker image. Run

docker images
REPOSITORY          TAG                 ID                  CREATED
<none>              <none>              6728bc817ad9        1 minute ago

And it should list your newly created image. Mine has the ID 6728bc817ad9

Preparing source code

So now we have our Docker image ready to compile OpenWRT. Let’s get to it.

# Take the id from the prvious step
docker run -i -t 6728bc817ad9 /bin/bash

# we need to compile as a non-root user, the image had "openwrt" user
su openwrt
cd ~/openwrt

# pull latest changes
git pull

Configure base system

It’s a good idea to not fiddle arround with too much option but to just go with the defaults and just setup the target system and see if everything compiles.

Run make menuconfig and set the target system.

For the Linksys 1900 AC v1

  • the Target System in make menuconfig should be set to “Marvell Armada XP/370” and
  • the Target Profile in make menuconfig should be set to “Linksys WRT1900AC (Mamba)”

Run make defconfig

Build it for the first time

# call make and enable console logging with V=s
make V=s

And now the wait begins. I’m serious. Get some coffee, do laundry, watch a movie. Your mileage may vary but it took arround 2 hours until everything was compiled.

Save your base image

What a glorious day. Everything compiled successfully. Let’s save that state of the image, so we can get back to this at a later stage and save us a lot of hassle in the future.

Let’s find that image

docker ps -l
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
da36d1d9481b        6728bc817ad9        "/bin/bash"         20 minutes ago      Up 20 minutes                           grave_car

Commit changes to the container:

sudo docker commit da36d1d9481b openwrt

It feels good to have save that work, doesn’t it?

Add a webinterface

Especially because now we mess with the packages we want to integrate in our OpenWRT distribution.

While I love me some terminal, I think it is really nice to have a web interface to configure some of the basic stuff. So let’s add it, shall we?

he webinterface is called is called LuCI. It is not in the default set of packages and is supported in a separate “feed”.

# Update your feeds
cd ~/openwrt
./scripts/feeds update -a

# Make LuCI available as a package
./scripts/feeds install luci

If you now open make menuconfig. You can add it to installation.

For good measure I throw in a make here, to see if still everything compiles.

Add shairport-sync

Now that we have a webinterface, we try something more experimental

I’m basically following the steps from shairport-sync-for-openwrt. I opted to get bleeding edge and use the development branch, which supports shairport 2.4.

# Update your feeds
cd ~/openwrt
./scripts/feeds update -a

# Create shairport package
cd ~/openwrt/packages
git clone https://github.com/mikebrady/shairport-sync-for-openwrt.git
cd shairport-sync-for-openwrt
git checkout development

# Install packages in base system
cd ~/openwrt
./scripts/feeds install libavahi alsa-lib libdaemon libpopt libsoxr libconfig

Now do make menuconfig and

  1. select Sound > shairport-sync. I made sure its not selected as a module but built in.
  2. select Kernel Modules > Sound Support > kmod-sound-core and kmod-usb-audio

Let’s compile with make.

Fetch the image

After hours of downloading and compiling things, the image is finally ready! .

All we have do is getting it onto the router. But first let’s get it onto the host machine. As my router is already running OpenWRT, I actually just need the upgrade package.

# fetch your container id with `docker ps -l`, in case you forgot
# mine is da36d1d9481b

# copy the sysupgrade
docker cp f9be3e8e90e5:/home/openwrt/openwrt/bin/mvebu/openwrt-mvebu-armada-xp-linksys-mamba-squashfs-sysupgrade.tar .

Flash the router

Now connect to your router with an Ethernet cable (you really don’t want to flash your device over WiFi).


Luckily my old installation also was running LuCI, which offers a interface for uploading a system upgrade.I just quickly deselect the box to keep my settings (I wanted to start fresh).

I upload the image and stare at my monitor for a minute.

Basic configuration

And what a relieve; after a tiny eternity I’m greeted with a login screen for my router with a warning that there is no password for root. I went ahead and set one.

To configure the rest of the services you need SSH access. Add your public SSH key by

cat .ssh/id_rsa.pub | pbcopy

and pasting it into the Dropbear input field. You can restrict SSH access to certain interfaces such as Ethernet cable.


Now that we have ssh access we can configure shairport-sync.

touch /etc/shairport-sync.conf
vim /etc/shairport-sync.conf

I added a very minimal configuration

general = {
    name = "Living Room";

alsa = {
    output_device = "hw:0";

The documentation states that one should specify the mixer, but I haven’t figured out a way how to get this information for my USB sound card and as the sound and response time was good enough for my taste I left it for another time. It might just mean to install alsamixer to read that information.


It was a lot easier than I thought. Yes, I spent almost an entire day making this work, but at least one hour was fighting with Docker and VirtualBox and almost 4 hours were just spent on waiting for the compilation. If I would try it again, I might want to tewak the settings for the virtual machine, and giving it a bit more power before the actual compilation phase. Another idea is to spin up a server from a Cloud service and do the compilation there.