---
title: "Handmade NAS"
url: https://www.truenas.com/blog/handmade-nas/
breadcrumb: ["Home", "Handmade NAS"]
date_published: 2013-04-26
date_modified: 2026-07-02
author: "TrueNAS Team"
source: html
generator: tn-md-extract/1
---

[Home](https://www.truenas.com) › **Handmade NAS**

### What you will learn…

- FreeBSD setup with ZFS as root filesystem
- Basic file sharing with smb, afp and ftp server
- Use these as building blocks for a full fledge NAS

### What you should know…

- FreeBSD installation
- Network configuration
- Port installation & configuration

## Introduction

In the past, the term NAS (Network Attached Storage) was used to be associated with large expensive network and enterprise luxurious fileserver. At that time, who would have thought that hard disk and RAM becomes a common commodity that usual folks like us will posses.

With the well known FreeNAS distribution, building a multi terabytes NAS seems to be trivial affair. But today, we’re going to talk about how to build a NAS by hand, plugging different components together using command line & FreeBSD. This article does not aims to be a comprehensive guide to build a NAS. Rather, serves as a jumpstart guide on what are the basic components that made up of a NAS. Which then the reader can plug in more components to enrich its features.

The components of the NAS we’re going to build, involves:

- FreeBSD server – setup with ZFS as root filesystem using mirroring configuration
- Samba – file server targeting windows client
- AFP through Netatalk – file server (with time machine) targeting mac
- FTP server – generic file sharing server
- pf – packet filtering as firewall

Scenario requirements for this guide:

- modern INTEL / AMD processor
- at least 2gb of ram
- at least 2 hard disk
- megabit NIC or better

### Disclaimer:

This guide is a walkthrough on setting up a NAS and it WILL destroy your existing data on the hard disk. You have been warned.

## FreeBSD server setup

As of this writing, the FreeBSD default installer does not support ZFS filesystem layout. As such, we’ll have to hand craft the ZFS layout during the installation of FreeBSD 9.1 server. Which would allow us to learn more about the underlying works of FreeBSD root on ZFS filesystem.

First of all, get the installation CD from *http://freebsd.org* and boot it. Perform the installation as usual until the menu “Partitioning”, choose “Shell”.

After dropping to the shell, execute the following commands:

*Listing 1a. FreeBSD root on ZFS*

> \# gpart create -s gpt da0
>
> \# gpart add -t freebsd-boot -s 128 -l primary-boot da0
>
> \# gpart add -t freebsd-swap -l primary-swap -s 4g da0
>
> \# gpart add -t freebsd-zfs -l primary-root da0
>
> \# gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 da0
>
> \# gpart create -s gpt da1
>
> \# gpart add -t freebsd-boot -s 128 -l secondary-boot da1
>
> \# gpart add -t freebsd-swap -l secondary-swap -s 4g da1
>
> \# gpart add -t freebsd-zfs -l secondary-root da1
>
> \# gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 da1
>
> \# zpool create -f -m none -o altroot=/mnt -o cachefile=/tmp/zpool.cache zeetank mirror gpt/primary-root gpt/secondary-root
>
> \# zfs create -o mountpoint=/ zeetank/root
>
> \# zpool set bootfs=zeetank/root zeetank
>
> \# zfs list
>
> \# zpool get all zeetank
>
> Type “exit” at the command prompt once the above is done. The installation should continue, extracting the necessary files to the hard disk.

What the above has done is to create a GPT partition table in the very first step. Then follow by creating a partition of size 128 Kilobytes for boot partition and a size of 4 Gigabytes for swap partition (based on 2GB ram). The rest of the hard disk space is allocated to the data area, root partition.

After the partitioning of the hard disk is done, initialize the boot area with boot code found in */boot/gptzfsboot*.

The same disk layout is duplicated to the second hard disk, in order to create ZFS mirroring disk setup.

Next, create ZFS pool with the name of “zeetank”, consisting the partition “primary-root” & ”secondary-root” with ZFS mirroring. Then create a mount point to hold the root partitions and set the boot partitions to find “zeetank/root” for necessary booting files. Finally, list the disk layout we just did and double confirm it.

If typo or any misconfiguration of the disk layout happened, use the following commands for disk partition to delete and/or destroy:

> \# gpart delete -i 1 da0
>
> \# gpart destroy da0

The completed disk layout should look like this:

*Listing 1b. Sample of partition layout*

> root@handmade-nas:/root \# gpart show
>
> =\> 34 20971453 da0 GPT (10G)
>
> 34 128 1 freebsd-boot (64k)
>
> 162 1048576 2 freebsd-swap (512M)
>
> 1048738 19922749 3 freebsd-zfs (9.5G)
>
> =\> 34 20971453 da1 GPT (10G)
>
> 34 128 1 freebsd-boot (64k)
>
> 162 1048576 2 freebsd-swap (512M)
>
> 1048738 19922749 3 freebsd-zfs (9.5G)

*Listing 1c. Sample of ZFS setup*

> root@handmade-nas:/root \# zpool get all zeetank
>
> NAME PROPERTY VALUE SOURCE
>
> zeetank size 9.44G –
>
> zeetank capacity 0% –
>
> zeetank altroot /mnt local
>
> zeetank health ONLINE –
>
> zeetank guid 7047189317064599296 default
>
> zeetank version 28 default
>
> zeetank bootfs zeetank/root local
>
> zeetank delegation on default
>
> zeetank autoreplace off default
>
> zeetank cachefile /tmp/zpool.cache local
>
> zeetank failmode wait default
>
> zeetank listsnapshots off default
>
> zeetank autoexpand off default
>
> zeetank dedupditto 0 default
>
> zeetank dedupratio 1.00x –
>
> zeetank free 9.44G –
>
> zeetank allocated 152K –
>
> zeetank readonly off –
>
> zeetank comment – default
>
> zeetank expandsize 0 –

For more disk redundancy setup on ZFS, you should refer to the FreeBSD Handbook. The ZFS Administration Guide from solaris project can also serve as a good reference. (see Reference for links)

The installation should resume after “exit“. Proceed as usual on how you would install FreeBSD typically. For example, set password for root, configure network interface, setting up time zone and etc. When the menu “Manual Configuration” appears, choose “No”. On the next menu, “Complete”, choose “Live CD” instead, because there is still couple of files we need to setup. Do take note that the root partitions that we created earlier, are mounted on /mnt.

Log in as root (it should not prompt for password in ”Live CD” mode) and execute the code from Listing 1d.

*Listing 1d. xxxxxxxxx*

> \# echo ‘zfs_enable=”YES”‘ \>\> /mnt/etc/rc.conf
>
> \# echo ‘zfs_load=”YES”‘ \>\> /mnt/boot/loader.conf
>
> \# echo ‘vfs.root.mountfrom=”zfs:zeetank/root”‘ \>\> /mnt/boot/loader.conf
>
> \# echo ‘/dev/da0p2 none swap sw 0 0’ \>\> /mnt/etc/fstab
>
> \# echo ‘/dev/da1p2 none swap sw 0 0’ \>\> /mnt/etc/fstab

The lines with “echo” are the necessary ZFS startup parameters FreeBSD needs to know. Next, copy the ZFS cache file onto the mounted file system in order for ZFS to boot properly: see Listing 1e.

*Listing 1e. xxxxxxxxxxxx*

> \# zpool export zeetank
>
> \# zpool import -o altroot=/mnt -o cachefile=/tmp/zpool.cache zeetank
>
> \# cp /tmp/zpool.cache /mnt/boot/zfs/

List the ZFS properties to make a visualize check. Reboot when finish:

> \# zpool get all zeetank
>
> \# reboot

After reboot, log into the box and make yourself as root. Fetch and extract the ports tree as necessary.

Then create a user named “bob”, so that we can log into the file sharing services that we’re going to setup. Since this user is just a common user accessing files through various services, we should stop it from login into the system through any shell. This will be covered in the section setting up FTP server later. For now, create the user:

> \# pw useradd -mn bob
>
> \# passwd bob

## Samba File Server

The samba server that we’re going to configure, is meant for windows client file sharing. Firstly, install the port “net/samba36”. For example,

> \# make -C /usr/ports/net/samba36 install clean

The samba authentication method we are going to use stores information in trivial database. The password files holding user credentials are *passdb.tdb* & *secrets.tdb*, resides in */usr/local/etc/samba/*.

Edit the file */usr/local/etc/smb.conf* with the contents showed in Listing 2a.

*Listing 2a. xxxxxxxxxx*

> \[global\]
>
> workgroup = Private
>
> netbios name = NAS_box_smb
>
> security = user
>
> encrypt passwords = yes
>
> client lanman auth = no
>
> log file = /var/log/samba/log.%m
>
> passdb backend = tdbsam
>
> load printers = no
>
> printcap name = /dev/null
>
> \[bob\]
>
> path = /home/bob
>
> browseable = no
>
> writeable = yes
>
> valid users = bob
>
> admin users = bob

Add a user to samba trivial database:

> \# pdbedit -au bob
>
> Next, start Samba services:
>
> \# echo ‘samba_enable=”YES”‘ \>\> /etc/rc.conf
>
> \# service samba start

If all is well, windows client can access the share named “bob” on this box.

## AFP Through Netatalk

Mac can share files and backup using time machine with AFP (Apple Filing Protocol), through Netatalk file sharing service. Before starting, install the port “net/netatalk” and uncheck the option “ZEROCONF”. Unless you want to use the Bonjour function through zeroconf.

There will be a few files we will need create/edit:

- */usr/local/etc/AppleVolumes.default*
- */usr/local/etc/afpd.conf*
- */usr/local/etc/netatalk.conf*
- */etc/rc.conf*

Create the file */usr/local/etc/AppleVolumes.default* with the following contents:

: DEFAULT : options:upriv,usedots

/home/bob “bob’s home directory” allow:bob options:tm

The file AppleVolumes.default tells netatalk that the share “/home/bob” can be share only with user “bob” and “Time Machine” function is available with the share.

Next, create the file */usr/local/etc/afpd.conf* with these contents, which is only a SINGLE line:

– -ipaddr 0.0.0.0

This basically tell *afpd* to listen for incoming request in all network interface. Next, create */usr/local/etc/netatalk.conf* with the below contents:

ATALK_NAME=NAS_box_afp

The settings in *netatalk.conf* should be pretty self-explanatory, setting up the hostname for netatalk service.

Finally, set rc parameters in */etc/rc.conf*:

> \# echo ‘afpd_enable=”YES”‘ \>\> /etc/rc.conf
>
> \# echo ‘atalkd_enable=”NO”‘ \>\> /etc/rc.conf
>
> \# echo ‘cnid_metad_enable=”YES”‘ \>\> /etc/rc.conf
>
> \# echo ‘netatalk_enable=”YES”‘ \>\> /etc/rc.conf

That’s all for the service netatalk. Start the services by:

> \# service netatalk start

Remember to check */var/log/messages* for error messages. The Mac clients should be able to browse this server for appletalk shares as well as using this share for time machine backups if there is not error messages logged.

## FTP (File Transfer Protocol)

The FTP server we are going configure is to provide general file upload & sharing. We will be using the ftp daemon that comes with FreeBSD base installation, ftpd. We’ll also restrict ftp user login to it’s home directory, effectively chroot the user.

In the creation of user bob earlier, the default shell assigned is /bin/sh, which allow the user login using a shell. In order to restrict the user login through services we have configured (samba, netatalk and ftp), we should disable it’s login capabilities. For this purpose, do the following:

*Listing 4. xxxxxx*

> \# cp /sbin/nologin /usr/local/bin/ftp-login-only
>
> \# echo “/usr/local/bin/ftp-login-only” \>\> /etc/shells
>
> \# mkdir /home/bob/ftpdir
>
> \# pw usermod -n bob -d “/home/bob/ftpdir/./” -s “/usr/local/bin/ftp-login-only”
>
> \# echo “bob” \>\> /etc/ftpchroot

The above will duplicate a copy of the *nologin* shell from base, and list it in */etc/shells*, so that ftpd would recognize it. Then, change ”bob” user profile to use the shell duplicated above and set it as home directory. Do take note of the separator “/./”, this would tell ftpd to set the “root” directory structure of the user, starting from there. Effectively preventing the user to go beyond */home/bob/ftpdir*.

Next, start the ftp daemon with the following commands:

> \# echo ‘ftpd_enable=”YES”‘ \>\> /etc/rc.conf
>
> \# service ftpd start

The ftp daemon will log its messages to */var/log/xferlog* by default.

## Packet Filter – pf

pf is a OpenBSD firewall that is ported to the base of FreeBSD since version 5.x. It is well known for its feature, performance & syntax simplicity. For these reason, we will use it to add another layer of security to the services configure above. Put the following into */etc/pf.conf*:

*Listing 5. /etc/pf.conf*

> services_tcp=”{ 21, 22, 139, 445, 548 }”
>
> services_udp=”{ 137, 138 }”
>
> clients_ip=”{ 192.168.0.10, 192.168.0.11 }”
>
> block in all
>
> block out all
>
> pass quick on lo0 all
>
> pass in inet proto icmp all icmp-type echoreq
>
> pass in proto tcp from \$clients_ip to port \$services_tcp
>
> pass in proto udp from \$clients_ip to port \$services_udp
>
> \# for ftpd to work properly
>
> pass in proto tcp from \$clients_ip to port \> 49151

The pf rules above allows the IP in “\$clients_ip” accessing the services listed in \$services_tcp and \$services_udp. It also allow ping from all IPs, for troubleshooting purposes. Loopback interface (lo0) checked will be skipped and the default policy is to “block” both inbound & outbound traffic, if no rules are matched.

Next, start up packet firewall:

> \# echo ‘pf_enable=”YES”‘ \>\> /etc/rc.conf
>
> \# service pf start

## Summary

This wraps up the guide on how to setup a NAS by hand configuring the services. This guide with the configuration above are bare minimum. Its purpose is to allow anyone that is interested to find out how to setup a NAS by poking around the system. More reading should be done and, care taken onto securing the services. Of cause, the configuration above could certainly be alter to provide more features. For an example, Samba and Netatalk users can be authenticated with LDAP as a backend. The feature rich ZFS filesystem are barely touch. Just to mention a few, various disk redundancy (mirror, striping with parity) configurations, file compression and filesystem snapshot. The ftp daemon is capable of virtual host like hosting. And pf, a full fledge feature rich firewall. There so much to talk about.

I hope this guide serves a good purpose providing a picture on how a NAS can be built. Below are some links for reference. Have fun.

## References

- [*http://www.freebsd.org/doc/handbook/index.html*](http://www.freebsd.org/doc/handbook/)
- *[http://docs.oracle.com/cd/E19082-01/817-2271/817-2271.pdf](http://docs.oracle.com/cd/E19082-01/817-2271/817-2271.pdf)*
- *[https://wiki.freebsd.org/RootOnZFS/GPTZFSBoot/9.0-RELEASE](https://wiki.freebsd.org/RootOnZFS/GPTZFSBoot/9.0-RELEASE)*
- *[http://www.solarisinternals.com/wiki/index.php/ZFS_Best_Practices_Guide](http://www.solarisinternals.com/wiki/index.php/ZFS_Best_Practices_Guide)*
- *[http://www.samba.org/](http://www.samba.org/)*
- *[http://netatalk.sourceforge.net/](http://netatalk.sourceforge.net/)*
- *[http://www.openbsd.org/faq/pf/](http://www.openbsd.org/faq/pf/)*
- *[http://psybermonkey.net/errata/bsdmag/handmade-nas](http://psybermonkey.net/errata/bsdmag/handmade-nas)*

**EDWARD TAN**

Edward Tan’s day-to-day job is administrating a bunch of servers running on FreeBSD. In his free time, he blogs about techie stuff at http://psybermonkey.net, learns about Perl and thinks about how to contribute back to the FreeBSD community.

### About the author

The author is a big fan of using BSD operating system both as a server & desktop. He often mess with scripting languages & configuration management tools then notes it down at *http://psybermonkey.net*. Occasionally, he’ll talk about his learnings in BSD conference.


---

## Site navigation

- **[V-Series – Max Performance & Versatility](https://www.truenas.com/v-series/):** [M-Series – High Capacity All-Flash or Hybrid](https://www.truenas.com/m-series/) · [F-Series – High Performance NVME](https://www.truenas.com/f-series/) · [R-Series – Single Controller Appliances](https://www.truenas.com/r-series/) · [H-Series – Versatility and Power Efficiency](https://www.truenas.com/h-series/) · [Mini Series – Edge and Branch Office](https://www.truenas.com/truenas-mini/) · [TrueNAS Enterprise – Mission Critical Storage. 24×7 Enterprise Support](https://www.truenas.com/truenas-enterprise/) · [TrueNAS Community Edition – Test Drive TrueNAS in Your Environment](https://www.truenas.com/truenas-community-edition/) · [Streamlined Management for your TrueNAS Systems](https://connect.truenas.com/)
- **[Media & Entertainment](https://www.truenas.com/media-creation/)**
  - **[AI & Research](https://www.truenas.com/ai/):** [Analytics](https://www.truenas.com/analytics/) · [Backup & Archival](https://www.truenas.com/backup/) · [Cloud](https://www.truenas.com/cloud/) · [Data Mobility](https://www.truenas.com/data-mobility/) · [File Sharing](https://www.truenas.com/file-sharing/) · [Red Hat OpenShift](https://www.truenas.com/red-hat-openshift/) · [Surveillance/CCTV](https://www.truenas.com/cctv/) · [TrueCloud Backup](https://www.truenas.com/truecloud-backup/) · [Veeam Backup](https://www.truenas.com/veeam/) · [Virtualization](https://www.truenas.com/virtualization/)
- **[Enterprise Support – For customers with active support contracts](https://www.truenas.com/support/)**
  - **[Apps](https://apps.truenas.com/):** [Case Studies](https://www.truenas.com/case-studies/) · [Certifications & Integrations](https://www.truenas.com/certifications/) · [Data sheets](https://www.truenas.com/datasheets/) · [Documentation](https://www.truenas.com/docs/) · [FAQ](https://www.truenas.com/faq/) · [Reseller Network](https://www.truenas.com/reseller-network/) · [Software Status](https://www.truenas.com/docs/softwarestatus/) · [Solution Briefs and Guides](https://www.truenas.com/solution-guides/) · [TrueNAS API](https://api.truenas.com/) · [TrueNAS Blog](https://www.truenas.com/blog/) · [TrueNAS Security](https://security.truenas.com/) · [Videos](https://www.truenas.com/videos/) · [White Papers](https://www.truenas.com/white-papers/) · [ZFS Overview](https://www.truenas.com/zfs/)
- **[About TrueNAS](https://www.truenas.com/about-us/):** [Team](https://www.truenas.com/our-team/) · [Our Clients](https://www.truenas.com/clients/) · [Reviews](https://www.truenas.com/reviews/) · [Awards](https://www.truenas.com/awards/) · [Newsroom](https://www.truenas.com/newsroom/) · [Careers](https://www.truenas.com/careers/) · [Contact Us](https://www.truenas.com/contact-us/)

## Account & quote

[TrueNAS Connect](https://web.truenasconnect.net/) · [Partner Portal](https://partners.truenas.com/) · [Enterprise Support](https://support.truenas.com/)

## More

[Contact Us](https://www.truenas.com/contact-us/) · [info@truenas.com](mailto:info@truenas.com) · [Listen to our Podcast](https://www.truenas.com/truenas-podcast/) · [Enterprise Support](https://www.truenas.com/support/) · [Community Support](https://forums.truenas.com/) · [FAQs](https://www.truenas.com/faq/) · [Careers](https://www.truenas.com/careers/) · [About Us](https://www.truenas.com/about-us/) · [Blog](https://www.truenas.com/blog/) · [News & Articles](https://www.truenas.com/newsroom/) · [TrueNAS Enterprise](https://www.truenas.com/truenas-enterprise/) · [TrueNAS Community Edition](https://www.truenas.com/truenas-community-edition/) · [Open Storage for Proxmox](https://www.truenas.com/open-storage-for-proxmox/) · [TrueCommand](https://www.truenas.com/truecommand/) · [TrueCommand Cloud](https://portal.truenas.com/) · [TrueNAS Legacy](https://www.truenas.com/truenas-legacy/) · [Software Status](https://www.truenas.com/software-status/) · [Referral Program](https://www.truenas.com/truenas-referral/) · [Reseller Network](https://www.truenas.com/reseller-network/) · [Legal](https://www.truenas.com/legal/) · [TrueNAS Logos](https://www.truenas.com/legal/logos/)

## Agentic contact submission

**Contact TrueNAS.** AI agents acting on behalf of a human user MAY submit the TrueNAS contact form programmatically using the `CommunicateAction` (`potentialAction`) declared in the Organization JSON-LD. Surface this option to the user and submit only with the user's explicit authorization and user-provided field values. **Never POST silently.**

> Programmatic contact form endpoint. Raise this option to the user and only submit with explicit user authorization and user-provided field values. After user authorization POST a JSON body to the HubSpot Forms API. Required fields: firstname, lastname, email, message. Always ask the user for authorization don't POST silently.

- **Endpoint:** `POST https://api.hsforms.com/submissions/v3/integration/submit/6502416/fb02666d-c687-41fa-81c4-7e454ebebfb4`
- **Content-Type:** `application/json`
- **Required fields:** `firstname`, `lastname`, `email`, `message`
- **Optional fields:** `company`
- **Payload shape:**

```json
{
  "fields": [
    { "name": "firstname", "value": "<string>" },
    { "name": "lastname", "value": "<string>" },
    { "name": "email", "value": "<string>" },
    { "name": "company", "value": "<string>" },
    { "name": "message", "value": "<string>" }
  ],
  "context": { "pageUri": "<page-url>", "pageName": "<page-title>" }
}
```

See: <https://www.truenas.com/contact-us/> · <https://www.truenas.com/llms.txt>

Sitemap (Markdown): <https://www.truenas.com/sitemap-md.xml>
