Eduardo Trujillo
4 minutes

For the past few months, I’ve been using Fedora as my main OS on my laptop, a 2014-ish MacBook Pro. OSX is not bad, but it does not have native support for things like Docker.

I still have my OSX partition setup in case I need something that I can’t do on Linux (like updating the firmware or enabling/disabling the startup sound).

These are some of notes I’ve collected along the way:

Writing the ISO to a USB stick

If you are installing Fedora on a MBP, you are probably running OSX initially. How do you write the ISO image into a USB stick and make it bootable?

I’ve found that Ubuntu’s guide on the topic is the easiest one to follow and works with the Fedora image too.

Here’s a quick summary:

hdiutil convert -format UDRW -o ~/path/to/target.img ~/path/to/fedora.iso
diskutil unmountDisk /dev/diskN
sudo dd if=~/path/to/target.img of=/dev/diskN bs=1m
diskutil eject /dev/diskN

See below on how to boot into the live image.

Dual-booting is possible without any additional software

Some guides for installing Linux on MacBooks point you to installing UEFI software like rEFInd. I would consider this an optional step since the laptop’s firmware has an OS selection screen built-in.

For both, booting the live image and picking which OS to boot, all you have to do is hold down the alt key while the laptop is booting and pick what you want to boot. If you formatted the USB stick correctly, Fedora should be an option.

TIP: You can setup a firmware password to protect the OS selection screen (just like it would on a PC).

Going online (wireless-ly)

Like on other laptops on Linux, the wireless card needs some drivers before you can use it.

On Fedora, the MacBook card’s driver is available under the akmod-wl package. However, you will first need to enable the RPM fusion repositories (both free and non-free).

The easiest way to do this is to use a Thunderbolt Ethernet adapter or to tether your phone. Otherwise, you will porbably need to copy a couple of RPMs.

TIP: On some cases, the kernel-headers package will have a different version from the current kernel in your system. This will prevent akmod from compiling the module. You should run dnf upgrade if that happens.

After restarting, the module should be compiled and you should be able to connect to an access point.

Turning off that red light

After booting Fedora (or any other Linux distribution), you will most likely notice that there is an odd red light coming out your laptop’s headphone jack. This is normal. The headphone jack on MacBooks has a S/PDIF port built-in.

The reason why the light is on while using Linux is that, by default, Linux does not enable power saving mode on the audio card. However, its very easy to enable it:

# As root:
echo 1 > /sys/module/snd_hda_intel/parameters/power_save

Additional power saving toggles can be found using powertop.

Go to sleep

For some reason, something keeps waking up the laptop after you press the power button or close the lid. Having you laptop turn on inside your bag is not fun!

The workaround seems to disable a couple of wakeup triggers:

# As root:
echo LID0 > /proc/acpi/wakeup
echo XHC1 > /proc/acpi/wakeup

The side effect being that your laptop won’t turn on automatically when you open the lid, however, you can now safely put it in your bag.

The webcam

The only piece of hardware that you’ll find that does not work on Linux is the webcam. This is due to the fact that the drivers for it are not open source.

There is, however, an ongoing effort to build/reverse-engineer a driver for Linux.

As a systemd unit

I wrote a small unit file that will fix the red light and wake up issues at boot time:

Description=Improve MacBook setup

ExecStart=/bin/bash -c "echo 1 > /sys/module/snd_hda_intel/parameters/power_save && echo LID0 > /proc/acpi/wakeup && echo XHC1 > /proc/acpi/wakeup"


Save this file as /etc/systemd/system/mbp.service and run systemctl daemon-reload && systemctl enable mbp to load the unit on every boot.

Eduardo Trujillo
4 minutes

The last time I wrote a post about my blog was around two years ago. At that type, I was riding on the Node.js hype train, so I was excited to rewrite the engine my blog was running on in JavaScript.

Fast forward to today, and now this blog is just a bunch of statically generated HTML files and images served by an Nginx server.

So what happened?

Side projects. Lots of them.

My blog used to be the go-to hobby project to work on after school. It served as a platform for me to learn about different languages, security practices, and how to structure web applications.

Though, once I got my CS degree, I started working a full-time developer, and, as part of the job, I started learning a lot and writing libraries to make said job easier.

After a while, I had many go-to projects that I could work on during my free time. Working on a project now meant also considering the opportunity cost of not working on other projects, and the blog routinely kept loosing this battle.

However, every now and then, the blog got some attention. Let’s take a look:


  • Pre-2014: Create a CMS from scratch in PHP and use it run a blog and a few other sites.
  • 2014: Jump on the node.js hype train and rebuild a simpler CMS in Javascript while keeping compatibility with the original database schema.
  • Late 2014-Early 2015: Port the views to React.js code while also making the server use isomorphic Javascript.
  • 2015: Extract the backend code into a separate Go API, while also building a Kubernetes cluster to run the front-end and backend containers, plus a small Redis instance for API rate-limitting.

Out of all the things I tried, there was one “feature” that I kept pushing back: A UI to write articles. My blog might have had bleeding-edge JS, an API, and a solid infrastructure, but it still lacked content.

Don’t get me wrong; These were really fun to implement and set up, and I learned things that I later applied to other projects. However, the high friction (posting articles over MySQL) kept getting in the way of content, which is kind of the centerpiece.

Given the reduce availability, I decided to knock down the walls obstructing content, and going for something simpler.

Enter: Hakyll

Jekyll is a very popular tool for generating static sites. It is used by GitHub pages and many blogs out there. However, it is not the only one: There’s Octopress, Hugo, GitBook, and a lot more.

One that caught my eye in particular was Hakyll. It is a very similar tool, written in Haskell, a purely functional language.

Let’s also mention that it’s 2015, and that there’s another hype train going around. This time its about Functional Programming.

So, I decided “if I’m going to create a statically-generated blog, why not do it in style?“. I jumped on another train and started writing my new blog using Hakyll.


Hakyll did not disappoint. It is a really fast alternative to Jekyll that also happens to be very extensible using plain Haskell code. This were my favorite bits on working with it:

  • You are not bound to just creating a blog, you can customize the compilation process to create many different kinds of collections and pages.
  • It backed by Pandoc, meaning that it supports reading posts in many file types, and also rendering them in other formats (like PDFs) besides just HTML.
  • It’s a Haskell script that gets compiled into an executable!
  • It does not require you to know too much about functional programming concepts.
Hakyll is simple and fast, but it's speed and extensibility are the killer features.

In less than a week of working with Hakyll, I had a new blog setup with my previous posts, a projects mini-wiki, and a sweet new design. However, this is probably because I already had some experience with Haskell and the Gulp/SCSS combo.

If you want to take a peek under the hood, the code used for generating this blog can be found on my Phabricator instance. I’m hoping to write a follow-up post on some of the custom snippets in it.

By the way:

  • Always keep your posts in a portable format (like Markdown)! It will make it easier to migrate your posts to other blog engines or static site generators.
  • If you are choosing which static site generator to use, make sure it has good support for syntax highlighting.
  • If you are trying out Hakyll, consider using stack instead of just cabal. You can easily setup a Hakyll project by using their template.
  • Support for LaTeX is also a good thing to look for: 1+2Ω=x+y+z21 + 2 - \Omega = x + y + z^2

Eduardo Trujillo
3 minutes

Metrics in Grafana

I’ve been playing with Deis, a Docker orchestration platform on AWS for the past few days. In fact, I got some services running on it. I have found it to be very useful since it automates a many processes that would actually be a pain to setup manually, such as automatically building Docker images, setting up load balancing across containers and servers, and making it super simple to scale different parts of an application.

However, one key missing part is monitoring. Deis’ documentation points you to Cadvisor and Heapster. These are two applications built by Google in order to collect and store statistics.

Cadvisor is very simple to setup. All you need is to load the service file using fleetctl. Once its running you should be able to see stats about containers running on each server. This is nice, but it does not really give you a complete picture given that it only shows stats for the host it is running on. If you have 5 servers in your cluster, you need to open 5 tabs to see the stats of each server.

Heapster is the next step. It connects to instances of Cadvisor, collects metrics, and then pushes them to a sink, such as an InfluxDB server, every 10 seconds.

Heapster is built with Kubernetes in mind, but luckily it also supports CoreOS which is what Deis runs on. It uses fleet to discover other nodes in the cluster and connect to their instance of the Cadvisor container.

In the past, I had managed to get this working but only for a few minutes. There seemed to be a bug with Heapster that caused it to stop flushing data to InfluxDB after a while. However, I recently tried using the newest version (v0.10.0) and the problem seems fixed now.

Below I’m including the two unit files that I’m using per CoreOS cluster to get this service working. With a few modifications, you should be able to get it running on a Deis cluster or any generic CoreOS cluster.


Note: The following services do not setup either InfluxDB, or Grafana. You will need to install this on a server or set them up using containers, and create a database for Heapster to write to. You can setup the Grafana dashboard by importing this template


No modifications necessary.

Description=cAdvisor Service

ExecStartPre=-/usr/bin/docker kill cadvisor
ExecStartPre=-/usr/bin/docker rm -f cadvisor
ExecStartPre=/usr/bin/docker pull google/cadvisor
ExecStart=/usr/bin/docker run --volume=/:/rootfs:ro \
    --volume=/var/run:/var/run:rw \
    --volume=/sys:/sys:ro \
    --volume=/var/lib/docker/:/var/lib/docker:ro \
    --publish=4194:4194 --name=cadvisor \
    --net=host google/cadvisor:latest \
    --logtostderr \
ExecStop=/usr/bin/docker stop -t 2 cadvisor



Modify the environment variables to match your setup. Also, note that this is not a global service. It is intended to run on only one server. Otherwise you would get duplicate stats.

After=docker.service cadvisor.service
Requires=docker.service cadvisor.service

    "INFLUXDB_NAME=k8s" \

ExecStartPre=-/usr/bin/docker kill heapster1
ExecStartPre=-/usr/bin/docker rm heapster1
ExecStartPre=/usr/bin/docker pull kubernetes/heapster:v0.10.0
ExecStart=/usr/bin/docker run --name heapster1 kubernetes/heapster:v0.10.0 \
    /usr/bin/heapster \
    --sink influxdb \
    --sink_influxdb_host=${INFLUXDB_HOST} \
    --sink_influxdb_name=${INFLUXDB_NAME} \
    --sink_influxdb_username=${INFLUXDB_USERNAME} \
    --sink_influxdb_password=${INFLUXDB_PASSWORD} \
    --coreos \
    --fleet_endpoints=http://${COREOS_PRIVATE_IPV4}:4001 \
ExecStop=/usr/bin/docker stop heapster1

Additional resources:

…or you can find more in the archives.

Copyright © 2015-2021 - Eduardo Trujillo
Except where otherwise noted, content on this site is licensed under a Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) license.
Site generated using Gatsby.