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.
Contents
- Download and Install
- First Boot
- Install pkgng
- Enable IPv6 on All Network Interfaces
- Copy SSH File
- Upgrade Base System
- WiFi
- Shell
- Set Environment Variables
- Behavior of fn+delete
- Command History in tcsh
- Command History in bash
- Firewall
- Jails
- bhyve
- nginx
- Certificate Authority
- OpenVPN
- Router
- Transparent Tor Proxy
- Poudriere
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
OpenVPN
TODO
Router
TODO
Poudriere
TODO: Build packages on Digital Ocean for rpi2