RaspBSD on Raspberry Pi 2 Model B

I bought a Raspberry Pi 2 Model B from CanaKit to install and learn FreeBSD. Fortunately for me (and you) RaspBSD is available.

I found at one point that using an 8GB Micro SD card caused me headaches as I ran out of disk space at some points. I would recommend buying a bigger card.

This guide was created using OS X 10.10 (Yosemite) as the "client". All instructions assume:

  • Correct software installed on OS X

  • You know how to use Disk Utility and CLI

  • You have already assembled the Raspberry Pi (RPi) hardware (case, etc.)

  • WiFi USB adapter is not connected initially (it's configured later)

  • Ethernet, HDMI, keyboard, mouse are connected to Raspberry Pi

  • USB to TTL serial cable is available for use (I bought this one: http://www.amazon.com/gp/product/B00QT7LQ88)

  • Drivers from http://prolificusa.com/pl-2303hx-drivers/

WARNING: This is a "living document" and will be updated as I learn from my mistakes. There may be a lot of errors in this doc so all the standard disclaimers apply.

Download and Install

Download the latest image. It was 292991 at the time of writing.

user@host ~ $ cd ~/Downloads
user@host ~/Downloads $ curl -O http://download.raspbsd.org/FreeBSD-armv6-11.0-RPI2-292991.img.gz
user@host ~/Downloads $ gunzip FreeBSD-armv6-11.0-RPI2-292991.img.gz

Insert a MicroSD card in the card reader and using "Disk Utility" unmount it. Do not eject it.

Get the name of the MicroSD card. On my machine it was /dev/disk2.

user@host ~/Downloads $ diskutil list

Write the RaspBSD image to the MicroSD card (/dev/disk2). WARNING: Be very careful where you're writing the image because you don't want to overwrite any other disk.

user@host ~/Downloads $ diskutil unmountDisk /dev/rdisk2
user@host ~/Downloads $ sudo dd if=FreeBSD-armv6-11.0-RPI2-292991.img of=/dev/rdisk2 bs=1m && sync

When the image has been written, unmount and eject in Disk Utility.

user@host ~/Downloads $ diskutil eject /dev/rdisk2

Insert the MicroSD card in the RPi.

First Boot

Power on RPi and let it boot up to the login prompt. Login as user raspberry and password raspberry.

% uname -a
FreeBSD rpi2 11.0-CURRENT FreeBSD 11.0-CURRENT #0 r292991: Fri Dec  4 16:49:10 MST 2015     brd@hive.raspbsd.org:/usr/local/raspbsd/obj/RPI2/obj/arm.armv6/usr/local/raspbsd/src/common/sys/RPI2  arm

Install pkgng

su as user root and install pkgng. You'll not be asked for root password.

% su -
root@rpi2:~ # pkg update
The package management tool is not yet installed on your system.
Do you want to fetch and install it now? [y/N]: y

Since pkgng is not installed you'll be prompted to install it. Say yes.

root@rpi2:~ # pkg update
root@rpi2:~ # pkg upgrade

Install useful tools.

root@rpi2:~ # pkg install tmux vim-lite

Enable IPv6 on All Network Interfaces

My network has IPv6 support but the interface didn't show an IPv6 address in use.

root@rpi2:~ # ifconfig
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
    options=600003<RXCSUM,TXCSUM,RXCSUM_IPV6,TXCSUM_IPV6>
    inet6 ::1 prefixlen 128
    inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1
    inet 127.0.0.1 netmask 0xff000000
    groups: lo
    nd6 options=21<PERFORMNUD,AUTO_LINKLOCAL>
ue0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
    options=80001<RXCSUM,LINKSTATE>
    ether ab:ab:ab:01:01:01
    inet 10.1.1.29 netmask 0xffffff00 broadcast 10.1.1.255
    media: Ethernet autoselect (100baseTX <full-duplex>)
    status: active
    nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>

Here ue0 is my Ethernet interface. As is visible, there's no IPv6 address.

Add the following lines to /etc/rc.conf

ipv6_activate_all_interfaces="YES"
ifconfig_ue0_ipv6="inet6 accept_rtadv"
rtsold_enable="YES"

Restart networking. If you're connected by SSH your session may be dropped. You can use a tmux or screen session to do this for you.

root@rpi2:~ # service netif restart

Now you should see IPv6 addresses on l0 and ue0 interfaces. If you don't, I found reboot-ing worked.

root@rpi2:~ # ifconfig
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
    options=600003<RXCSUM,TXCSUM,RXCSUM_IPV6,TXCSUM_IPV6>
    inet6 ::1 prefixlen 128
    inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1
    inet 127.0.0.1 netmask 0xff000000
    groups: lo
    nd6 options=23<PERFORMNUD,ACCEPT_RTADV,AUTO_LINKLOCAL>
ue0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
    options=80001<RXCSUM,LINKSTATE>
    ether ab:ab:ab:01:01:01
    inet6 fe80::abab:abff:fe01:0101%ue0 prefixlen 64 scopeid 0x2
    inet6 2601::abab:abff:fe01:0101 prefixlen 64 autoconf
    inet6 fd85::abab:abff:fe01:0101 prefixlen 64 autoconf
    inet 10.1.1.29 netmask 0xffffff00 broadcast 10.1.1.255
    media: Ethernet autoselect (100baseTX <full-duplex>)
    status: active
    nd6 options=23<PERFORMNUD,ACCEPT_RTADV,AUTO_LINKLOCAL>

Enable IPv6 Privacy Features

An important privacy feature in IPv6 is to create and use temporary addresses. They are disabled by default and you need to enable them.

First check the current status.

root@rpi2:~ # sysctl net.inet6.ip6.use_tempaddr
net.inet6.ip6.use_tempaddr: 0
root@rpi2:~ # sysctl net.inet6.ip6.prefer_tempaddr
net.inet6.ip6.prefer_tempaddr: 0

Set new values to enable use of temporary addresses.

root@rpi2:~ # sysctl net.inet6.ip6.use_tempaddr=1
net.inet6.ip6.use_tempaddr: 0 -> 1
root@rpi2:~ # sysctl net.inet6.ip6.prefer_tempaddr=1
net.inet6.ip6.prefer_tempaddr: 0 -> 1

Restart networking. You should see new IPv6 addresses on ue0 interface after the OS deals with the Route Advertisement and configures its IPv6 addresses.

root@rpi2:~ # service netif restart
root@rpi2:~ # sleep 120
root@rpi2:~ # ifconfig
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
    options=600003<RXCSUM,TXCSUM,RXCSUM_IPV6,TXCSUM_IPV6>
    inet6 ::1 prefixlen 128
    inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1
    inet 127.0.0.1 netmask 0xff000000
    groups: lo
    nd6 options=23<PERFORMNUD,ACCEPT_RTADV,AUTO_LINKLOCAL>
ue0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
    options=80001<RXCSUM,LINKSTATE>
    ether ab:ab:ab:01:01:01
    inet6 fe80::abab:abff:fe01:0101%ue0 prefixlen 64 scopeid 0x2
    inet6 2601::abab:abff:fe01:0101 prefixlen 64 autoconf
    inet6 2601::a897:3f8c:c8b0:dab9 prefixlen 64 tentative autoconf temporary
    inet6 fd85::abab:abff:fe01:0101 prefixlen 64 autoconf
    inet6 fd85::a897:3f8c:c8b0:dab9 prefixlen 64 tentative autoconf temporary
    inet 10.1.1.29 netmask 0xffffff00 broadcast 10.1.1.255
    media: Ethernet autoselect (100baseTX <full-duplex>)
    status: active
    nd6 options=23<PERFORMNUD,ACCEPT_RTADV,AUTO_LINKLOCAL>

When you're sure everything's working as intended, you should make these changes permament by adding these lines to /etc/sysctl.conf.

net.inet6.ip6.use_tempaddr=1
net.inet6.ip6.prefer_tempaddr=1

Now when you reboot these changes will stick.

Copy SSH File

From your OS X (or Linux) box make it easy to login without entering your password all the time.

$ ssh-copy-id -i ~/.ssh/id_rsa 10.1.1.29

Of course, use the IP of your RPi2 box.

Upgrade Base System

TODO: What's the ideal way to upgrade the base system? Is it freebsd-update fetch install? That doesn't work yet.

WARNING: As of January 20, 2016, there is a blocking issue with upgrading from source on RPi2. Follow the discussion on FreeBSD-ARM mailing list: make buildworld failed with error "relocation truncated to fit: R_ARM_JUMP24 against symbol _fini'". I tested with revision 294969 (January 30) and it was working again. Apparently, it's an issue with adding support for clang 3.8.0 and it will fail again when clang 3.8.0 is imported to base.

Download Sources

$ su -
# pkg update
# pkg install ca_root_nss
# which -a svnlite
/usr/bin/svnlite
# svnlite checkout svn://svn.freebsd.org/base/head /usr/src

Possible Errors and Fix

Although I do not recommend it, if instead of svn you use https or http,

# svnlite checkout https://svn.freebsd.org/base/head /usr/src

You may run into these errors:

  • svn: E120108: Error retrieving REPORT: The server unexpectedly closed the connection.

  • svn: E000054: Error retrieving REPORT: Connection reset by peer

  • svn: E120106: ra_serf: The server sent a truncated HTTP response body.

I ran into these frequently. The only option I found was to try again and again with almost no success.

# cd /usr/src
# svnlite cleanup && TMP=/usr/home/raspberry/ svnlite up
# TMP=/usr/home/raspberry/ svnlite checkout http://svn.freebsd.org/base/head /usr/src

After trying the above for countless times and failing, another option was to use git instead of svnlite. I have not tested this myself.

# pkg install git
# cd /usr/src
# svnlite cleanup
# rm -rf ./*
# rm -rf ./.svn
# git svn init svn://svn.freebsd.org/base/head
# git svn fetch -r294105
# git svn rebase

At the time of writing HEAD was at revision 294105. In the above example, I chose to fetch svn history starting from the most recent revision. You should browse to FreeBSD svn to get the latest revision number. The reason is you want the initial download to not be too large by avoiding downloading complete svn history. git svn rebase is the equivalent of svnlite update so you'll be able to keep up with updates/revisions in future.

More information from:

Build Toolchain

# chflags -R noschg /usr/obj/*
# rm -rf /usr/obj
# cd /usr/src
# svnlite update

Based on freebsd-arm post by Andreas Schwarz you may optionally want to disable debug files, expecially when like me you're using an 8GB storage card or you may find you have run out of disk space.

# echo WITHOUT_DEBUG_FILES="YES" >> /etc/src.conf

Create /etc/make.conf file ...:

# vim /etc/make.conf

...with these contents:

CC=clang
CXX=clang++
CPP=clang-cpp
WRKDIRPREFIX=/tmp
WITH_PKGNG=yes

I encountered an issue making buildworld and filed a bug. Workaround is to make sure CPUTYPE?=native is not in /etc/make.conf.

Make buildworld (compiler toolchain). This will take a long time to run on RPi2 (about 36 hours on my machine).

# make buildworld

Build Kernel

# cd /usr/src
# make buildkernel

Install Kernel

# cd /usr/src
# make installkernel

Install World

The handbook instructs to drop into single-user mode.

# shutdown now

Instead of showing a prompt the output "hung" and pressing enter did not yield a response. However, when I ran ctrl+alt+del the machine was rebooted.

I found through a mailing list post that I need to have serial console access to proceed. This is where the USB to TTL serial console cable comes into play.

Configure for Serial Console Access

Download Mac OS X drivers from http://prolificusa.com/pl-2303hx-drivers/ and install them on your Mac. Reboot.

Power off RPi2 and disconnect the power cable.

DO NOT attach both the power cable and the serial cable simultaneously or your RPi2 will fry.

Connect the USB-to-TTL cable to RPi2 following the RPi2 GPIO docs.

  • Red end of the cable connects to RED pin on top left (see diagram in docs)

  • Leave the second RED pin empty

  • Black end connects to first BLACK pin on top row from the left (in the diagram) next to the empty red pin

  • White end connects to pin 14

  • Green end connects to pin 15

Connect the USB end of the cable to the Mac's USB port. Use screen to connect to the serial console like so

# ls -alhtr /dev/tty.*
crw-rw-rw-  1 root  wheel   18,   2 Dec 14 18:22 /dev/tty.Bluetooth-Modem
crw-rw-rw-  1 root  wheel   18,   0 Dec 14 18:22 /dev/tty.Bluetooth-Incoming-Port
crw-rw-rw-  1 root  wheel   18,   4 Dec 14 18:25 /dev/tty.usbserial
# screen /dev/tty.usbserial 115200

You'll see the RPi2 booting up.

Use Serial Console for Next Steps

Login as root and then immediately go into single user mode.

# shutdown now
Shutdown NOW!
shutdown: [pid 775]
#
*** FINAL System shutdown message from root@rpi2 ***

System going down IMMEDIATELY



System shutdown time has arrived
Dec 15 02:23:50 rpi2 shutdown: shutdown by root:
Stopping cron.
Waiting for PIDS: 717.
Stopping sshd.
Waiting for PIDS: 713.
Stopping powerd.
Waiting for PIDS: 663.
Stopping ntpd.
Waiting for PIDS: 660.
Stopping casperd.
Waiting for PIDS: 607.
Stopping rtsold.
Stopping devd.
Waiting for PIDS: 393.
Writing entropy file:.
Writing early boot entropy file:.
.
TerminaDec 15 02:23:54 rpi2 syslogd: exiting on signal 15
Enter full pathname of shell or RETURN for /bin/sh:
#
# adjkerntz -i
# mergemaster -p
# cd /usr/src
# make installworld

This will install new world and system binaries from /usr/obj and will take a long time to complete.

Final Cleanup

While still connected through the serial cable, complete the final cleanup.

# cd /usr/src
# mergemaster -iF
# make delete-old
# reboot

After the reboot you may power off your RPi2, disconnect serial cable, and plug in the power cable.

WiFi

I have the CanaKit WiFi dongle (CKXW1000). Inserting it and running ifconfig shows the driver (run0) it's using.

root@rpi2:~ # ifconfig | grep -B3 -i wireless
run0: flags=8802<BROADCAST,SIMPLEX,MULTICAST> metric 0 mtu 2290
    ether de:de:de:22:22:22
    media: IEEE 802.11 Wireless Ethernet autoselect (autoselect)

Create or edit /etc/wpa_supplicant.conf and add these lines. Change the SSID and PSK to match your enviornment.

network={
    ssid="myssid"
    psk="mypsk"
}

Add these lines to /etc/rc.conf

wlans_run0="wlan0"
ifconfig_wlan0="WPA SYNCDHCP"

Enable IPv6 SLAAC by adding this line to /etc/rc.conf

ifconfig_wlan0_ipv6="inet6 accept_rtadv"

Restart network.

root@rpi2:~ # service netif restart

Note: I had better luck restarting the RPi, shutdown -r now

kldstat had the following output

root@rpi2:~ # kldstat
Id Refs Address    Size     Name
1   20 0xc0100000 784e20   kernel
2    1 0xc42f2000 20000    if_run.ko
3    4 0xc4312000 51000    wlan.ko
4    1 0xc436d000 b000     firmware.ko
5    1 0xc438d000 b000     uhid.ko
6    1 0xc4261000 a000     wlan_wep.ko
7    1 0xc42cc000 b000     wlan_tkip.ko
8    1 0xc43c3000 e000     wlan_ccmp.ko

Scan for available networks

root@rpi2:~ # ifconfig wlan0 scan
SSID/MESH ID    BSSID              CHAN RATE   S:N     INT CAPS
HOME            ff:ff:ff:3a:3a:3a    1   54M -31:-61  100 EP   RSN HTCAP WPS WPA WME
xfinitywifi     d8:d8:d8:a9:a9:a9    6   54M -36:-67  100 ES   HTCAP WME ATH
myssid          b5:b5:b5:c0:c0:c0   11   54M -21:-43  100 EPS  RSN HTCAP WME

Shell

In my image the default shell for the user was tcsh, even though the documentation states that it should be sh. To find your shell, run echo $SHELL.

Available Shells

To view a list of available shells, cat /etc/shells.

% cat /etc/shells
# $FreeBSD: head/etc/shells 59717 2000-04-27 21:58:46Z ache $
#
# List of acceptable shells for chpass(1).
# Ftpd will not allow users to connect who are not using
# one of these shells.

/bin/sh
/bin/csh
/bin/tcsh

Change Shell

To change your shell, use chsh. You'll be prompted to enter your user's password.

% /usr/bin/chsh -s /bin/sh

Log out and log back in to see that you're using sh.

$ echo $SHELL
/bin/sh

I'm trying to learn tcsh so sometimes I change my shell to use that. Whenever you see $ prompt it means I'm using sh/bash, when you see > prompt it means I'm using tcsh.

% /usr/bin/chsh -s /bin/tcsh

For my daily driver I like to use bash

# pkg install bash
# echo "fdesc   /dev/fd         fdescfs         rw      0       0" >> /etc/fstab
# mount -t fdescfs fdesc /dev/fd
$ /usr/bin/chsh -s /usr/local/bin/bash

Set Environment Variables

There are some important environment variables (env vars) you'll want to either set for the first time or modify to suit your requirements.

Since we changed the shell to sh in the previous step, we can use export to modify env vars.

To view all env vars, use env.

$ env
SSH_CLIENT=10.1.1.33 62976 22
LOGNAME=raspberry
PAGER=
MAIL=/var/mail/raspberry
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/usr/home/raspberry/bin
EDITOR=
OLDPWD=/etc/defaults
PWD=/usr/home/raspberry
TERM=xterm-256color
SSH_TTY=/dev/pts/0
HOME=/usr/home/raspberry
USER=raspberry
LC_COLLATE=C
SSH_CONNECTION=10.1.1.33 62976 10.1.1.29 22
SHELL=/bin/sh
BLOCKSIZE=K

EDITOR

Check what the EDITOR env var is currently set to, and you'll notice it's empty.

$ echo $EDITOR

Set this env var to use nano (installed earlier) to be the default editor of choice.

$ export EDITOR="/usr/local/bin/vim"
$ echo $EDITOR
/usr/local/bin/vim

PAGER

Check what the PAGER env var is currently set to, and you'll notice it's empty.

$ echo $PAGER

When it's not set the default pager is more.

I prefer to use less as my pager so I'll set it.

$ export PAGER="/usr/bin/less"
$ echo $PAGER
/usr/bin/less

Behavior of fn+delete

Mac Keyboard doesn't have a forward delete key the way generic keyboards do. Instead, the key combination fn+delete is used to do the same thing. This does not work as expected in csh or tcsh. Instead of forward deleting a character, a tilde (~) is printed.

For example, in the text below, I typed fn+delete and got a tilde instead.

% hel~o

I found a solution on Terminal on Mac - Delete key behavior. Use bindkey to map a sequence to delete-char.

% bindkey "^[[3~" delete-char

Now when you type fn+delete forward delete should work. To make it permanent, add the same command to ~/.cshrc file.

% echo 'bindkey "^[[3~" delete-char' >> ~/.cshrc

Some useful resources:

Command History in tcsh

It appears csh doesn't save history in a file to use across sessions. But tcsh does support the feature. Add these lines to your ~/.cshrc file.

set history=2500
set savehist=5000
set histfile=~/.tcsh_history

history stores commands for the current session. savehistory stores them across sessions.

Thanks to tofutim for this tip.

Command History in bash

Add these lines to your ~/.bash_profile file

HISTFILESIZE=1500
HISTSIZE=750
HISTCONTROL=ignoreboth

Then source the file to activate the settings.

$ source ~/.bash_profile

Firewall

I'll configure the pf (Packet Filter) firewall with a few rules.

I found out about sysrc, a tool to safely configure settings in /etc/rc.conf. Instead of editing the file by hand I'll use that.

To learn more about pf read Firewalling with PF.

To learn more about pf in FreeBSD read the pf chapter in the Handbook.

Instead of me explaining all the tiny details of the rule set you should read the aforementioned resources. I'll add comments only to explain a tiny bit of what's going on.

Enable pf in /etc/rc.conf

# sysrc pf_enable="YES"
pf_enable: NO -> YES
# sysrc pf_rules="/etc/pf.conf"
pf_rules: /etc/pf.conf -> /etc/pf.conf
# sysrc pflog_enable="YES"
pflog_enable: NO -> YES
# sysrc pflog_logfile="/var/log/pflog"
pflog_logfile: /var/log/pflog -> /var/log/pflog

Start pf without configuring any rules. You'll see a warning because /etc/pf.conf does not exist but it can be ignored for now.

# service pf start
/etc/rc.d/pf: WARNING: /etc/pf.conf is not readable.
# service pflog start
Starting pflog.

Create these rules in /etc/pf.conf

ext_if = "ue0"
localnet = "{ self }"
# add any /64 subnet(s) you have configured on your network
# just for fun I don't allow SSH over IPv4
adminnet = "{ fc00::/7, fe80::/10 }"
# edit the list to exclude your internal network otherwise you may not be able to SSH in
martians = "{ 127.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8, 169.254.0.0/16, 192.0.2.0/24, 0.0.0.0/8, 240.0.0.0/4 }"
client_out = "{ ssh, domain, nntp, http, https }"
admin_services = "{ ssh }"
udp_services = "{ domain, ntp }"
icmp_types = "{ echoreq, unreach }"
http_servers = "{ 159.203.215.175 }"
http6_servers = "{ 2604:a880:1:20::fa:4001 }"

# could use *drop* instead of *return* to provide no feedback
set block-policy return

match in all scrub (no-df max-mss 1440)

antispoof for $ext_if

block all

set skip on {lo0}

block drop in quick on $ext_if from $martians to any
block drop out quick on $ext_if from any to $martians

pass quick inet proto { tcp, udp } to port $udp_services keep state

# allow ping and MTU path discovery
pass inet proto icmp all icmp-type $icmp_types

# allow outbound traceroute
pass out on $ext_if inet proto udp to port 33433 >< 33626

# only allow ingress SSH over IPv6 from a specific subnet
pass in inet6 proto tcp from $adminnet to any port $admin_services

#
# IPv6 related rules
#

pass out on $ext_if inet6 proto icmp6 all icmp6-type echoreq keep state

pass out on $ext_if inet6 proto icmp6 all icmp6-type {neighbradv, neighbrsol}

# ND advertisement in
pass in on $ext_if inet6 proto icmp6 all icmp6-type {neighbradv, neighbrsol}

# Router advertisement out
pass out on $ext_if inet6 proto icmp6 all icmp6-type routeradv

# Router solicitation in
pass in on $ext_if inet6 proto icmp6 all icmp6-type routersol

Now enable these rules using the pfctl command

# pfctl -f /etc/pf.conf

Use tcpdump to read pf logs. Thanks to how to read pflog logs? for the tip.

# tcpdump -netttr /var/log/pflog

Jails

TODO

bhyve

TODO

nginx

Install nginx and enable it

# pkg update
# pkg install nginx
# sysrc nginx_enable="YES"

Configure nginx.

# vim /usr/local/etc/nginx/nginx.conf

Leave everything as is, except the following. Comment out server directives as well.

user www www;
worker_processes  4;
error_log  logs/error.log  info;
pid        logs/nginx.pid;

events {
    worker_connections  1024;
}

http {
    ...
    include /usr/local/etc/nginx/conf.d/*.conf;
    ...
    #server {
    #    ...
    #}
    ...
}

Create an example server config (edit it as you like)

# mkdir -p /usr/local/etc/nginx/conf.d/
# vim /usr/local/etc/nginx/conf.d/example_com.conf

The config should look something like this.

server {
        listen 10.1.1.29:80 default_server;
        listen [2601::abab:abff:fe01:0101]:80 default_server;
        listen [fd85::abab:abff:fe01:0101]:80 default_server;

        server_name .example.com "";

        root /var/www/example.com;
        index index.html index.htm;

        location / {
            # First attempt to serve request as file, then
            # as directory, then fall back to displaying a 404.
            try_files $uri $uri/ =404;
            # Uncomment to enable naxsi on this location
            # include /etc/nginx/naxsi.rules
        }
}

Create directory for logs

# mkdir -p /usr/local/etc/nginx/logs/
# chown www:www /usr/local/etc/nginx/logs/

Create directories to place the website's files.

# mkdir -p /var/www/example.com
# chown raspberry:www /var/www/example.com
# chmod 755 /var/www/example.com

Verify the nginx config is valid.

# nginx -t

You may see the following error but you can safely ignore it. It's documented at nginx error after upgrading. You just need to restart nginx to make this error go away. Do not ignore other errors, though.

nginx: [emerg] mkdir() "/var/tmp/nginx/client_body_temp" failed (2: No such file or directory)

Restart nginx.

# service nginx restart

Certificate Authority

TODO

OpenVPN

TODO

Router

TODO

Transparent Tor Proxy

TODO

Poudriere

TODO: Build packages on Digital Ocean for rpi2