Turning a Raspberry Pi into a UniFi controller appliance en

By ERIKvanPAASSEN on Friday 7 March 2014 00:08 - Comments (68)
Category: UniFi, Views: 113.882





Last updated: 25 september 2014

A few months ago, a hard-float version of the Oracle Java 7 JDK for Raspbian has been released, opening up lots of new opportunities for the Raspberry Pi platform. This blog post will show you how to turn the Pi into a wifi controller appliance for the - almost equally affordable - UniFi access points from Ubquiti, which are becoming increasingly popular.

I'll be using a Raspberry Pi, model B, revision 2.0 which has 512 megabytes of RAM available. Keep in mind that this is just a proof of concept. I have not tested the setup very thoroughly and running the UniFi controller software on Linux is not officially supported, as far as I know. That being said, I haven't ran into any problems yet.

Update 25 september 2014: I'm using the Raspberry Pi to control three UniFi access points for over half a year now and it seems to be rock-solid.

http://tweakers.net/ext/f/qs7hNCPb2zKBY1DHi0Izedxk/full.jpg
Screenshot of the UniFi controller web interface.

Raspbian setup

The first step in the process will be getting your Pi up and running with Raspbian. You can download an image on the Raspberry Pi downloads page. If you don't know how to write the image to your SD card, I'd recommend you to take a look at the wiki page on this topic. After you've flashed Raspbian to the card, it's time to boot up your Pi and log in with username pi and password raspberry.

Raspbian configuration

Now we still need to expand the filesystem to the size of the SD card, so we'll be able to use all the space provided by the card. To do so, run:

sudo raspi-config

Choose Expand Filesystem to adjust the filesystem size. While we're in this neat little configuration tool, you might also want to adjust the amount of RAM assigned to the graphics adapter. You can do so by choosing Advanced Options > Memory split As we're running headless, 4MB would be more than enough. Reboot the device for the changes to take effect.

sudo reboot

Installing the UniFi software and its dependencies

The UniFi software package has two dependencies: Oracle Java, as stated earlier, and MongoDB. The Oracle Java 7 JDK is provided by the 'oracle-java7-jdk' package, which is installed by default on new versions of Raspbian. If you are not sure Java is installed on your device, just tell your package manager to deal with it.

sudo apt-get update && sudo apt-get install oracle-java7-jdk

Now it's time to fulfill the second requirement, MongoDB. Normally, this would be quite a pain since the ARM platform is not supported by it. Fortunately, Per Ola Ingvarsson created a non-x86 version and put it online. Using this guide, you could compile it and move on. But it will take hours and hours on your Raspberry, so I'll be using the lazy approach and just fetch some precompiled binaries from GitHub, thanks to Brice Morin. If you're not experienced with compiling software, I'd recommend you to the same. Create the directory '/opt/mongodb', download the binaries (found in 'mongo/bin' in the GitHub repository) to it and make them executable.

sudo mkdir /opt/mongodb
cd /opt/mongodb
sudo wget https://github.com/brice-morin/ArduPi/blob/master/mongodb-rpi/mongo/bin/bsondump?raw=true -O bsondump
sudo wget https://github.com/brice-morin/ArduPi/blob/master/mongodb-rpi/mongo/bin/mongo?raw=true -O mongo
sudo wget https://github.com/brice-morin/ArduPi/blob/master/mongodb-rpi/mongo/bin/mongod?raw=true -O mongod
sudo wget https://github.com/brice-morin/ArduPi/blob/master/mongodb-rpi/mongo/bin/mongodump?raw=true -O mongodump
sudo chmod +x *

Finally, it's time to download the UniFi software. I'll be using version 2.4.6, which is available from this UniFi update announcement. You'll need to accept a license agreement, so I can't provide you with a direct download link. I downloaded the package on my desktop machine and transferred it to the home directory of the pi user using SFTP. Unzip the package and move the extracted directory into /opt.

unzip UniFi.unix.zip
sudo mv UniFi /opt/UniFi

Next, we'll need to tell the controller software where it can find MongoDB. This is an easy one, as we'll only need to replace a symlink.

cd /opt/UniFi/bin
sudo ln -fs /opt/mongodb/mongod mongod

At last, we're ready to fire up the controller!

sudo java -jar /opt/UniFi/lib/ace.jar start

Or start it in the background by running:

sudo java -jar /opt/UniFi/lib/ace.jar start &

If all went well, you should now be able admire the web interface of your super-awesome UniFi controller appliance through HTTPS on port 8443. To observe the log while the controller is running in the background, use:

sudo tail -f /opt/UniFi/logs/server.log

To stop the controller again:

sudo java -jar /opt/UniFi/lib/ace.jar stop


P.S. You're running server as 'root'. This won't be a good idea for production use, but using a Raspberry Pi for production will probably never be a good idea anyway. However, if you'd set permissions on the 'data' and 'log' subdirs of the UniFi directory, nothing would stop you from running the server as a different user.

Update March 15th, 2014: Running the controller on startup

To run the UniFi controller when the Raspberry boots, we'll need a startup script. Fortunately, there's a Debian package mentioned in the release announcement and this package contains such a script.

By default, the script would assign a memory limit of 1024M to the controller, which I adjusted to 384M. I don't know yet if this would suffice, but it'll be easy to change anyway. I also changed the base dir path and the Java home.

So, create the file /etc/init.d/unifi with the following contents:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#!/bin/bash
#
# /etc/init.d/UniFi -- startup script for Ubiquiti UniFi
#
#
### BEGIN INIT INFO
# Provides:          unifi
# Required-Start:    $local_fs $remote_fs $network
# Required-Stop:     $local_fs $remote_fs $network
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Ubiquiti UniFi
# Description:       Ubiquiti UniFi Controller
### END INIT INFO

NAME="unifi"
DESC="Ubiquiti UniFi Controller"

BASEDIR="/opt/UniFi"
MAINCLASS="com.ubnt.ace.Launcher"

PIDFILE="/var/run/${NAME}/${NAME}.pid"
PATH=/bin:/usr/bin:/sbin:/usr/sbin

JAVA_HOME=/usr/lib/jvm/jdk-7-oracle-armhf
# JSVC - for running java apps as services
JSVC=`which jsvc`
#JSVC_OPTS="-debug"
JSVC_OPTS="${JSVC_OPTS}\
 -home ${JAVA_HOME} \
 -cp /usr/share/java/commons-daemon.jar:${BASEDIR}/lib/ace.jar \
 -pidfile ${PIDFILE} \
 -procname ${NAME} \
 -outfile SYSLOG \
 -errfile SYSLOG \
 -Djava.awt.headless=true -Xmx384M"

[ -f /etc/default/rcS ] && . /etc/default/rcS
. /lib/lsb/init-functions

[ -d /var/run/${NAME} ] || mkdir -p /var/run/${NAME}
cd ${BASEDIR}

is_not_running() {
        start-stop-daemon --test --start --pidfile "${PIDFILE}" \
                --startas "${JAVA_HOME}/bin/java" >/dev/null
        RC=$?
        return ${RC}
}

case "$1" in
        start)
                log_daemon_msg "Starting ${DESC}" "${NAME}"
                if is_not_running; then
                        ${JSVC} ${JSVC_OPTS} ${MAINCLASS} start
                        sleep 1
                        if is_not_running; then
                                log_end_msg 1
                        else
                                log_end_msg 0
                        fi
                else
                        log_progress_msg "(already running)"
                        log_end_msg 1
                fi
        ;;
        stop)
                log_daemon_msg "Stopping ${DESC}" "${NAME}"
                if is_not_running; then
                        log_progress_msg "(not running)"
                else
                        ${JSVC} ${JSVC_OPTS} -stop ${MAINCLASS} stop
                fi
                log_end_msg 0
        ;;
        status)
                status_of_proc -p ${PIDFILE} unifi unifi && exit 0 || exit $?
        ;;
        restart|reload|force-reload)
                if ! is_not_running ; then
                        if which invoke-rc.d >/dev/null 2>&1; then
                                invoke-rc.d ${NAME} stop
                        else
                                /etc/init.d/${NAME} stop
                        fi
                fi
                if which invoke-rc.d >/dev/null 2>&1; then
                        invoke-rc.d ${NAME} start
                else
                        /etc/init.d/${NAME} start
                fi
        ;;
        *)
                log_success_msg "Usage: $0 {start|stop|restart|reload|force-reload}"
                exit 1
        ;;
esac

exit 0


This script uses jsvc, which we'll need to install. Run:
sudo apt-get update && sudo apt-get install jsvc

Enable execution of the script and make it run at boot time:
sudo chmod +x /etc/init.d/unifi
sudo update-rc.d unifi defaults

You can now start your controller using your new script, or just reboot your Pi.
sudo /etc/init.d/unifi start

Follow-up

Jimmy Selgen Nielsen wrote an excellent follow-up tutorial about running the controller under it's own user. It's really worth reading. You can find it here. (Update 28 april 2015: Link has been restored.)

Setting up log rotation should not be necessary, as the UniFi controller has log rotation built in.

You might also want to hang onto this topic in the Ubiquiti forums.