Jump to content

Introduction to a Self Managed Life: a 13 hour & 28 minute presentation by FUTO software

From FUTO

Welcome! This page is very large and there are many steps, please take your time. If you would like to edit (you're welcome to fix errors even without any account!), please read this first. Thank you!

Preface[edit | edit source]

Dedication[edit | edit source]

Thank you to Tim Gilles, aka Slipperman, whose remarks on what makes someone a “real professional” stuck with me for a lifetime. I listened to Tim on the mixerman radio show. Tim wanted to demystify his craft in a way that anyone could understand; that would inspire EVERYONE to pick up a microphone & a tape machine and give it a shot themselves. He did this with his own “unique” writing style. His work inspired me to do the same with everything I’ve done, from board repair to self-managed servers. Tim passed away two years ago. I hope his legacy lives on through everyone who tries to open doors for the next generation rather than gatekeep information via ego inflating elitism.

Intro[edit | edit source]

I started using GNU/Linux in 2002, back when I saved up the $79.99 necessary to buy SuSE Linux 8.1 Professional as a boxed set from the Best Buy across the street from the Staten Island mall for my 14th birthday. I started hosting my own servers in 2005, and put together systems for my own business’ use since early 2011. I didn’t do everything outlined here immediately; it was slowly built piece by piece over a long time. I never documented it in a way that would allow my grandma to use it. In 22 years, I can’t remember reading GNU/Linux documentation that felt like it was designed for normal people. That’s what I’m looking to do here.

From 2002 to the present, two things remain true:

  • You can do cool things with GNU/Linux
  • These cool things are hidden behind a labyrinth of
    • Half baked software
    • Horrible UI
    • Forum elitists & gaslighting assholes who will make you think YOU’RE the crazy one for expecting things to work.
    • People that will tell you to “RTFM” with no regard for whether that documentation actually works.
    • black boxes. I mean literally hidden behind actual black boxes. For six months. Unfixed. On the stable version of a server operating system (that bug is present in 24.10 long-term-stable even today).

So much of the open source user experience is not designed for normal people. Whether it was using NDISwrapper 20 years ago to get wifi to work or messing with SCSI emulation to burn a CD, GNU/Linux is pain. It’s all pain.

It’s painful enough that people will happily trade their data, sovereignty, privacy, and their rights to avoid ever having to deal with it; and I can’t blame them.

This has to change. As of 2024, most of you live your life:

  1. Dependent on closed source software.
  2. Running on someone else’s server where you can be kicked off at any time.
  3. Forced into forced arbitration or your device won’t work anymore.
  4. With no privacy.
  5. Training AI with your creations.

Now is a time like no other for you to feel empowered to build systems that you control & understand.

My goal with this guide is not to tell you the way you HAVE to do something, or to imply that my way is the best. My goal is to inspire you by showing you what’s possible. You don’t have to be a computer engineer or someone with an IQ of 160 to figure this all out. And, admittedly, to inspire capable developers to look at the pain points scattered throughout this guide (of which there are many) and decide “enough is enough; let’s make this better.”

The fun here is in building your own system, your own way. This is my sovereign cloud; there are many like it, but this one is mine. I can’t wait to see how you build yours.

Why Build Your Own Sovereign Cloud?[edit | edit source]

Apple and Google push users into closed ecosystems while removing options for personal control over data. Think back to when smartphones had microSD card slots, so you could store your photos, videos, & music locally & cheaply. As these companies started pushing paid cloud services, microSD slots disappeared from every phone. Apple no longer gives you a working “delete” button, and Google has mistakenly flagged people as criminals for sending photos a doctor requested of their sick child during COVID lockdowns. These issues come up because you don’t own the software or services you’re using. If you can’t review the source code, it’s not your software. If you can’t host the service yourself, it’s not really yours.

FUTO is looking to change that. We want to provide solutions that let you take back control, whether it’s running your own cloud or hosting your own services. Many of these services have 1% adoption (if they’re lucky!) because of the barriers to use.

One example is Immich; it’s photo gallery software that uses local AI, so you never have to worry about your personal data being analyzed by some remote server. It’s incredibly fast & efficient! I think it’s the best in its field. Right now, if you want to use it, you need to set up your own GNU/Linux server and use Docker to get everything running. You either become a GNU/Linux sysadmin or you sell your data (and your soul) in exchange for a half-decent UI.

Until now!

FUTO’s belief in self managing your own servers.[edit | edit source]

We believe that any piece of software we create or offer that has a client, must be accompanied by server source code that allows you to run your own server. You have to have control over your devices. At the same time, if we throw the source code at you and tell you “have fun!”, have we really enabled you to run your own system? That’s akin to throwing a party and saying “hey, anyone who wants to join us is allowed in!” when you only tell your best friends where the door is. We want the door to the party to be open to everyone; and for all of you to know where it is. So, let’s see if we can put spicy brownie’s concerns to rest.

The Rabbit Hole to Hell[edit | edit source]

I’m going to show you exactly how to set this up because that’s been a common question in the comments. I’m going to show you how to set up Immich. To do that, I need to show you how I get my files from my phone to my server. If I’m doing that, I’m connecting to my server from outside, which means I have to show you how to set up a VPN tunnel. I’m not going to forward ports for all these random services. If I’m doing that, I might as well show you how to set up a router that will always get updates, which means building your own.

While I’m at it, I might as well show you how to block all ads, even when you’re connected from your phone. While we’re in there, let’s show you how to set up something similar to Google Docs, Google Sheets, calendar, contacts, home surveillance with notifications, self-hosted mail, a business phone system that curses out annoying customers for you, and everything else.

Warning: This becomes a rabbit hole very quickly because there are so many items to cover. I’m not going to breadcrumb you. I want to provide you with everything, which means we have to start from the BEGINNING!

A Long Journey Ahead[edit | edit source]

This isn’t going to be a 10-minute video, nor will it be a 10-page guide. It’ll probably be a ten-hour video, and a 1000-page guide. You’ll get to figure out how much I hate you based on whether or not I provide you with timestamps or a table of contents.

Understanding the Basics: Modem, Router, Switch, and Wireless Access Point[edit | edit source]

Before we dive into discussing building a router, I want you to understand the key components of your home network: the modem, router, switch, and wireless access point. These devices work together to connect you to the internet and allow multiple devices to communicate with each other. Most consumer products package the router, switch, and wireless access point all in one, hiding from you what each component is for. You might even have a modem that includes all three, meaning you have one device on your home network! Let’s break down the purpose of each device.

Modem[edit | edit source]

The modem is your gateway to the Internet, connecting your home to your Internet service provider (ISP).

What a Modem Does:

  • Translates the signal from your ISP (e.g., cable, fiber, or DSL) into a format your devices can use. However, typically, the interface for fiber is called an optical network terminal (ONT).
  • Acts as the bridge between your ISP’s network and your home network.

Types of Modems:

  • Cable Modem: Connects to your ISP via a coaxial cable.
  • DSL Modem: Connects via a phone line.
  • Fiber Modem: Connects via a fiber-optic cable. More properly called an optical network terminal (ONT)

Important: A modem typically has only one Ethernet port, which is why you need additional devices like routers and switches to connect multiple devices in your home. A modem may have a phone jack to attach a standard telephone.

Router[edit | edit source]

The router manages traffic between your local network (your home devices) and the internet (outside world).

What a Router Does:

  • Allows you to have more than one device on your network.
  • If you attach your computer to your modem directly, you are simply connecting to the “outside” world’s network. This is referred to as “WAN” - Wide Area Network is a network that connects multiple LANs over large distances, while a LAN is a network confined to a local area. This can work, but when you do this you do not have an internal network. The computer you attached to your modem is the only computer in your home that can go online with this configuration.
  • Routers create a 2nd internal network for your devices so you can attach more than one thing to the internet (WAN). Wouldn’t it suck if you could only have one wired device attached to your home internet? This is why most people need a router!
  • Routes Traffic: Directs internet traffic from the OUTSIDE (this is called the “WAN”) to the correct device on the INSIDE, your home network (this is called the “LAN”), and vice versa. Now, multiple devices (e.g., computers, phones, smart TVs) can communicate with the internet through your modem, and with each other within your home.
  • Provides NAT (Network Address Translation): Translates your devices’ private IP addresses into a single public IP address provided by your ISP.

Note: The router you get from your ISP or buy from a store, 99% of the time, is a combo device that looks like this: includes a router, switch, and wireless access point all in one box. Understanding their roles separately is key when setting up a more advanced system like pfSense.

Traditional wired router:[edit | edit source]

Below is a traditional wired router. This combines a router & a switch but has no wireless access point.

Cheap Walmart Wi-Fi router:[edit | edit source]

This is a TP-Link wireless router: a router, switch, and wireless access point all in one. This is most likely what you have in your closet right now, covered in wires, under the set of workout pants you bought six months ago after your failed New Year’s resolution to go running every morning, that has slow speed unless you’re 2 feet from it. These often come with SIP-ALG (a component that transforms Voice-over-IP packets, which generally isn't needed any longer today) on by default, and will mess with your phone systems endlessly even if you try turning it off. Avoid the Walmart routers.

Switch[edit | edit source]

A switch expands the number of devices you can connect to your local network using Ethernet cables.

What a Switch Does:

  • Expands Connectivity: If your router only has a few Ethernet ports, a switch allows you to connect more wired devices (e.g., computers, gaming consoles, network-attached storage).
  • Forwards Data: A switch is smarter than a basic splitter. It knows which devices are connected to each port and forwards data to the correct device, improving network efficiency.
  • The type of basic switch I am using for this example is the smaller type to the left, that has no advanced routing features, settings, or web interface to mess with. It’s just a dumb switch. We use it to get more ports on our home network than the one port on the pfSense router.

Switches come in different sizes, from small 4-port models to large 24-port (or even larger) models used in business environments. The small Netgear switches that cost $15 are more than adequate for most people’s home networks & will not cause random disconnects or issues with our router setup.

Cheap switch[edit | edit source]

This is a basic Netgear switch that you get for $15. It allows you to connect four devices to your pfSense router. You would attach the LAN port on the pfsense router to a port on this switch(any port is fine) & then connect your wired devices(wireless access point for wifi, computers, etc) to other ports on the switch. Some points to note:

  • This switch is gigabit - meaning, 1 gbps.

    • 1 gbps = stuck transferring around 100 megabytes per second real world performance(aka the speed of ten year old hard drives)
    • This means even if you have a fast solid state drive in the server & your personal computer, transfer speed will be around 100-120 megabytes per second
    • If you have a gigabit internet connection & are downloading a file at 1 gbps, you can also grab a file from your server without slowing your download.
  • This has no Power over Ethernet(PoE)

    • If you want to power wireless access points, office voice over IP(VoIP) phones, or cameras, you have to plug them into something or get PoE injector later.
    • A Power over ethernet switch can power devices you plug the ethernet cord into which is very cool for setting up security cameras, only have to run 1 wire.

    These cheapies will usually not have Power over Ethernet to power cameras & wireless access points & office desk phones, nor will they usually support configuring ports for VLANs (we will get into that in the wifi section at the end). This is a good starter switch since it is reported to pass VLAN tags, so if you bought wifi access points or switches that supported creating isolated networks this switch would pass those tags(we’ll get into that at the end of the guide); no need to worry about that right now.

    These cheap switches work great, and also come in 8 port versions for a few bucks more.

Expensive switch[edit | edit source]

The Netgear XS724EM switch is an expensive, fancier switch.

  • Speed
    • Supports 2.5 gigabit or 10 gigabit per second ethernet on its ports.
    • If you have a network interface card(NIC) that supports 2.5 gbE on each end(these are becoming more common), you can get over 270 megabytes per second transfer rate(more than 2x a normal gigabit switch)
    • If you have a network interface card(NIC) that supports 10 gbE on each end(your computer does not have this unless you bought it separately & installed it), you can get over 800 megabytes per second even with a poorly tuned setup. This is likely faster than any of the drives inside your computer unless you bought fancy NVMe drives.
  • Power
    • Can power a bunch of cameras, phones, wireless access points over ethernet
  • Ports
    • Has 24 ports instead of 5, can connect a lot more stuff.
    • COMPATIBILITY of the ports - does 10 GbE over standard ethernet plugs/jacks. If you wire your house with good cat6a and put good 10 gbE network interface cards in the machines you’re working with you can get 800 megabyte per second networking all around your house without digiorno connectors.
  • Virtual LAN support
    • Allows you to create separate networks on the same switch.
    • Can keep your untrusted internet of things(IoT) devices like cheap light bulbs & cameras & thermostats on isolated separate networks from your trusted devices.

The VLAN support is a big one because later on when we connect wifi access points that are advanced we are going to do far more than just make a “guest network”; we are going to make a network that your IoT devices (bulbs, thermostats, cameras, etc) can connect to and isolate them in a way where your computer running your security cameras & home automation can connect to them, but they are isolated from connecting to anything else. This isn’t necessary though and a bunch of you will probably skip the VLAN part at the end, since that gets a little too complicated for a home setup.

This is an expensive switch. There are many inbetween, but I thought it’d be useful to show an example of the cheap side & expensive side to show what is available & what you can get for the money. If you are ok with gigabit ethernet you can easily get by with way cheaper; right now you can either buy gigabit switches cheaply, or 2.5 gbe & 10 gbe switches at crazy high prices. There isn’t much inbetween.

4. Wireless Access Point (WAP)[edit | edit source]

A wireless access point (WAP) provides Wi-Fi access to your network, allowing devices like phones, tablets, and laptops to connect wirelessly. You could add a wireless access point like the ones below, to the old blue Linksys router above, to turn it into a “wireless router.”

What a Wireless Access Point Does:

  • Provides Wireless Connectivity: The WAP connects to your router (or switch) and broadcasts a Wi-Fi signal, letting wireless devices connect to your network.
  • Doesn’t Route Traffic: It’s important to note that a WAP doesn’t perform the same function as a router. It simply extends your network by adding wireless connectivity.

These are mesh network access points. They allow you to connect each to your switch and place them in separate areas of your home to make sure you have great connectivity everywhere.

The way these work is you would place the access points on different parts of your house and have an ethernet wire going to each one. The access points intelligently work together to figure out which one you should be connected to based on which provides the strongest signal to your laptop/phone where you are right now. You’d place one on the side of your house, one in the basement, one on each side of each floor in your home, and wire them all to your switch & you’ll get amazing wifi connectivity from anywhere. Good wireless access points will switch over so seamlessly that your file transfer does not stop or fail as it is happening.

These setups are more expensive since proper mesh equipment that works right costs more & you are buying multiple access points.

This is an ancient wired router with no wifi.

This is a cheap ass wireless access point. I don’t recommend any of these especially when something like a TPLink EAP6120 is about $50 used & offers much better seamless roaming if you want to add access points later, VLAN functionality, etc. I know it’s tempting to buy the lame ones because they are in stock at best buy & walmart for instant gratification but you’ll regret it later.

This is an ancient wireless router that is a legend. The unbreakable, unbeatable, Linksys WRT54G. It is a router, a switch, and a wireless access point all in one.

Internet Protocol addresses[edit | edit source]

You have an address on the front of your building. You have a phone number - this is how people find you. Your modem will be how you get an IP address from your internet service provider. It usually looks like 64.91.255.98 or 8.8.8.8 - you may have seen this before.

Most of you with a home internet connection have something called a Dynamic IP. This means that your IP can change.

Your IP address may change for a number of different reasons:

  • When you unplug your modem [for a long period of time].
  • When you plug your modem into a new router.
  • Every day, just for the hell of it!

This can make things more difficult than when you have a static IP - static IPs do not change. You get an internet protocol address, and that’s what you’re stuck with, for better or for worse.

For home users, most people don’t need a static IP. Static IPs are for when I want something to “stay put”. I want my phone number to stay put so people know where to find me. I want my home address to stay put so the mailman knows where to find me (and so I know where to go home!) and, in this case, I want my IP to stay put so I can always find my home server, no matter where I am in the world.

If you are reading this - you likely have a dynamic IP provided by your home internet service provider. We will have a workaround for this that allows you to be able to find your server at the same place every time you go to use it no matter how often its IP changes.

How These Devices Will Work Together in Your Setup[edit | edit source]

For this setup, you’ll use a dedicated pfSense router instead of the combo device provided by your ISP. Here’s how the connections work:

1. Modem to Router[edit | edit source]

  • The modem takes the signal from your ISP and passes it to your pfSense router via an Ethernet cable.
  • The modem will be connected to the WAN (Wide Area Network) port on the router.

2. Router to Switch[edit | edit source]

  • Your pfSense router manages traffic between your devices and the internet.
  • Since the Intel NUC running pfSense has only two Ethernet ports, you’ll connect the second port (the LAN (Local Area Network) port) to a switch to connect multiple devices.

3. Switch to Devices[edit | edit source]

  • The switch is connected to the LAN port of your pfSense router.
  • Any wired devices (like computers, gaming consoles, or network storage) can be connected to the switch using Ethernet cables.
  • This allows multiple devices to communicate with each other and access the internet through the pfSense router.

4. Adding Wireless Access[edit | edit source]

This will allow your phones, laptops, and other wireless devices to connect to the network without wires.

  • If you only plan to have wireless devices on your network, you can attach your wireless access point directly to the LAN port on your pfSense router.
  • If you wish to have a combination of wired & wireless devices on your network, you would attach a wired switch to the LAN port on your pfSense router, and then plug the Wi-Fi access point into a port on your switch.
  • If you have no plans to have wireless devices on your network, you do not need a wireless access point.

A Common Home Network Setup vs. Your New Setup[edit | edit source]

Common Setup (with ISP Combo Device):

  • Modem → ISP-provided combo device (modem + router + switch + WAP)
  • All devices (wired and wireless) connect to the combo device.

Your New Setup (with pfSense):

  • Modem → pfSense Router (dedicated firewall/router)
  • pfSense Router → Switch (for wired devices)

This new setup gives you better control over your network, improved security, and the ability to block ads with pfSense and tools like pfBlockerNG. It is important that you know what each component does & their purposes. By understanding what each component does, you’ll be better equipped to set up and manage your new pfSense-based network!

Why Build Your Own Router?[edit | edit source]

Regular Security Updates & OpenVPN[edit | edit source]

Let’s start at the very beginning with OpenVPN. We are not opening ports to the internet for ANYTHING, except for receiving self-hosted mail. We’re running a bunch of different open source services that less than 0.1% of the population (if I’m being generous) actually use. I LIKE Immich, Home Assistant, Syncthing, FreePBX, OnlyOffice, Nextcloud, Mailcow, Frigate. But I don’t want them just open to the internet.

They’re nice software, but they’re used by 0.0001% of the population. Further, even if they WERE secure, by opening ports to the internet, I am letting every Tom, Dick & Harry who wants to peek in see what I am running on my IP address.

OpenVPN is used by companies in the S&P 500, banks, and governments; it’s everywhere! The beauty of OpenVPN is that if there’s ever a security breach, it’s going to get found and fixed because there are tens of millions of eyes on it at any given moment. There is too much investment in OpenVPN for it to wither on the vine and become fundamentally insecure. OpenVPN is as secure as it gets, and while it’s not perfect, we are massively reducing our RISK of being hacked & exploited by utilizing OpenVPN to get into our home network vs. opening ports willy nilly to 10 different pieces of software.

I don’t want people to be able to see that these services are all running on my server. That means there are four, six, eight, or 15 different points of failure. I’d rather have one point of failure that’s managed properly. And that’s what a VPN is for—a way to create a secure, encrypted tunnel between your phone and your server.

Why can’t I buy a $30 router at walmart?[edit | edit source]

Short lifespan for firmware updates[edit | edit source]

Consumer routers you find in stores may offer features like OpenVPN, but the problem is that many stop receiving updates shortly after you buy them.

Buggy[edit | edit source]

Many of the lower end store routers are buggy and can cause problems with what I am showing you how to set up. Certain TP-Link routers have randomly messed with SIP traffic in the middle of a call, and the router that Spectrum and Verizon provide have SIP-AlG turned on by default; which will mess with our phone system. They don’t let you turn it off in the configuration settings either!

Back to my point; using a router where you are at the mercy of the manufacturer to provide you with updated firmware leaves you vulnerable to security risks as new exploits are discovered. For example, three years down the line, there might be a very important update for OpenVPN, but your router’s manufacturer might have stopped supporting your model after just six months. Now you’re screwed.

Increased likelihood of getting hacked over time[edit | edit source]

You’re making it harder for yourself by using a router that will become vulnerable to exploits in OpenVPN. OpenVPN is exceptional software: these holes get plugged, and they get plugged fast.

…if the manufacturer actually updates the firmware. They often don’t. Think about it;

  1. You already paid for the router.
  2. Providing you with updated firmware costs them money & time.
  3. But they already have your money
  4. so they don't care

You might think I’m being bombastic; what’s so bad about using an older version of OpenVPN?

OpenVPN exploits:[edit | edit source]

A CVE is a common vulnerability & exploit - aka, a way to hack into something. These are a small number that have occurred over the years. Finding CVEs isn’t a bad thing, every piece of software ever created is going to have security vulnerabilities. It is only bad if you are running hardware that you cannot update once a fix has been released.

1. CVE-2024-27459, CVE-2024-24974, CVE-2024-27903, CVE-2024-1305[edit | edit source]

  • Discovered: March 2024
  • Description: Multiple vulnerabilities were found, mainly affecting OpenVPN’s client-side on Windows, Android, iOS, macOS, and BSD. These included stack overflow, unauthorized access, & plugin flaws leading to potential remote code execution (RCE) and local privilege escalation (LPE). Users were advised to update to OpenVPN versions 2.6.10 or 2.5.10 to mitigate the risks. You can only update OpenVPN versions if your router lets you.

Terminology note: “client-side” means the part of the software that runs on your device (like a computer or smartphone), as opposed to “server-side,” which would be the part running on a remote server (Apple/Google’s server).

“Remote Code Execution (RCE)” is a vulnerability that lets a hacker run code they want to run on your device. “Local Privilege Escalation (LPE)” means a vulnerability that lets a hacker get higher permissions (i.e. becoming an admin rather than being a regular user) allowing them to do things they shouldn’t or gain full control over your system.

2. Code Signing Key Intrusion (OpenVPN 2.5.8)[edit | edit source]

  • Discovered: December 2022
  • Description: An intrusion was detected involving OpenVPN version 2.5.8. There’s no evidence suggesting the key was misused & OpenVPN proactively re-released the software signed with a new key for security. This is why updates matter.
  • Sources: OpenVPN Security Advisory

3. CVE-2022-0547[edit | edit source]

  • Discovered: February 2022
  • Description: Enabled authentication bypass in external authentication plug-ins when more than one of them makes use of deferred authentication replies, which allows an external user to be granted access with only partially correct credentials. aka, I can have a sawed off copy of your house key & still get in.
  • Sources: OpenVPN Community

4. CVE-2020-15077, CVE-2020-36382[edit | edit source]

  • Discovered: 2020
  • Description: These vulnerabilities affected OpenVPN Access Server, with risks of information leakage and potential denial-of-service (DoS). Patches were released fast to address these security issues, which requires you have a router that allows you to continue updating it after the manufacturer has given you the middle finger & told you to buy a new one.
  • Sources: OpenVPN Security Advisory

5. CVE-2018-9334[edit | edit source]

  • Discovered: 2018
  • Description: A denial-of-service vulnerability in OpenVPN’s handling of authentication processes, potentially allowing attackers to disrupt services was patched.
  • Sources: OpenVPN CVE List

6. CVE-2017-7521[edit | edit source]

  • Discovered: 2017
  • Description: A memory exhaustion flaw was found where an attacker could exploit OpenVPN’s message handling to cause service disruption.
  • Sources: OpenVPN CVE List

Guaranteed long term compatibility & updates[edit | edit source]

Even a cheap 10-year-old desktop PC can be a good router for the next ten years; as long as it has a good network interface card. If it runs out of RAM or new network technologies come out, you won’t throw it away; you’ll buy a new network card for $40 or more RAM at a yard sale. Ten years from now, going from 2 GB of RAM to 8 will probably cost less than $10.

Using a standard x86 PC as a router, with known good Network Interface Cards, means you are less likely to encounter compatibility or longevity issues when using any of these open source router systems. It gives you more control, and if you’re reading this, you probably have an old desktop PC in the garage or closet you’re not using anyway. Get it two good network interface cards and get it back in commission!

What about OpenWRT?[edit | edit source]

There are open source packages like OpenWRT doing the lord’s work to keep these routers going. This is a good project, run by good people. I do not want to denigrate them in any way; what I am about to say is in no way their fault. They do their best to keep routers running with their firmware for as long as possible, but eventually, it becomes too difficult or untenable to provide updates for older chipsets & hardware, and they fall off the list. Those old routers will only work with older versions of OpenWRT.(especially for those 4/32 device)

But it’s a lot of work to support 100s of different makes & models, all using their own specific hardware. When we build a router using a standard computer, we can install router software like pfSense or OPNsense, which means the chances of our hardware not getting updates/not being supported shrinks to almost nothing. These open source projects do not have to support a gazillion different hardware configurations. They support x86, and if you have x86 (most normal desktop computers are x86), you’re good. It makes it easier to maintain on a mass level & provide regular updates to. The likelihood of your “hardware not being supported” with an open source router distribution when it is a desktop PC with a good network card shrinks to near 0.

By building your own router using pfSense, an open-source firewall, and cheap, dedicated hardware, you guarantee long-term support and control over your setup. With pfSense, you can get regular updates, customize your network settings, and even block ads across all devices using pfBlockerNG.

Building Our Own Router[edit | edit source]

Let’s dive into the first step: setting up pfSense on an Intel NUC (a small-factor barebone PC, Next Unit of Computing) to serve as your router. We’ll be setting this up with OpenVPN, which is very important for connecting securely to your home network.

As for the hardware, I’m using an Intel NUC because it’s compact, reliable, and it has two Ethernet ports, which are necessary for setting up a router. One port is used for your WAN (internet), and the other for your LAN (internal network). For a pfSense router, we must choose a machine with TWO ethernet ports, not one!

Why pfSense?[edit | edit source]

I chose pfSense ten years ago because:

  1. It’s open-source.
  2. It’s fast.
  3. It gets regular updates for security issues.
  4. The parent company has paid corporate & business clients relying on their software, which is based on an open source core. The developments with regards to making certain network cards work well with FreeBSD get included upstream to the free versions.
  5. This means that me, as a scrub who didn’t pay for it, get something that is very similar to what corporate clients who are paying $10,000 or more are getting.
  6. If I mess something up with my very unusual custom setup, I can pay the developers of the software to fix it for me. This level of support is not common in many open source projects. If I want to cry uncle & pay them an annual fee, they will respond to my questions & provide me with REAL answers rather than tell me to go “rtfm.”
  7. It comes with features like pfBlockerNG to block ads, scams, and malware at IP & DNS level with regular updates.

I use pfSense now because:

  1. I’m used to it.
  2. The idea of redoing my complicated setup from scratch gives me hives.
  3. See #2, in regard to becoming acquainted with the unique quirks of other open source software.

I had very good reasons for choosing pfSense ten years ago – and I have good reasons to use it today. That doesn’t mean it’s the best. Feel free to use whatever you want to use. For the purposes of this guide, I will be using pfSense.

There’s a bit of a debate between pfSense and OPNsense. TL;DR, the developers of pfSense are not the nicest people sometimes. If this bothers you, consider checking out OPNsense. Since I’ve been using pfSense for a decade, I’ve built much of my infrastructure around it. I am well aware of its quirks and don’t feel like setting up my network from scratch, so I am using pfSense for this tutorial. Regardless of the developers, you are infinitely better off using pfSense on your own hardware than standard routers.

Choosing the Right Hardware[edit | edit source]

Why an Intel NUC?[edit | edit source]

When searching for hardware to build a pfSense router, you’ll often come across a variety of mini PCs on platforms like Amazon. However, there are several issues with these options:

  1. Inconsistent Quality: You’ll find reputable brands like Mikrotik listed alongside unknown generic random stuff. I trust Mikrotik - I don’t trust random junk. Amazon allows random junk from unverified, untrusted vendors to show up routinely at the top of the search results.
  2. Unreliable Reviews: Amazon’s review system has known issues:
  3. Safety Concerns: Amazon has a history of selling mislabeled or dangerous products, including:

…and the list goes on. This guide is going to be 600+ pages when done; do you want to do all of this work only to have the primary component be a piece of junk from a website that sells cat guillotines? No.

The Better Alternative: Repurpose an Old Desktop PC[edit | edit source]

Instead of risking your project with unknown mini PCs, consider using an old desktop computer:

  1. Reliability: A 10-12 year old desktop is likely more reliable than no-name mini PCs.
  2. Choice of Network Card: Desktop PCs offer PCI Express slots for additional network cards, so YOU can choose the network interface card for your setup. You often do not know what chipsets are used in the no-name-mini-PCs. pfSense & other FreeBSD-based routers are sensitive to poor-quality chipsets.
  3. Cost-Effective: You can re-purpose an old desktop you already have & save money on purchasing new hardware.

Choosing the Right Network Interface Cards (NICs)[edit | edit source]

To transform your old desktop into a capable router:

  1. Add Quality NICs: Install high-quality network cards, preferably Intel-based.
  2. pfSense Compatibility: Check the pfSense forums for compatible chipsets and cards.
  3. Examples of Good NICs:
    • Intel X540
    • Intel 350

Caution When Purchasing NICs[edit | edit source]

  1. Avoid Realtek at all costs: Read pfSense and FreeBSD forums to learn about the issues from people who use Realtek network interface cards. Sometimes you’ll get something working, but often you will get headaches and nightmares that are not worth the cost savings. Realtek network cards are best avoided in pfSense & similar setups due to known issues with poor performance & compatibility. Intel network interface cards are preferable for reliability & better support in open-source projects like pfSense.

Note of Appreciation: pfSense developers have created drivers for network interface chipsets like the 225 (citation 1, citation 2) that didn’t exist before. Intel network interface cards are known to have better performance & reliability in FreeBSD systems than Realtek chipsets. The ecosystem of open source firewalls are invested in providing support for these chipsets, providing solutions when the manufacturer doesn’t.

This is an excellent argument in favor of paying money for open source software. The igc driver for the i225 Intel network chip was made available to everyone! Commercial users, non-paying users of pfSense, and other FreeBSD based routers/firewalls all benefit from people paying for open source software. Top notch programmers wrote these drivers because they were able to pay their rent & bills doing so.

When you pay for open source software, you are sending a message that it makes sense for top notch programmers to spend money developing open source code that doesn’t abuse you rather than going to work for facebook.

  1. Buy from Reputable Vendors: Avoid counterfeit products by purchasing from trusted sellers. There are many counterfeit cards out there.
  1. Vendors don’t know the difference: Many vendors selling knockoff cards do not even know they are doing it. Wholesale liquidators operate with low profit margins while selling a wide variety of equipment and lack the time and expertise to vet all of what they sell. As a result, many vendors sell counterfeit and fake Intel network cards.
  • Recommended: The Art of Server on eBay (link)
  • Example product: Intel X540 (link)

Verify Compatibility: Make sure the card fits your PC’s available slots.

  • Be wary of non-standard form factors or connectors.

HINT: Buying cards that are branded from server re-sellers is a good way to avoid fakes. For instance

Don’t buy Digiorno[edit | edit source]

Buying used network cards, and used hardware, is ok. Actually, it’s encouraged; it’s a great way to buy better hardware than you’d otherwise be able to afford, and it avoids senseless waste. However, be careful to not buy Digiorno. There are amazing deals to be found in the used server world, but it is also a jungle ready to eat you alive if you’re naive enough to believe those crazy folks have any respect for the civilized world of standardized connectors.

Good vendors will be able to tell you the difference between normal hardware and Digiorno. If they do not know the difference, YOU DO NOT WANT TO BUY FROM THEM!

Building a DIY pfSense router with an old desktop PC and quality Intel NICs is likely to provide a more reliable and expandable solution than generic mini PCs. With a random mini PC, if you get a bad network interface card, you’re out of luck. With your old desktop PC, you can choose the network interface card. Want 2.5GbE? Get another card. Want 10 Gbps? Get another card. Want fiber? Get another card. Have a card with the wrong chipset? Swap in another card.

We are going down a 10+ hour rabbit hole of hell setting up all sorts of confusing, crazy GNU/Linux software. Even a 1% increase in the likelihood of this being more difficult as a result of random garbage Amazon hardware isn’t worth it to me for $100-$200 in savings.

I chose an Intel NUC because it has two quality NICs, and I was able to find one affordably. You do not have to buy the computer I bought to use as a router: this is your journey!

Note: There is no one “right” way to do this. As long as you use a stable, quality computer with GOOD network interface cards that the pfSense & FreeBSD community approve of, you are set!

Step 1: Downloading pfSense and Preparing a Bootable USB Drive[edit | edit source]

1.1 Download pfSense[edit | edit source]

pfSense’s website has unfortunately become cancer in recent years. While I am all for paying for software, the concept of having to add to cart, checkout, and insert billing information to download a free image… no. Avoid using this version of the website. Instead, go here. Feel free to buy it and pay for their support, but don’t jump through stupid hoops.

  1. Open your web browser and visit the pfSense mirror site.
  2. Choose the correct architecture for your system (usually amd64 for most modern computers, including Intel NUCs). If you don’t know what the difference is between these, pick amd64.
  3. Select the USB installer image (.img.gz) from the available options.

1.2 Unzip the Downloaded pfSense File[edit | edit source]

  1. After the download completes, you’ll need to uncompress (unzip) the file.
  2. The file typically ends with .gz. Use the right tool for your operating system:
  • Linux or macOS: Open a terminal and run the following command:

    gzip -d pfSense-CE-memstick-*.img.gz
  • Windows: Use a tool like 7-Zip. Right-click the file, choose “Extract Here,” and let the tool unzip it.

1.3 Create a Bootable USB Drive with the pfSense Image[edit | edit source]

Warning: This process will erase everything on the USB drive.

  1. Insert a USB flash drive (at least 4GB in size) into your computer.
  2. Use one of the following methods to write the pfSense image to the USB drive:

Windows:[edit | edit source]
  1. Download and install [Rufus|https://en.wikipedia.org/wiki/Rufus_(software)].
  2. Open Rufus and select your USB drive.
  3. Click the “SELECT” button and choose the unzipped .img file you downloaded.
  4. Click “Start” and let Rufus create the bootable USB.

GNU/Linux or macOS:[edit | edit source]
  1. Open the terminal and type the following command:

    sudo fdisk -l
  2. Make note of drives in the system. Do not erase these.

  3. Plug in the flash drive.

  4. Open the terminal and type the following command again:

    sudo fdisk -l
  5. Make note of the drive that was not present before. Write it down.

  6. Double-check size/brand/model to make sure this new device is the device you plugged in.

  7. Now, unplug the drive you just plugged in.

  8. Run:

    sudo fdisk -l
  9. Does the drive you wrote down in step 5 still appear? If so, you made a mistake, and you’re on your way to deleting all of your data. Don’t do that. Do not pass go, do not collect $200 – back to the beginning. If not, you can now plug your drive back in.

  10. Run:

    sudo fdisk -l
  11. If the drive that did not appear last time, appears this time, and is the same device as in step 5, you are likely on your way to not erasing your entire system. Good job, that makes you less of an idiot than me; a low bar, but it’s something.

  12. Run the following, replacing /dev/sdX with your drive, and replace the pfSense img file with the filename of your image file:

    sudo dd if=pfSense-CE-memstick-serial-*.img of=/dev/sdX bs=1M

Your bootable USB drive with pfSense is now ready for use! If you managed to erase your entire computer by writing pfSense’s image to your operating system drive EVEN AFTER all of this, congratulations, you’re almost as stupid as me.

Step 2: Disable Secure Boot and Install pfSense on the Intel NUC[edit | edit source]

Before you can install pfSense, you’ll need to disable Secure Boot if you are using a modern computer. Many modern computers, especially those pre-installed with Windows 10 or 11, come with Secure Boot enabled, preventing you from booting into an operating system that isn’t signed by Microsoft initially. Since pfSense is open-source and unsigned, we need to disable Secure Boot to start our installation.

1. Disabling Secure Boot in BIOS[edit | edit source]

  1. Insert the USB Drive
    • Plug in the USB drive containing the pfSense installation image into one of the USB ports on your Intel NUC.
    • Make sure this is done before you power on the device.
  2. Enter the BIOS
    • Power on the Intel NUC and immediately start pressing F2 (or the designated key for your system) to access the BIOS settings.
    • Keep pressing this key until you enter the BIOS. On some systems, the BIOS key may be different (e.g., Delete or Esc), but F2 is common for most systems.
  3. Disable Secure Boot
    • Inside the BIOS, navigate to the Boot section.
    • Locate Secure Boot and toggle it to Off. Depending on your BIOS, Secure Boot may be located under the Security or Boot sections.
    • Once Secure Boot is disabled, you’re ready to install pfSense.
  4. Set Boot Priority
    • In the BIOS, go to Boot Priority settings.
    • Set your USB drive as the first boot device. This will allow the system to automatically boot from the USB drive containing the pfSense installer.
    • Alternatively, you can press F12 (or the appropriate key) during boot to manually enter the boot menu & select the USB drive each time.
  5. Save and Exit BIOS
    • Press F10 to save your changes and exit the BIOS, or whatever key does it on your machine.
    • The system will now reboot, and if the USB drive is set as the first boot option, it should boot directly from the USB flash drive and load the pfSense installer.

Step 3: Installing pfSense on the Intel NUC[edit | edit source]

Boot from the USB Flash Drive[edit | edit source]

1.1 Power on the Intel NUC[edit | edit source]

  • Make sure the USB drive containing the pfSense installer is still plugged into the Intel NUC.
  • Power on the NUC and press F10 (or the relevant boot menu key) to select the USB drive as the boot device.

1.2 Select the USB Drive in Boot Menu[edit | edit source]

  • In the boot menu, you’ll see a list of available boot devices. Select the USB flash drive that contains the pfSense installer.
  • Press Enter to boot from the USB drive.

Begin the pfSense Installation[edit | edit source]

2.1 pfSense Installer Menu[edit | edit source]

  • After a few moments, the pfSense installer menu will appear.
  • Use the arrow keys on your keyboard to select Install and press Enter to begin the installation.

2.2 Choose Installation Method[edit | edit source]

  • The installer will guide you through the process. When prompted to choose an install method, select Auto (ZFS) for the file system.
  • ZFS is a great file system that offers data integrity, snapshots, and other advanced features. You probably won’t use most of them, but it’s still an excellent choice.

Select the Correct Installation Drive[edit | edit source]

Raidz1 is a good option in that it allows one of the drives in your machine to die, and the router to keep going. This requires you have not one, but two drives inside your router machine. This is not a bad idea. You should be making a backup file of your router anyway so that you can restore regardless of what happens to any and all of the hardware on this one: but, this will allow the router to keep working even if a single drive dies. I am using stripe, no redundancy, which is the option you will be picking if you have only one drive in the router.

3.1 Select Internal SSD or Hard Drive[edit | edit source]

  • The next step is to select the disk where pfSense will be installed. This is a very important step, so pay close attention.
  • You will see a list of drives. The USB drive will usually appear as a small capacity device (e.g., 4GB or 8GB).
  • Choose the larger drive that represents your Intel NUC’s internal SSD or hard drive (e.g., 256GB, 512GB).
  • Important: “generic-mass-storage-class” is usually your external USB flash drive. If you’re using a PC with an internal drive, there’s a 99% chance that “generic-mass-storage-class” is NOT what you want to select unless you’re intentionally installing to a USB mass storage device (which is not recommended for a permanent installation).
  • In my case, the Micron SSD was my internal SSD. Your drive name may be different, but look for a larger capacity drive that matches what you know is inside your NUC or PC.
  • Use the arrow keys to highlight the correct drive, then press Enter to confirm your selection.

3.2 Confirm Erase and Installation[edit | edit source]

  • Once the correct internal drive is selected, the installer will ask if you want to erase the drive and proceed with the installation.
  • This will erase all data on the selected drive. Make sure you’ve backed up any important data before proceeding.
  • Confirm by selecting Yes. The installer will now copy files and set up pfSense on the internal drive. This may take a few minutes.

Complete the Installation and Reboot[edit | edit source]

4.1 Remove the USB Flash Drive[edit | edit source]

  • After the installation is complete, you’ll be prompted to reboot the system.
  • Before rebooting, remove the USB flash drive from the Intel NUC. This makes sure it boots from the newly installed pfSense system on your internal drive.

4.2 Reboot and Load pfSense[edit | edit source]

  • After removing the USB drive, press Enter to reboot the system.
  • The Intel NUC will now boot into pfSense from the internal drive, and you’ll be greeted with the pfSense console screen.

Now that pfSense is installed, you’re ready to proceed with the initial configuration. This includes setting up your WAN (external network) and LAN (internal network) interfaces to make the NUC function as your network router.

Step 4: First-Time Configuration of pfSense[edit | edit source]

Now that you have pfSense installed on your device, it’s time to set it up and configure the basic settings. This step will cover configuring the WAN (internet) and LAN (local network) interfaces, setting IP addresses, and making sure everything is ready for further setup.

1. Connecting and Booting Up pfSense[edit | edit source]

1.1 Connect Your Devices:[edit | edit source]

  • Plug your cable modem into one of the Ethernet ports on your pfSense device.
  • Plug your desktop computer (the one you’re using to set everything up) into the other Ethernet port.
  • At this point, you don’t need more than these two connections.

1.2 Power On and Watch the Boot Process:[edit | edit source]

  • Turn on your pfSense device.
  • You’ll see a lot of text scrolling on the screen as the system boots up. Don’t worry if it seems overwhelming—this is normal.
  • Pay close attention to the information displayed, especially towards the end of the boot process. Look for any text related to an IP address or interface name, like what is pictured below:

NOTE: Interface names can be ascertained by looking at what is going on as the machine boots. This is helpful for later! Refer to images below.

2. Initial Configuration Steps[edit | edit source]

2.1: VLAN Setup Prompt[edit | edit source]

- One of the first prompts you’ll see is: “Should VLANs be set up now?”

2.1: VLAN Setup Prompt[edit | edit source]

  • What is a VLAN? VLAN stands for Virtual Local Area Network. It’s a way to create separate networks within your network. For example, if you have a switch with 52 ports and want to have five different networks all connected to your router with just one cable, you’d use VLANs. However, this is way too advanced for what we’re doing here.
  • You may see a bunch of random text appear before you have a chance to respond. Don’t worry, you haven’t missed your opportunity to input. You can still type ‘n’ and hit enter when you’re ready.
  • This is just normal open-source nerd UI/UX that is not designed for normal people. You will see a lot of this. That is why we’re here!
  • For now, press ‘N’ to skip VLAN setup. We’re setting up just one local network, so VLANs aren’t necessary at this stage. You may do this later with the wifi section to have segmented wifi networks for trusted & untrusted devices & to limit their access, but that does not have to be done right now and can be done later!

2.2: WAN and LAN Interface Assignment[edit | edit source]

  • Next, pfSense will show you which interfaces are available on your device. This is where you assign the Ethernet ports for WAN (internet) and LAN (internal network).
  • Pay close attention to the bottom third of the screen. You’ll see information about which interface (e.g., em0 or igb0) has received an IP address. The interface that received an IP address is most likely your WAN interface. In my case, em0 is the interface attached to Spectrum cable internet; makes sense that it’s sad…
  • Your desktop PC is not going to “provide” an IP address to the router; it is going to try to retrieve an IP address from the router. This is how we determine that the interface that has received an IP address is the WAN interface connected to our modem.
  • The names of these interfaces may vary depending on your hardware and pfSense version. Don’t worry if they don’t match exactly what you see in this guide.

When prompted:

  1. Enter WAN Interface Name:
    • Input the name of the interface that received an IP address (e.g., em0).
  2. Enter LAN Interface Name:
    • Input the name of the other interface (e.g., igb0).

Confirm the interface assignments when prompted. This tells pfSense which port to use for WAN (internet) and which for LAN (local network).

NOTE: This is the IP address that you would be accessing the pfSense web interface on. This is also your “gateway” address, i.e., what your computer connects to in order to get an IP address, and before it connects to any IP outside of this subnet (subnet = other devices on your LAN, e.g., cellphone, TV, file server, etc.).

3. Configuring LAN IP Address[edit | edit source]

3.1: Default LAN IP[edit | edit source]

After assigning interfaces, pfSense will show you the default LAN IP address, usually 192.168.1.1.

This is the IP address of your router (pfSense) within your local network.

Any device that connects to the router will be assigned an IP address in the 192.168.1.x range by default. For instance, your PC may grab an IP of 192.168.1.46, 192.168.1.16, etc., if set to connect automatically via DHCP (Dynamic Host Configuration Protocol).

DHCP means when you connect to a router it grabs an IP address/DNS server/etc. to you by default, “Plug N Play” style. This is the default configuration of most devices you will ever connect to the internet unless you went out of your way to re-configure them. This includes your computer, cellphone, game console, IoT devices, security cameras, etc. They’re all connecting via DHCP.

3.2: Changing the LAN IP (Optional)[edit | edit source]

Requirements:

You don’t need to change this unless you have a specific reason to do so, such as conflicts with other networks you’re using. I have chosen to change it, and will be working with the following configuration throughout this guide. You do not have to follow what I am doing, but if you want to be able to copy & paste along with me addresses of things, feel free to do it this way, it won’t hurt.

  1. Set Interface IP address
    • The number for the LAN interface was 2 in my case
  2. Configure the new LAN IPv4 address via DHCP
    • Choose n
    • This isn’t referring to having DHCP so that clients who connect can get an IP address. This means should this interface have a dynamic IP itself, meaning the the router/gateway would have a different IP each time we connect to it. There is no need for this.
  3. Enter the new LAN IPv4 address
    • 192.168.5.1 is my LAN IPv4 address that I will choose for my router.
    • This is where your pfSense router will be accessible via web browser. This will be your gateway address, and this will be your DNS server.
  4. Enter LAN IPv4 subnet bit count
    • 24 is the subnet bit count
    • (This is shorthand for a subnet mask of 255.255.255.0).
  5. IPv4 upstream gateway address
    • Press enter for none.
  6. Configure IPv6 address for LAN interface via DHCP6
    • Press y , we’re not using IPv6 in this guide anyway.
    • I hit y, you can hit n and specify an address manually, but I will not be using IPv6 so it makes no difference to me, no need to specify an address I have to remember for something I will never use.
    • You’re welcome to set up an IPv6 home network if you want; I am not covering that here.

3.3: DHCP Setup[edit | edit source]

  1. DHCP (Dynamic Host Configuration Protocol) automatically assigns IP addresses to devices on your network. This makes it easier to connect new devices without manually configuring IP settings on each one. This is what allows clients to be able to get an IP address automatically as soon as they connect via Wi-Fi or with an ethernet cord into your switch. You want this so that by default people can go online without having to specify their IP manually.
  2. When asked if you want to configure DHCP, choose Yes.
  3. Set the DHCP range. This is the range of IP addresses that will be assigned to devices on your network. For example:
    • Start Address: 192.168.5.2
    • End Address: 192.168.5.254
  4. Since we have our router on 192.168.5.1, the next address that’s available is 192.168.5.2 which is the start, and 192.168.5.254 as the end.
  5. For Do you want to revert to HTTP as the webconfigurator protocol, choose n. No need to use HTTP instead of HTTPS. We’re never going to connect to this without a VPN anyway, so HTTP vs HTTPS isn’t the biggest security deal in the world, but it’s a good practice to use HTTPS whenever possible.

This allows up to 254 devices on your local network, which is more than enough for most home setups. If you have more than 254 devices at home, you’re likely not reading a beginner’s guide from a board repair person cosplaying as a sysadmin.

If you want to go crazy, you can do a different setup entirely: change the LAN IP to something even less common if you want to avoid conflicts, such as 172.16.10.1 as a LAN IP, subnet 24. This would allow 254 devices that would be given IPs such as 172.16.10.2, 172.16.10.30, etc.—and your pfSense router web interface would be accessible on 172.16.10.1. When you connect to other people’s networks, if you don’t disable LAN access in the OpenVPN android client, and their network has a 192.168.1.1, and yours has a 192.168.1.1… You see where this is going. Chances are they don’t have a 192.168.5.1 though.

NOTE: If both your home network and a remote network you’re connecting from via VPN use the same IP range, you can end up with routing & connectivity issues. Let’s say you’re at a coffee shop. You connect via wifi. On their network, you are 192.168.1.3. You connect to your home network via your VPN, and you want to connect to your local mailserver… but you both have the same pos linksys wrt54g router, which defaults everyone to 192.168.1.*. so you try to connect to 192.168.1.3. Do you see where this is going?

Changing your home network to a less common IP range can mitigate this risk. Always check the IP range of networks you frequently connect to and adjust your home network accordingly. Or, just make yours some weird-ass number that nobody else will be using. The latter works for me.

4. Finishing Up[edit | edit source]

At this point, the basic configuration is complete. You can now:

  1. Unplug the monitor, keyboard, and mouse from your pfSense device.
  2. Put away your keyboard and mouse.
  3. Turn your cable modem off for a minute or two, and then plug it back in. Some modems get mad when you plug in a new router.

NOTE: Configuring the LAN IPv4 address and subnet mask sounds confusing if you’re used to plugging in your 50 year old Linksys WRT54G & getting going. It’ll get easier with time, but for now, let’s go over what some of these pieces do. You can always come back to this later.

What is the LAN IPv4 Address? The LAN IPv4 address is the IP address assigned to your router on your local network. All your devices from your computer, phone, or smart TV(if you are reading this and still using a smart tv…) use that address as the “gateway” to get to the internet & also to communicate with each other. The default configuration is that pfSense assigns 192.168.1.1 as the LAN IP address. This is the norm for most routers.

  • This address is special because it tells devices where to send data when they want to leave your network. For example, if your PC needs to visit apple.com, it sends the request to the router’s LAN IP (192.168.1.1, otherwise known as the gateway), which then forwards it to the internet.
  • If you’re not changing anything, you can stick with the default (192.168.1.1). I change it because everyone uses 192.168.1.1. If you use a VPN or other networks frequently, changing it to something like 192.168.5.1 can avoid headaches down the line. If I am trying to connect to 192.168.1.1 on my home network, but 192.168.1.1 is the gateway IP of the wifi router my phone is connected to at my friend’s house… you see where this gets confusing.

What is a Subnet Mask? A subnet mask is what defines the “size” of your local network. Your LAN is like a neighborhood; the subnet mask is like a property line that goes over how many houses can fit in the neighborhood.

  • The default subnet mask for most home networks is 255.255.255.0. This tells your router that there can be up to 254 devices(playstations, phones, computers, etc) connected to your network. That’s a lot. If you have more than 254 devices in your house, you’re probably not reading this guide.
  • This subnet mask is written abbreviated as /24 because the first 24 bits (the 255.255.255 part) of the address are fixed while it’s only the last 8 bits are available for device addresses.

Why Configure a Static LAN IP? When you assign a static LAN IP to your router, you’re making sure that its address never changes. It would make no sense to have a router IP that changes constantly. Your servers & devices all need to connect to the router, so keep the router where it is. Moving it around senselessly makes no sense. It would be akin to Walmart changing its address every day.

  • Imagine your router’s address was constantly changing. One moment it’s at 192.168.1.1, and the next, it’s at 192.168.1.87. Your devices would be as confused as I am when I call a New York state tax office.
  • By giving a static IP like 192.168.5.1 to the router, I’m making sure that everything in your network knows where to go.

Step-by-Step explanation if you’re still confused:

Set Interface IP Address: - When it asks you to “Set interface IP address,” this is where you’re assigning the LAN IPv4 address. Think of it as giving your router its permanent address in your local network. Enter 2 to configure the LAN interface.

Configure the New LAN IPv4 Address: - Here, you’re telling pfSense what address you want to use for the router. For example, 192.168.5.1 makes your router accessible at that address. - Remember: This is the gateway address that all your devices will use to connect to the internet. Write it down somewhere because you’ll need it later to log in to the pfSense web interface.

Enter LAN IPv4 Subnet Bit Count: - This is where you specify the subnet mask abbreviated. For most home setups, the bit count is 24, aka 255.255.255.0. This allows up to 254 devices to connect to your network. If you’re just starting out, stick with /24. - To keep it simple when you see 192.168.5.0/24 what they mean is everything from 192.168.5.1 to 192.168.5.254. - Why not use a bigger subnet? Because you’re reading a beginner’s guide. How about you get one device to work in your broom closet before going for over 254?

IPv4 Upstream Gateway Address: - This is asking if your LAN interface needs a separate gateway to reach the internet. Since your router is the gateway for your LAN, just press Enter to leave this blank. - Your LAN doesn’t need to forward traffic anywhere else because the router handles it.

Configure IPv6 Address for LAN Interface via DHCP6: - You’re not using IPv6. Forget about IPv6 for now. We’ll get to how this makes using your VPN a nightmare later on. If you are not a datacenter or a sysadmin for amazon web services, you have no need for ipv6 in your life at this stage.

5. Accessing the pfSense Web Interface[edit | edit source]

Now that the basic network setup is complete, you can access the pfSense web interface to configure more advanced settings.

  1. On your desktop computer (connected to the LAN port), open a web browser.
  2. Go to https://192.168.5.1 or https://pfSense.home.arpa.
  3. You may see a security warning in your browser. This is because pfSense is using a self-signed SSL certificate, which is fine for local networks. Click “Advanced” and proceed to the site.
  4. Log in with the default credentials:
    • Username: admin
    • Password: pfsense
  5. Once logged in, you’ll be prompted to change the default password. Set a strong password to secure your router.

5.1: Initial Web Setup Wizard[edit | edit source]

  1. Set the Hostname:
  • Choose a hostname for your pfSense router. This can be something simple like “pfsense” or “home-router.” You will be able to access the router at pfsense.home.arpa once we set everything up with DNS later, instead of having to visit the router’s web interface based on its IP address. If you typed roflcopter into this box, you would be able to access your router at https://roflcopter.home.arpa rather than typing in https://192.168.5.1 – you get the idea.
  1. Set DNS Servers:
  • For now, you can use a public DNS provider like Google DNS (8.8.8.8), but we’ll replace this with AdGuard DNS or similar later for ad-blocking.
  • Uncheck the option to “Allow DNS server list to be overridden by DHCP/PPP on WAN,” so your ISP cannot override the DNS settings you choose.
  1. Time Zone:
  • Set the correct time zone for your location (e.g., US Central if you’re in Texas).
  1. Final Steps:
  • Once these settings are configured, hit “Next.” It’ll ask you to configure the WAN interface. Unless you have a funky setup, you need not change anything here. This is not for you to mess with.
  • It’ll ask you to configure the LAN interface again, but you need not touch anything, remember we already did this and the settings you put in earlier should be what shows up.
  • It’ll ask you to make a secure password; it is a good idea to set a secure password and save it in a password manager. No post-it note on the monitor nonsense!
  • You’ll be taken to the final page where you can apply the settings and restart the web interface.

6. Final Check and Preparing for the Next Steps[edit | edit source]

At this point, pfSense is fully installed, and the basic configuration is complete. Here are some final steps and checks:

  1. It’s a good idea to restart your cable modem when you make these changes, especially if it was previously connected to another router.
  2. You might want to reset the internet connection on the device you’re using to access the pfSense web interface, especially if it was connected to a different network before.
  3. Before we move forward to setting up additional features (like ad-blocking), make sure your internet connection is stable and working as expected.
  4. Test your internet connection by browsing the web from a device connected to the LAN.
  5. Remember, you can now manage everything through the web interface. You shouldn’t need to directly connect to the pfSense device with a monitor and keyboard again unless something breaks. Put the keyboard, mouse, and monitor plugged into that pfSense device away; we’re (hopefully) never touching that again. If you are, that means something bad has occurred.
  6. If you encounter any issues, re-check everything you did.

Congratulations! Your pfSense router is now set up and ready for use. Now the real fun begins. :)

Setting Up FreeDNS for Dynamic DNS[edit | edit source]

Why Do You Need Dynamic DNS?[edit | edit source]

Your IP address changes.

Your IP address is like your home address or phone number. You want this to be static - as in, doesn’t change. Imagine if all of the road names and highway exits changed each day, or if your friend’s phone number changed every day. This would be a mess. How would you know who to call? It would be very confusing. This is how it is when you have a dynamic IP.

Most of you setting up a home server likely have a residential internet plan from providers like Spectrum, AT&T, or Verizon. Unlike professional hosting services with static IPs, residential plans assign dynamic IP addresses that change as often as the relationship partners of people with borderline personality disorder. This can be a problem when you want to access your home network remotely.

What if you had a speed dial button that automatically kept track of that friend’s changing number, and just allowed you to reach your friend every time you pressed on their name? That’s how a dynamic DNS works.

Even if you DO manage to memorize 33.484.382.1, imagine having to memorize a new number every week. Or every day!

And what if it changes in the middle of the day? Imagine having to check your IP address every day, or calling home & going “hey honey, can you go to whatismyip.com and give me the number so I can add something to my calendar? Thanks!”

That would be horrible.

What you want to do is go to chrisserver.mooo.com or mysite.ddns.net and it takes you right to your server, every time. This is possible because someone else can do the work of keeping track of your router’s IP address and assigning it to that domain name. Or, something. That thing is a dynamic DNS provider.

This is where Dynamic DNS comes in handy. It automatically updates a friendly hostname to point to your current IP address, so you can always access your home network using a consistent address.

Setting Up FreeDNS[edit | edit source]

Step 1: Register on FreeDNS[edit | edit source]

We’re going to use a service called FreeDNS. It’s free, easy to use, and even has some fun domain options.

1.1 Create a FreeDNS account[edit | edit source]

  1. Visit FreeDNS: Go to freedns.afraid.org.
  2. Register: Click on “Sign up Free” in the lower center of the page.
  3. Fill out form: Fill in the required fields (username, password, and email) and click “Create Account”.
  4. Verify your account by clicking the link in the confirmation email.

1.2 Log into FreeDNS & create subdomain[edit | edit source]

Continue with the steps to set up your subdomain as needed.

This is going to be the “website name” we associate with our home server internet connection. When you visit rossmanngroup.com, this actually means 208.113.140.53. When you type http://rossmanngroup.com in your browser, you’re asking your browser to go to 208.113.140.53 and knock on port 80 to serve us a website. When you type https://rossmanngroup.com in your browser, you’re saying we’re going to 208.113.140.53 and knocking on port 443 to be served a website with https/ssl.

The subdomain enclosed in red in the screenshot above is the first part of the website name, and the domain enclosed in green is the second part of the website name. The destination enclosed in blue is where our combined website name leads us. So, louishomeserver.chickenkiller.com in the configuration above, would lead us to 8.8.8.8

  1. After clicking the activation link from the FreeDNS email, you should be immediately logged in. You should save the username & password they gave you in a password manager.
  2. Add a New Subdomain: Once logged in, click on “Add a subdomain” in the middle of the screen from the main menu. Or, click Subdomains on the left side menu.
  3. Fill out the fields:
    1. Subdomain: Choose a custom name (e.g., “louishomeserver”). That’s the part I circled in red in my screenshot above.
    2. Domain: Select one of the available free domains (e.g., chickenkiller.com). This is the green field in my screenshot above. You can get your own pretty, custom named .com address, but you’ll have to pay for it.
    3. Destination: Here’s the trick - put in a WRONG IP address on purpose (e.g., 8.8.8.8). This will help us confirm if our setup is working later.
  4. The entire point of this is for our router to constantly update FreeDNS by telling it what our IP address is. If we put what our IP address is RIGHT NOW in this field, we won’t know for sure if pfSense is working properly with FreeDNS. We’d have to debug it through log files. Ew.
  5. Click “Save” to create your hostname.

NOTE: Setting an incorrect initial IP address lets us test that pfSense is correctly updating the dynamic DNS entry. This diagnostic step is an important one; screw things up & make sure that the system you put into place to auto-fix-it fixes it. This is far less dangerous than the alternative, which is “assuming that it works.”

1.3 Get the update URL from FreeDNS[edit | edit source]

The update URL is the URL pfSense will access to tell FreeDNS that your domain name’s IP address has changed & should change to the IP that your router is accessing that FreeDNS URL from.

  1. After saving, click “Dynamic DNS” from the upper left menu of choices.
  2. You’ll see your new subdomain at the bottom.
  3. Right-click on the “Direct URL” link next to your hostname and copy the link address.
  4. This URL is how we will update our IP address automatically. DO NOT SHARE THIS WITH ANYONE OR THEY WILL BE ABLE TO MESS WITH YOU ENDLESSLY BY CHANGING THE IP THAT YOUR NEW WEBSITE NAME ATTACHES TO AWAY FROM YOUR SERVER!!

NOTE: The Direct URL contains what is like a “password” necessary for updating your dynamic DNS record. Keep this URL secure and don’t share it publicly unless you want your dynamic dns domain name redirecting to goatse

1.4 Leave FreeDNS page open & make sure it has the WRONG IP for you.[edit | edit source]

  1. Either the dynamic DNS page on FreeDNS OR the subdomains page on FreeDNS. Make sure the IP address is as we entered before, which is 8.8.8.8.
  2. IT IS IMPORTANT THAT THIS IP ADDRESS NOT BE YOUR IP ADDRESS! WE WANT IT TO BE WRONG!
  3. Make sure it is still set to the 8.8.8.8 I told you to set it to before.
  4. If it is not, set it to 8.8.8.8.
  5. Reload both pages. Still 8.8.8.8? Good.
  6. We want this to be wrong – it changing from “wrong” to “not wrong” when we finish our work will mean that our setup works.

Why are we doing things this way? It takes an insignificant amount of extra time to do things like this, as a check against everything; from software glitches to my own carelessness and absent-mindedness.

Starting with the assumption that nothing works properly allows us to figure out at the very beginning if everything has been configured properly. Starting with the assumption that nothing works will allow us to figure out if our system ACTUALLY works BEFORE WE NEED IT TO WORK!!

You’ll see when we are testing certain features and functionalities and software like syncthing later that this comes in very handy. In the video, a connection will work & have a green checkbox the first time, but local discovery will not work the second time, even though the server & client IPs remain unchanged.

NEVER ASSUME A FIX WORKS WITHOUT BREAKING THE THING IT IS SUPPOSED TO FIX AND SEEING WHAT IT DOES.

Assume that nothing works, especially your own brain, and you will create systems that guard against much more than your own human error!

Step 2: Configuring pfSense for Dynamic DNS[edit | edit source]

pfSense has to talk to FreeDNS regularly to tell it our IP.

2.1 Log into pfSense[edit | edit source]

Open the pfSense web interface and log in, at https://192.168.5.1 or https://pfsense.home.arpa

2.2 Enter Dynamic DNS settings[edit | edit source]

In the pfSense dashboard, there is a menu on the top. Go to Services > Dynamic DNS.

2.3 Enter Dynamic DNS entry[edit | edit source]

  1. Click the “+ Add” button to add a new entry.
  2. Configure the Settings:
    • Service Type: Select “Custom” from the dropdown. This might seem counterintuitive since “freedns” exists as an option in this dropdown menu, but trust me here. You trust me… right? This is the green box in the screenshot I provided above.
    • Interface to Monitor: Select WAN (your external internet connection), this is the part circled in purple above.
  3. Interface to send update from: Select WAN, the part circled in purple above.
  4. HTTP API DNS Options: I check “Force IPv4 DNS resolution” because I have been scarred by my residential internet service provider’s issues with IPv6 before Spectrum bought Time Warner Cable. You don’t have to check this, but I check it because I hate IPv6 & have it turned off entirely in my own setup. I shouldn’t be passing my prejudices onto my children. But here I am passing this one onto you :’(
  5. Update URL: Paste the Direct URL you copied from FreeDNS. Everything after the question mark in this URL is like your password and username combined.
   IF YOU ARE USING CLOUDFLARE, you need to use your Zone ID as the username and the token you just created (with the Zone.DNS - Edit permission) as the password. Otherwise, if you use the token alone, the status will always appear green, but you won’t be able to connect. You might end up spending four hours, like I did, debugging all sorts of issues until you finally find the answer in a four-year-old Reddit post. Also, make sure you disable proxy on cloudflare.
  1. Max Cache Age: When will this run? By default, this runs when an update is forced by you or when the router notices the WAN address (the IP address your ISP assigned to you) has changed. I see no harm in having it update once per day. If there’s any sort of stupid bug or issue or crap where it tried to update & failed because the wind was blowing the wrong day, packet loss, etc… It costs literally nothing to do this, we’re in 2024, even people in the middle of nowhere have 768/128k DSL… There is zero downside to setting this to check at the minimum allowed interval, of once per day. “Inspect what you expect” as my stepmom would say, who was director of the Brookfield library. :) She could tell you firsthand that….. Nothing you expect people (OR COMPUTERS) to do, will they actually do.
  2. Description: Add something like “FreeDNS IP Update” to remember what this is for.
  3. Save the Configuration: Click “Save and force update” to store your Dynamic DNS settings.

If it went well, the two areas I circled in red above should look similar to mine. A green checkmark under “status”, and the “cached IP” should be your actual IP address that you see when you go to a site like whatismyip.com

Step 3: See if Dynamic DNS actually works[edit | edit source]

We purposely put an incorrect IP of 8.8.8.8 in there rather than our real IP address to make sure this actually works. Now we’re going to see what happens when we try to get it to work.

  1. Go to the pages I had you keep open before, the dynamic DNS page on FreeDNS OR the subdomains page on FreeDNS.
  2. The IP was 8.8.8.8 before. Has it changed to the IP address that you see when you visit whatismyip.com, that is the WAN address in pfSense? If it is, you did good.
  3. Another way: Force an IP Change:
  4. Disconnect and reconnect your home internet connection to force your ISP to assign a new IP address.
    1. You can do this by rebooting your modem or temporarily disconnecting your internet connection.
    2. Sometimes, you may not be able to get a new IP, and that’s ok!
    3. Sometimes, you can’t get a new IP from your ISP immediately.
  5. As long as you input an incorrect IP address into the FreeDNS field for your subdomain when adding your subdomain to FreeDNS, and you saw it change to your WAN IP when you set up FreeDNS dynamic DNS in pfSense, you are fine.

Verify DNS Resolution[edit | edit source]

To make sure your new hostname resolves to your home IP address, perform a DNS lookup from any device:

  1. Open a Terminal or Command Prompt:

    • On Linux or macOS, open Terminal.
    • On Windows, open Command Prompt.
  2. Run an nslookup Command:

    nslookup louishomeserver.chickenkiller.com

Replace louishomeserver.chickenkiller.com with your actual hostname.

  1. Verify the Result:

    • The output should show your current public IP address associated with your hostname.
    • This confirms that your dynamic DNS is working correctly.
    • You could also just use ping.
    ping louishomeserver.chickenkiller.com

Does it ping your IP address? You’re good.

Why This Setup Is Important[edit | edit source]

With this dynamic DNS setup, you no longer have to remember or manually track your public IP address, even when it changes. By using a hostname like louishomeserver.chickenkiller.com you can always access your home network remotely, no matter where you are or how often Spectrum goes down & changes your IP on you.

This is useful for accessing home servers or services from outside your network via OpenVPN (next section!). pfSense Dynamic DNS service with FreeDNS makes sure that my chosen hostname always points to my current IP address. No matter how often my IP changes, I don’t have to change configuration settings in my programs.

OpenVPN: Setting up Secure Access from Anywhere[edit | edit source]

Why OpenVPN? Why do I need this?[edit | edit source]

Because opening ports for personal use is a bad idea!

but louis, every website & hosting provider opens ports!”

Webhosts and datacenters open ports so that millions of people can access their services. You’re opening ports to access a porn server in your closet. You’re not the same.

Listing the ports we’d have to open.[edit | edit source]

Each one of these things needs its own open port on your router. That’s like having a house with 15 different doors, each one made of cardboard with a cutout in the middle allowing them to see in. No, we’re not doing that.

  • Immich to do machine learning on your photos, because your self-image isn’t bad enough as it is.
  • Home Assistant to pretend you’re Tony Stark
  • Syncthing because screw Google.
  • MailCow because you think you can run email better than Google (if you’re reading this guide, you probably can’t)
  • Frigate to catch your neighbor stealing your packages
  • OnlyOffice because you’re too cheap for Microsoft 365
  • FreePBX because… actually, I don’t know why you’d torture yourself with that. Lenny makes it worth it. Maybe

Why Opening Every Port is Dumber Than an 820-2330 Macbook’s hinge design[edit | edit source]

Here’s why exposing all of this directly is a terrible idea:

You’re Advertising What You’re Running: Any script kid with a port scanner can see exactly what you’re running.

Your Software is Probably Full of Holes: These projects are great, but they have 10,000 users, 5 of which believe they are entitled to 25 years of updates & bugfixes after a $3 donation, maintained by one person in their spare time, whose users are assholes that think feeding yourself off of your work is too much to ask for.

If I were smart (and evil), I could make a list of:

  • Every IP address
  • What software they ran
  • What version they ran

Then, I’d keep up with exploits/vulnerabilities that are announced in the news. I’d go back to my list & double check to see who’s running that software, and see if they work. At best, you become part of a botnet and waste some electricity mining my crypto. At worst, I’ve stolen all of your data & use it to blackmail you.

I like these programs; they’re fun software! But, similar to my taste in relationships; it isn’t about who I like. It’s about who I trust. The software I have the most fun with isn’t who I’d trust with banking credentials (or my future children). Maybe I got that the wrong way around….

OpenVPN: Only 1 Port to open, with better security:[edit | edit source]

One Port to Worry About: Instead of 15 points of failure, we have one potential point of failure.

NOTE: OpenVPN uses a single port for all traffic, which is usually port 1194 UDP. Most OpenVPN servers will default to port 1194. Make sure your ISP didn’t block this. Bad ISPs will block ports commonly used for running servers so you pay 5x as much for the same internet unless you buy a “business”(extortion) plan. I paid $409.99/mo for 10 mbps upstream when I had a store in New York; hint, you’re not paying extra for better internet..

Stealth Mode: To the outside world, you’re just running OpenVPN. They can’t see your unpatched version of hellanzb from 2007. (shout out to pjenvey if he’s reading this today!)

OpenVPN security in four pictures:[edit | edit source]

Here is what it’s like opening ports to a bunch of random open source projects people make in their spare time:

Here is what it’s like only opening a port for OpenVPN.

When you use OpenVPN, you are opening one port to get access to your network, with a door that many commercial interests have a stake in keeping strong. When you open ports for random crap, you have windows people can look through, and doors that look like… Well… Yeah. And 2 guys watching them.

Decreasing Attack Surface with OpenVPN is a best practice[edit | edit source]

OpenVPN isn’t a hobby project coded by your cousin’s methhead roommate. This is used by everyone, from companies with more money than sense to just about anyone who doesn’t want their data plastered all over the internet:

  • Having ONE service open to the public rather than 10 means a smaller attack surface.
  • Having one service
  • OpenVPN is designed with one purpose in mind, a secure connection.
  • It is over 20 years old.
  • Commercial interests (aka people actually paying money for software that rely on it for their infrastructure, not this guy) use & rely on it.
  • There are more eyes on the code of OpenVPN than hellanzb.

Marketing wankery? …Kind of, but they’re not lying here.

Is this 100% accurate? No. Are more people for whom millions of dollars rides on the security of their software using OpenVPN than hellanzb. Yes!

Having a home server is cool. But the programs we’re talking about are used by 0.0001% of 0.000001% of the world. OpenVPN can still have vulnerabilities; it isn’t perfect! But remember, in the world of network security, nothing is perfect! This isn’t about being perfect. It’s about controlling what we can control, and minimizing risk & attack surface every chance we can. A UFC fighter makes a better bodyguard than a mall cop, regardless of the fact that they’re equally useless against a bomb or a comet.

This guide walks you through the process of setting up OpenVPN on pfSense. OpenVPN allows you to access your home network as if you were there.

All of the services we want to use require having access to this network we are placing our server on, from anywhere. This setup will make sure that all traffic from the phone is routed through the VPN with no DNS leaks, which will be important for our adblocking-via-router section that comes after.

Setting up OpenVPN within pfSense for secure access[edit | edit source]

Step 1: Install OpenVPN Client Export package in pfSense[edit | edit source]

This will make it way easier for us to create the files necessary for clients to connect. You click a button and it’ll generate a file that you put on your phone or laptop. You open the OpenVPN client, import the file, put in your username & password, & boom – you’re set.

1.1 Log into pfSense:[edit | edit source]

1.2 Install the package

  • Go to System > Package Manager > Available Packages.
  • Search for “openvpn-client-export”.
  • Install the OpenVPN Client Export Utility.

Step 2: Set up Certificates[edit | edit source]

2.1 – Make a Certificate Authority[edit | edit source]

The Certificate Authority (CA) is what signs and verifies the server and client certificates used to establish secure connections. You don’t have to have any idea what that means to use a VPN. Here’s how we create the CA in pfSense:

  1. Log into pfSense:
  2. Open your browser and go to your pfSense IP address (e.g., https://192.168.5.1 or https://pfSense.home.arpa).
  3. Log in with your credentials (default: admin / pfSense unless changed).
  4. Navigate to the Certificate Manager:
  5. Go to System > Cert Manager in the top navigation menu.
  6. Create a New CA:
  7. Under the CAs tab, click the + Add button to create a new Certificate Authority.
  8. Fill in the CA Details:
    • Descriptive Name: OpenVPN-CA (or any name you choose)
    • Method: Create an Internal Certificate Authority
    • Key Length: 4096 bits (recommended for strong security)
    • Digest Algorithm: SHA-512 (for secure hashing)
    • Lifetime (days): 3650 (about 10 years)
    • Distinguished Name:
      • Country Code: Your country’s two-letter code (e.g., US for the United States)
      • State or Province: Your state or province
      • City: Your city or locality
      • Organization: Your organization name
  9. Common Name: OpenVPN-CA (or another descriptive name)
  10. Save the CA:

2.2 - Creating the OpenVPN Server Certificate[edit | edit source]

Next, create the server certificate that the OpenVPN server will use for secure client connections.

  1. Navigate to the Certificates tab in Cert Manager.

    • Add a New Server Certificate:
  2. Click + Add/Sign to create a new certificate.

  3. Fill in the Server Certificate Details:

    • Method: Create an Internal Certificate

    • Descriptive Name: OpenVPN-ServerCert – name it something that makes it easy to identify as a SERVER certificate later for OpenVPN

    • Certificate Authority: Select OpenVPN-CA (the CA you just created)

    • Key Length: 4096 bits

    • Digest Algorithm: SHA-512

    • Certificate Type: Server Certificate.

      WARNING: Make sure you do not leave this set to user certificate, which is the default option.

    • Lifetime (days): 3650

    • Distinguished Name: Match the details you used for the CA

    • Common Name: louis.chickenkiller.com (you can use whatever you put for your dynamic DNS domain name here)

  4. Click Save. You should now see OpenVPN-ServerCert listed under the Certificates tab.

2.3 Create a VPN Group for your VPN users[edit | edit source]

To connect your Android phone to the VPN, create a user account with an associated client certificate.

Log into pfSense:

Open User Manager:

Go to System > User Manager.

Add a New Group:

  • In the Groups tab of User Manager, click the + Add button to create a new Group.
  • Fill Out the Group Information:
    • Group name: Choose a group name that makes sense for VPN users (e.g., vpnusers).
    • Click Save.

2.4 Create a VPN user[edit | edit source]

  1. In the Users tab of User Manager, click the + Add button to create a new user.
  2. Fill Out the User Information:
    1. Username: Choose a username (e.g., vpnuser1).
    2. Password: Enter a strong password.
  3. Add the user to the vpnusers group you just made.
  4. For Certificate, check “Click to create a user certificate”. DO NOT FORGET TO CREATE A USER CERTIFICATE FOR THE USER.
  5. Create a name for the user certificate, such as vpnuser_client_cert so you can recognize it as the USER cert later.

BEFORE YOU HIT SAVE:

Before you hit save on adding a new user account:

  1. Scroll to the Certificates section of the user creation form:
  2. Click + Add to generate a new certificate for this user.
  3. Configure the User Certificate:
    1. Certificate Authority: OpenVPN-CA
    2. Key Length: 4096 bits
    3. Digest Algorithm: SHA-512
  4. Save the user with the certificate:
  5. Click Save.
  6. Verify User Creation. You should now see the user listed under System > User Manager > Users.

Step 3: Configure OpenVPN Server[edit | edit source]

3.1 Open the OpenVPN Wizard, and set settings according to what you see below in section 3.2 and in images above[edit | edit source]

  1. Log into pfSense:
  2. Go to VPN > OpenVPN.
  3. Click on the Wizards tab.
  4. Fill out details according to what you see above. Keep in mind that when you are DONE, you will have to go back in & edit settings for that VPN server that were NOT EDITABLE while you were creating the VPN.

3.2 OpenVPN Server Configuration[edit | edit source]

After you have finished, go back and edit that server you just made to make sure all of this matches:

  1. Description: openvpn server itself
    • This is for your reference only. You can name it something descriptive like “HomeVPN” or “MyVPNServer.”
  2. Protocol: UDP on IPv4 only
    • UDP is faster and more efficient for VPN traffic, and IPv4 only is typically sufficient unless you have a specific need for IPv6.
  3. Interface: WAN
    • This setting makes sure that your OpenVPN server will listen for incoming VPN connections on the WAN interface.
  4. Local Port: 1195
    • Default is 1194 and TOTALLY FINE. I chose 1195 because I already use 1194 for another system.
  5. TLS Authentication: Enabled

Cryptographic Settings

  1. DH Parameters Length: 4096 bits
    • Stronger than the default 2048-bit encryption.
  2. Data Encryption Algorithms:
    • The following algorithms are listed in the priority you selected:
      • AES-256-GCM
      • AES-128-GCM
      • CHACHA20-POLY1305
  3. Fallback Data Encryption Algorithm: AES-256-CBC
    • Used for compatibility if a client doesn’t support GCM encryption algorithms.
  4. Auth Digest Algorithm: SHA-512
    • SHA-512 provides a high level of integrity protection for your VPN packets, making sure that the data hasn’t been altered.
  5. Hardware Crypto: Intel RDRAND engine - RAND

Tunnel Settings

  1. IPv4 Tunnel Network: 192.168.6.0/24
    • This is the virtual network that your VPN clients will use.
  2. Redirect IPv4 Gateway: Checked
    • This forces all client traffic through the VPN tunnel.
  3. IPv4 Local Network: 192.168.5.0/24
    • This allows VPN clients to access your local network.
  4. Allow Compression: Refuse any non-stub compression (Most Secure)
  5. Type-of-Service: Unchecked
  6. Inter-Client Communication: Unchecked
  7. Duplicate Connections: Unchecked

Client Settings

  1. Topology: Subnet
  2. DNS Default Domain: newvpn
  3. DNS Server 1: 192.168.5.1
  4. DNS Server 2: 94.140.14.14 (AdGuard DNS)
  5. DNS Server 3: 94.140.15.15 (another AdGuard DNS server)

Advanced Client Settings

  1. Dynamic IP: Checked

  2. Advanced Configuration:

    • Custom Options:

      tun-mtu 1200; mssfix 1160; push "dhcp-option DNS 192.168.5.1";
  3. Gateway Creation: IPv4 only

    For the Gateway creation OpenVPN server setting: CHOOSE IPv4 only This will save you lots of hassle and misery later! Explanation at the end of the OpenVPN section.

NOTE: Let’s talk about RDRAND. This is the hardware random number generator (RNG) built into Intel processors. It’s fast, easy to use & pfSense might already be using it. WARNING: For 99% of the people reading, this will be a total waste of time.

1. What is RDRAND? Why care?

RDRAND makes random numbers using your CPU. but it’s a closed source black box. You can’t see how it works, and there have been [concerns that some random number generators might not be as random as you’d like. There are all sorts of pissing matches going on over this stuff on the internet by people way smarter than you or I.

Point being, if you care about privacy or you’re handling sensitive data, you might not want to rely solely on a system you can’t inspect. At the same time, if you’re reading this guide, you’re enough of a newbie that rdrand is not going to be how someone “gets” you.

2. Why not use just RDRAND?

While it is fast, if the hardware random number generator fails or is compromised, your security goes down without noticing. A VPN depends on top-notch randomness for encryption, so you need more than one source of entropy to stay safe.

3. How do I make it safer?

pfSense already mixes entropy from several sources which includes rdrand. In most cases, you’re good to go.

4. Should I disable it?

Probably not. RDRAND is fine. Think of it as an ingredient rather than the entire thing.

5. THen why did you mention it?

The “uhm, akshually” people. They’re in the bushes, always waiting.

TL;DR: RDRAND isn’t bad, but don’t trust it alone. Let pfSense do its thing and mix it with other entropy sources. If you’re running anything highly sensitive and don’t like trusting Intel, you can disable it—but for most people, you’ll be fine with the default settings.

Step 4: Get .ovpn file to connect your phone to the VPN[edit | edit source]

4.1 Export the OpenVPN Client Configuration for Your Android Device[edit | edit source]

  1. Go to VPN > OpenVPN > Client Export.
  2. For “remote access server,” choose the OpenVPN server you made.
  3. For “Host Name,” enter the URL you made on FreeDNS for dynamic DNS. In our case, this was louishomeserver.chickenkiller.com.
  4. Under Export Type, choose Android - OpenVPN Connect.
  5. Download the configuration file (e.g., vpnuser1-android.ovpn).

4.2 Import the Configuration into OpenVPN Connect on Android – SECURELY!!!!![edit | edit source]

  1. Transfer the .ovpn file to your Android device. DO THIS SECURELY.
  2. Install the OpenVPN Connect app from the Play Store.
  3. Import the configuration file and connect to the VPN.

VPN connectivity can be done with a certificate alone, without a username or password. This means that if you misconfigured something, and this file gets into the wrong hands, any Tom, Dick or Harry has access to your home network!

Don’t upload the file to public file transfer sites

Don’t do this. Do not store the key to your front door on megaupload.

Instead, do this:

  • Connect your phone directly to your computer with a USB cable to transfer the file; simple and secure.
  • Or, use an encrypted messenger you trust. Just make sure it’s actually secure, not just convenient.

Why the extra caution?

  • This .ovpn file is sensitive. It’s part of what allows access to your server.
  • If someone gets this file & figures out your password, they’re in. Not good.
  • And if there’s a config mistake (it happens), they might not even need the password.
  • Without this file, even if someone knows your username & password, they’re not getting in.

Treat this file like your bank details. Don’t put it on a post-it note to the 4:3 monitor in front of your Windows XP Service Pack 1 computer.

Don’t leave it lying around in your downloads folder. Don’t share it casually.

The chances of someone intercepting this file and using it maliciously are low, but we don’t take unnecessary risks with security. It’s not paranoia, it’s good practice.

Do it right, and you’ll save yourself potential headaches down the road. Plus, you’ll have the satisfaction of knowing you’ve set things up properly.

4.3 Edit Settings on OpenVPN Android Application[edit | edit source]

  1. Open the OpenVPN Connect application.
  2. Go to the three lines in the upper left corner and tap Settings.
  3. Scroll down to Advanced Settings.
  4. Switch security level from “legacy” to “preferred”.
  5. Uncheck “DNS fallback”.

NOTE: Disabling “DNS fallback” keeps the VPN connection from going back to using non-ad-blocking(and usually google) DNS when something fails. When your setup breaks, I want you to KNOW - by way of it not working. I don’t want it to training-wheels you back to a working setup using Google’s DNS.

You now have an OpenVPN server on pfSense you can connect to from anywhere in the world; your Android device will have all its traffic routed through the VPN. You’ll fully benefit from pfBlockerNG’s ad-blocking via IP blocking and DNS domain name blocking when you’re logged in through the VPN, and you’ll have access to all of the services we will be setting up for calendar, contacts, email, backup, office, home automation & surveillance, business phone, password management & more.

IPv4 vs IPv4+IPv6 & VPN nightmares:[edit | edit source]

Choosing IPv4 + IPv6 can cause issues. I’ve seen this cause random disconnects after about 10 minutes of connection that is miserable to figure out.

In my case, I am combining two of the worst things in the world: American residential cable broadband & T-Mobile on a Pixel phone. I lose 5G when I walk under a tree, and my internet goes down more often than yours.

Why using IPv4 & IPv6 with OpenVPN for this setup is discouraged.[edit | edit source]

Enabling both IPv4 and IPv6 may be the way to go for enterprise class connections. If you’re reading this, you might be stuck on horrible residential broadband & unable to pick a better ISP. In these environments, the 1% benefit IPv6 enables

  1. NAT64/DNS64 Compatibility Issues: Mobile networks often use NAT64/DNS64 for IPv6-only networks. This can clash with your VPN’s IPv6 routing, causing random failures.
  2. Path MTU Discovery (PMTUD) Quirks: IPv6 relies heavily on PMTUD. If there are issues along the path, you can have connectivity problems that are hard to diagnose.
  3. ISP IPv6 Implementation: Some ISPs (spectrum) can have less-than-great IPv6 implementations. This can lead to unstable connections when you’re trying to use both IPv4 and IPv6.
  4. Dual-Stack Timeout Issues: When both protocols are available, your devices might try connections on both. If IPv6 is unstable, you’ll experience timeouts and apparent connection failures. THIS MAKES UP FOR ANY & ALL POTENTIAL BENEFITS OF IPv6, WHICH YOU WILL NEVER NOTICE IN EVERYDAY USAGE.
  5. Carrier-Grade NAT (CGN) Interactions: The interplay between CGN for IPv4 and IPv6 routing through your VPN can lead to connection state inconsistencies.

The Practical Solution[edit | edit source]

You have two main options:

  1. Live In a Nightmare: Dive deep into network engineering, potentially spend $150,000 backhauling fiber to your house to get around your horrible cable company.
  2. A Practical Approach: Click “IPv4 only” in OpenVPN server settings.

Option #1 can gargle my balls.

Setting Up pfBlockerNG for Ad-Blocking in pfSense[edit | edit source]

Why adblock at the router?[edit | edit source]

Why not?? Isn’t this beautiful?

louis@happycloud:~/Downloads/frigate$ ping googleadservices.com
ping: googleadservices.com: Name or service not known

Seeing Name or service not known trying to contact a google ad server warms my heart. :D

Ad-blocking at the router level offers several advantages:

  1. Simplicity: Instead of installing ad-blockers on every device, you can block ads network-wide.
  2. Complete coverage: Blocks ads on devices where traditional ad-blockers can’t be installed (smart TVs, Android/iOS apps). Somewhere, there is probably some piece of garbage application that has an ad in it that you can’t install ublock origin onto. What if it were blocked from connecting at the router level?
  3. Control: You can manage internet connectivity and ad-blocking for all connected devices from a single point.

We’ll use two methods for blocking:

  • IP address blocking - blocking 103.31.6.184
  • Domain name blocking - blocking googleadservices.com

This dual approach makes sure more effective ad-blocking, as it covers both static IP addresses and changing domain names associated with ad servers.

Step 1: Measure our Baseline[edit | edit source]

1.1 Install stock Google Chrome[edit | edit source]

No ad-blocking extensions, no privacy protections. We want to test our ROUTER’S ability to block ads – not our browser’s. The browser is going to be the “constant” here. In an ideal setup, we want to block ads at the router level (which we CAN control) in order to not see ads in random Android apps & unreliable smart TVs (which we can’t always control).

You won’t always be able to block ads with certain hardware or software. And even if you can, can your boyfriend, your mother-in-law, your kids? Imagine having kids that grow up in a household with no ads. :)

Don’t use your normal web browser with all the ad-blocking stuff built-in because then we can’t tell if what we did actually worked. We’re starting by installing stock, vanilla Google Chrome, no extensions installed, and running a couple of quick tests. Something tells me Google’s business model isn’t going to provide us an ad-free web browsing experience by default…

1.2 Run adblock & DNS tests[edit | edit source]

My Initial results:

  • Ad-block tester: 38 points out of 100
  • D3Ward Ad Block testing: 6 blocked out of 135
  • DNS: Using home device (pfSense DNS resolver)

Your mileage will vary.

Step 2: Install pfBlockerNG[edit | edit source]

  1. Log in to your pfSense web interface.
  2. Navigate to System > Package Manager > Available Packages.
  3. In the search bar, type “pfBlockerNG”.
  4. Find pfBlockerNG-devel and click the Install button (you want the devel version because it receives more updates &, as AvE would say, is more betterer).
  5. Wait for the installation to complete.

Step 3: Configure pfBlockerNG General Settings[edit | edit source]

  1. After installation, go to Firewall > pfBlockerNG.
  2. Under General Settings:
    1. Enable pfBlockerNG: Make sure this is checked.
  3. Click IP next to general.
  4. For Outbound Firewall Rules, make sure both LAN and OpenVPN interfaces are selected for REJECTING.
  5. I had you set up OpenVPN before pfBlockerNG explicitly because it makes this option automatically be checked for you, but double check just in case!
  6. Click Save at the bottom.

Step 4: Set Up DNSBL (DNS Blacklists)[edit | edit source]

  1. Navigate to Firewall > pfBlockerNG > DNSBL.
  2. Enable DNSBL: Check this box to enable DNS-based blocking.
  3. DNSBL Mode: Set to Unbound Mode to use pfSense’s DNS Resolver for DNSBL.
  4. Go down to DNSBL Configuration, make sure some random bs IP is in virtual IP address (LIKE 10.10.10.1), this is where we are directing requests to ad-ridden domain names to.

Step 5: Add DNSBL Feeds & IP blocklist feeds (Lists of Ad Domains)[edit | edit source]

Let me explain how these feeds work in pfBlockerNG because the interface can be intimidating for a newbie.

The feeds tab has two main sections: IP address feeds at the top (for blocking specific IPs) and DNS feeds at the bottom (for blocking domain names like googleadservices.com).

When you’re looking at the feeds, you’ll see these checkboxes and plus signs that can be a bit confusing. Here’s what they mean:

  • If you see a checkbox on the left, that means it’s a GROUP of feeds. If you see a blue checkbox next to “PRI1” that means all the feeds under that group are already enabled.
  • Individual feeds will have their own checkboxes to show if they’re active.
  • The plus signs let you add new feeds to your configuration.

When you want to add feeds, click the plus sign to add the feed.

For IP blocklists, make sure the action is set to “Deny Both”.

For DNS blocklists, set the action to “Unbound”.

Even if you see something’s already checked, sometimes clicking “Enable All” can catch feeds that weren’t properly activated. I’ve had weird situations where I thought I added everything in a group but missed some - the interface isn’t always super clear about what’s actually enabled.

For what to block: I avoid blocking things like Tor or torrent trackers. Why would you block that? That’s like DDoSing Pornhub - they’re giving you free stuff! One of them blocks AWS, avoid that unless you want non-functional internet (sadly the world runs on AWS whether we like it or not).

It is very easy to block too much and then not be able to log into youtube, receive email, visit your bank, etc. More isn’t better here.

  1. Go to Firewall > pfBlockerNG > Feeds.
  2. Scroll to the DNSBL Feeds section.
  3. Add multiple feeds by clicking on different categories and enabling relevant lists.
  4. For each selected feed:
    • For DNS block lists, set “Action” to Unbound.
    • For IP lists, set “Action” to Deny Both.
  5. There is a blue “ENABLE ALL” method at the bottom that will often save you a lot of time.
  6. Recommended categories to add:
    • Easylist
    • Malicious
    • Phishing
    • Malware
    • Suspicious
    • Trackers
    • Spam (for email)
  7. Avoid adding feeds that might block legitimate services (e.g., AWS, public DNS servers, Tor).
  8. After selecting feeds, click Save to apply these DNSBL lists.
  9. Don’t enable/turn them on one by one. When you click on a list of feeds, note the blue “enable all” button. Don’t be like Louis of 2018 & toggle each line to “on” manually like an idiot (I actually did this :’( )

Step 6: Update and Apply Lists[edit | edit source]

  1. Navigate to Firewall > pfBlockerNG > Update.
  2. Select “Force” option.
  3. Set “Reload” option to “All.”
  4. Click “Run” to download and update all lists (both DNSBL and IP lists).

This process can take a while.

Step 7: Testing and Verifying Ad-Blocking Effectiveness[edit | edit source]

  1. Clear cache and cookies in your test browser.
  2. Revisit the ad-blocking test sites:
    1. adblock-tester.com
    2. d3ward.github.io/toolz/adblock.html -> This project is no longer maintained and has been archived.

Expected results:

  • Ad-block tester: Improved score (e.g., 78 out of 100)
  • D3Ward Ad Block testing: Many more blocked (e.g., 119 out of 135)

Step 9: Implement AdGuard DNS[edit | edit source]

  1. Visit adguard-dns.io and go to the “Routers” section.
  2. Copy the DNS server addresses that block ads and trackers.
  3. In pfSense, go to System > General Setup.
  4. Uncheck “Allow DNS server list to be overridden by DHCP/PPP on WAN.”
  5. Remove existing DNS servers and add the AdGuard DNS servers. Use what is on AdGuard’s site: at the time of this writing, they were as follows. Only use the below servers if you see them on adguard-dns.io:
    1. Primary DNS: 94.140.14.14
    2. Secondary DNS: 94.140.15.15
  6. You checked AdGuard’s site rather than copy & paste from here, right? RIGHT?
  7. Save changes.

Step 10: Configure the DNS Resolver[edit | edit source]

  1. Go to Services > DNS Resolver.
  2. Enable DNS Resolver: make sure this is checked.
  3. Click Enable Forwarding Mode.
  4. Save and apply changes.
  5. Reload the DNS Resolver service.

Step 11: Verify adblocking from Desktop[edit | edit source]

  1. Clear DNS cache and browser data.
  2. Rerun the ad-blocking tests.
  3. Visit dnsleaktest.com and run an extended test to confirm you’re using AdGuard DNS. You should see something like the figure above. Your DNS should be DIFFERENT than it was before! If not, something went wrong.
  4. Redo your adblock test:
  5. You should see adblocking become even more better, or more betterer as AvE would say, than what you had prior to installing pfBlockerNG, depending on the feeds you’ve chosen.

Step 13: Verify adblock on mobile via VPN[edit | edit source]

To make sure ad-blocking works on mobile devices connected through VPN:

  1. Clear browser data on your phone.
  2. Disconnect from the VPN we attached to earlier.
  3. Visit the following websites and note the results:
  4. Go over to the OpenVPN app & connect to VPN

Double-check that you’re using the pfSense DNS on dnsleaktest.com & NOTHING ELSE!! You do not want your ISP’s server, or anyone else’s server, to show up. If in doubt, research the IP address & hostname of the DNS that is coming up.

  1. Compare results to those without a VPN connection.

Expected results:

  • Much more ad-blocking on mobile when connected to VPN
  • Confirmation that you’re using AdGuard DNS through the VPN

Step 14: Verify VPN allows connectivity to home network.[edit | edit source]

Try to visit your router’s IP address https://192.168.5.1/ once you have connected to the VPN – and make sure you are connected to the CELLULAR network only, not your home Wi-Fi!!

Congratulations; you’ve set up an ad-blocking system that blocks a ton of ads before your internet connection even wastes bandwidth loading them, for all devices on your network. Blocking ads in a browser using uBlock Origin is fun, but nothing compares to the feeling of blocking ads they think you can’t block. It’s beautiful. :D This means that even inside of Android apps that have ads, you can block them all—it just takes the right feed. :D

REMEMBER: THIS IS YOUR JOURNEY!!! FIND THE FEEDS THAT MAKE YOU HAPPY, YOU DO NOT HAVE TO USE THE SAME ONES THAT I DID!

Installing Ubuntu Server with RAID 1, LVM, and LUKS Encryption[edit | edit source]

Now it’s time to install the operating system on our host server. I’ll walk you through the process of installing Ubuntu Server with a nice configuration including RAID 1 for boot drive redundancy, encrypted LVM for flexibility in expanding storage if we move this setup to a larger set of drives, and LUKS encryption for security. This setup makes sure your server can boot even if one drive fails, while keeping your data secure. Even if someone breaks into your house & steals all of your stuff, all they have is encrypted crap. Unless they’re the NSA, in which case you’re screwed, but if you’re reading this guide, you’re probably not that important.

Installing Ubuntu Linux[edit | edit source]

For our server’s operating system, we’re going with Ubuntu Linux. Why Ubuntu? If you’re watching this, you’re probably more of a newbie than an expert. Ubuntu is user-friendly, has good documentation, and has a huge community ready to help. It’s widely renowned as the first “newbie friendly” GNU/Linux distribution, dating back to 2006 when it was one of the few distros that didn’t require torturing yourself with ndiswrapper to get wifi working. Robert Storey put it best:

The huge collection of Linux/BSD systems listed on DistroWatch is a testimonial to how difficult it is to make a decision. However, after spending weeks trying to get XYZ distro to recognize your wireless card, it’s really nice to have an OS that just works.”

Imagine having a laptop as your only computer, before smartphones with tethering were widely available. You don’t have access to a wired connection. Where were you getting your drivers from? Maybe you do have access to a wireless connection, but your only CAT5 cable is 5 feet long. And your router is in an un-air-conditioned garage. In the middle of summer. So you go to your 98°F garage, sit on the floor, googling only to find a plethora of threads where elitist douchebags tell you to RTFM to get wifi to work.

And they wonder why people used closed source operating systems…

In 2005, the concept of anything in GNU/Linux “just working” was a joke. If you wanted to burn a CD you had to set up something called SCSI emulation to use the optical drive on your computer. From the ground up, GNU/Linux was fundamentally not designed for normal people. Ubuntu changed that in a radical way and continues to have a reputation for being a newbie-friendly “gateway drug” to GNU/Linux. It’s not the best and it has its flaws, but it is designed and developed with ease of use for normal people in mind. For a beginner’s guide, that matters.

Why Not Arch or Gentoo?[edit | edit source]

I use Arch Linux now, SuSE from 2002-2004, and Gentoo from 2004-2015. I enjoy making my life difficult for no good reason. In my 30s, I’ve come to realize that I derive sick pleasure from making my life difficult for no good reason; but I wouldn’t recommend that for beginners (or anyone). With Ubuntu, you get a system that’s easy to set up and maintain without the extra hassle, designed to be as idiot-proof as possible, and designed for normal humans to use. If you wish to use another distro, GO FOR IT! There is NO one “right way” to do any of what I am doing here!

Installing with RAID 1: Choosing Your OS Drive[edit | edit source]

We are going to be using RAID 1. RAID 1 is a mirroring setup, where we use two drives for the operating system instead of one. This means one of the drives can completely fail and the server continues running. I would suggest that you find not one, but TWO SSDs for this purpose. We will be using MDADM for RAID. Ubuntu allows you to do this upon install without having to edit configuration files.

Why software RAID using MDADM instead of hardware RAID with a RAID controller card?[edit | edit source]

RAID controller cards are for people with datacenters that have hundreds of drives and need maximum performance/resilience for specific applications, that want the task of managing these drives separate from the software running the computer. This was also very useful back when machines were powered by Pentium 1 processors.

NOTE: Some hardware RAID controllers will give you improvements in performance, but it’s not worth the downside. There are controllers where when they fail, you have to replace it with the exact same controller for your setup to work again - aka, digiorno all over again. Using software RAID like MDADM means you can take drives out of a pentium 4 and put them into a macbook and it’ll just detect it & work.

It is 2024, and even a ten-year-old computer will do software RAID just fine with no perceivable penalty in performance.

Why not use RAID built into my motherboard?[edit | edit source]

That is called “fake RAID.” Fake RAID is cancer. It is not “hardware” RAID, it is just software RAID by another name.

When you create a RAID array using the garbage built into your motherboard, the RAID configuration is sometimes stored in a proprietary format that is only readable by that specific manufacturer’s RAID implementation. I used the word “sometimes” because it depends on your system. I have no idea what system you have. I want ALL of the people reading this to have a system that works if they transfer these drives to another system, not “some” of you. It costs you nothing to use mdadm, which offers certainty of compatibility when you transfer these drives to other hardware.

When certainty & uncertainty have the same price, all other things being equal, I’ll take the certainty!

MDADM software RAID is a standardized system that transfers across computers – I am not using hardware RAID, I am not using whatever RAID is in the BIOS of your computer, because I have no idea what they are using or whether it is something standard or something that will be aggravating later. If you have to take these drives and put them in another computer, there will be less hassle using software RAID than there is using hardware RAID, it’s literally plug and play (well, you may have to use a liveCD to run grub-install to register the bootloader with the new machine’s UEFI, but… The RAID part will work at least!).

Drive recommendation for OS:[edit | edit source]

We’re going to have two drives in RAID 1. You can use more if you like – RAID 1 need not be two drives! I like Micron SSDs; they have always had consistently lower failure rates than Samsung’s budget “EVO” line for me with regards to NVME devices. I’ve RMA’d the same 2 TB Samsung EVO 970 five times now… Five… Times. You can get two budget 4 TB SSDs for under $500 now – I recommend these.

We are going to be using these SSDs for virtual machines that perform many tasks. Here are some of the storage-intensive ones:

  • Self-hosted mail. Your inbox may be 50+ GB like mine.
  • Complete phone backup of everything – can easily eclipse 2 terabytes. Mine is 1.4.
  • FreePBX phone system – call recordings over time can go over 50 GB easily.

I suggest buying drives for your operating system disk that are considerably fast and have enough space to store all of this. With regards to security camera recordings, and the backup of your 40 terabytes of recipes stored as .mkv files – that, we’ll do on an array of hard drives. You don’t need to get SSDs.

RAID IS NOT A BACKUP![edit | edit source]

IMPORTANT NOTICE: RAID 1 IS NOT A BACKUP!

Many people incorrectly believe that RAID 1 is a “backup.” It is not! RAID 1 sets up your machine so that the operating system is installed on TWO drives rather than one, with each drive being an exact mirror of the other. This way, if one drive fails while you’re using your server, it will still run. Think of RAID 1 like the green goo you can put in your tire to plug up a hole, or a spare wheel, allowing you to limp to a service center for repairs.

Here are a few reasons why RAID 1 is not a backup:

  1. Backups allow you to restore your system if you accidentally mess something up. RAID 1 is a perfect mirror, so it applies to everything you break.
  2. RAID 1 means you’re attaching two hard drives to your computer to install the operating system on instead of one. These drives are both connected to the same computer. If your computer’s power supply fails and sends incorrect voltages to the drives, both get fried.
  3. When one drive in a RAID 1 array fails, the other often fails soon after, especially if they’re the same brand and were purchased at the same time.
  4. RAID 1 works so well that you might not notice when one drive fails until the second one also fails, leaving you with no data.

NOTE: MDADM does work well enough that you won’t tell when a drive fails. Later in this guide we’re going to set it up so that your machine is constantly checking & emails you the moment there is any issue with your drive using mdadm’s monitor command.

Step-by-Step Installation Guide[edit | edit source]

What you should have

  • Two identical SSDs (e.g., Samsung 870 EVO 250GB), but bigger will be better here since we’ll be using this to backup everything on your phone + many other things.
  • A USB drive to put the Ubuntu installation image on
  • An old computer to use as a server (even a 10-year-old desktop or laptop can work)

1. Prepare the Installation Disk[edit | edit source]

Warning: This process will erase everything on the USB drive.

  1. Insert a USB flash drive (at least 4GB in size) into your computer.
  2. Go to ubuntu.com and download the LTS (Long Term Support) version of Ubuntu Server.
  3. Use one of the following methods to write the Ubuntu image to the USB drive:

Windows:

  1. Download and install Rufus.
  2. Open Rufus and select your USB drive.
  3. Click the “SELECT” button and choose the unzipped .img file you downloaded.
  4. Click “Start” and let Rufus create the bootable USB.

GNU/Linux or macOS:

  1. Open the terminal and type the following command:

    sudo fdisk -l
  2. Make note of drives in the system.

  3. Plug in the flash drive.

  4. Open the terminal and type the following command again:

    sudo fdisk -l
  5. Make note of the drive that was not present before.

  6. Double-check size/brand/model to make sure this new device is the device you plugged in.

  7. Run the following, replacing /dev/sdX with your drive, and replace the ubuntu-server.iso file with the filename of your image file. Make sure you use the right PATH, that is the directory your image is in.

    sudo dd if=/path/to/ubuntu-server.iso of=/dev/sdX bs=4M status=progress

Your bootable USB drive with Ubuntu Server Linux is now ready for use!

2. Boot from the USB Drive[edit | edit source]

  1. Insert the USB drive into your server.
  2. Power on the server and enter the boot menu (usually by pressing F12 or another function key).
  3. Select the UEFI option for your USB drive.

3. Begin the Ubuntu Server Installation[edit | edit source]

  1. Choose “Try or Install Ubuntu Server” from the boot menu.
  2. Select your language and keyboard layout.
  3. Choose “Install Ubuntu Server” (not the minimized version).
  4. Select “Search for third-party drivers” for better hardware support. Don’t check this box if you want to live Richard Stallman’s ethics. Check this box if you want to reduce the chances of random things in your computer not working. I check the box. I’m going to hell, I know….

4. Configure Network[edit | edit source]

4.1 Why a Static IP?[edit | edit source]

We are going to set up a server that we are going to consistently access. This means we always want it to be at the same place.

Imagine trying to deliver mail to someone who lives on 20 Main Street today, and 90 Chandler Avenue tomorrow. Imagine trying to frequent a restaurant whose address changes every week. It would be annoying, inconvenient, and perhaps downright impossible.

We want our server to always be at the same address. The “D” in “DHCP” means “dynamic” – as in, changing. We don’t want that. We want a “static” IP, meaning it does NOT change.

When setting up your server, we need to give it a static IP, so we always know where to find it, and it never changes. How do we know what IP to give it? Go back to pfSense’s DHCP server configuration page & you can find it by going to Services —> DHCP Server. The “subnet range” tells you the list of available IPs. Keep in mind that you cannot use the IP address of your pfSense router here.

  • Router Gateway: My router’s IP is 192.168.5.1. This is the gateway address.
  • Address Pool Range: My address pool range is from .15 to .245, leaving .246 to .254 and .2 to .14 available. This setup provides a buffer of IPs for servers and other devices.

Why the Buffer? I don’t want any conflicts where someone plugs in their computer while mine is rebooting and steals my IP. We will be setting up STATIC MAPPINGS so that nobody else can grab the IP address of my server – the IP we choose for our server will be reserved for our server’s specific network interface card and not some hated brother in law that thinks he’ll play games when your spouse has him over. However, this is still good practice.

4.2 Choosing a Static IP[edit | edit source]

For my servers, I pick an IP between 192.168.5.2 and 192.168.5.14. This ensures no one else can sneakily take my server’s IP while it’s rebooting.

  1. In your pfSense router, go to Services > DHCP Server.
  2. Understand your subnet. For example, 192.168.5.0/24 covers IPs from 192.168.5.1 to 192.168.5.254
  3. Your router’s IP is typically 192.168.5.1. We can’t use that. Since we made the address DHCP pool range 192.168.5.15 192.168.5.245, this means that we have 192.168.5.2 through 192.168.5.14 free – no computer connecting with DHCP (which is the default for 99.9999% of all network devices in your home) will be using these, so they’re free for the taking.
  4. Choose the network interface that’s connected (usually the one that has already received an IP via DHCP).
  5. Change the configuration from DHCP to Manual:
  • IP Address: Choose an address outside your DHCP pool (e.g., 192.168.5.2)
  • Subnet: Usually 255.255.255.0 (or /24 in CIDR notation)
  • Gateway: Your router’s IP (e.g., 192.168.5.1)
  • Name servers: Use your router’s IP as the DNS server


Please note: if you skip step 4 by choosing Continue without network, you not be able to set up your internet connection later.

5. Prepare the Drives[edit | edit source]

5.1 Format the drives[edit | edit source]

  1. In the installer, locate your two SSDs (ignore the USB installer drive).
  2. For each SSD:
    • Select the drive and choose “Reformat”.
    • Select “Use as boot device” – this will create an EFI partition on each.

5.2 Configure EFI Partitions[edit | edit source]

For each SSD:

  • Locate the automatically created EFI partition (usually 1GB).
  • Edit the size to 512M.
  • Make sure it’s set to mount at /boot/efi.

5.3 Create Boot Partitions for RAID[edit | edit source]

  1. On each SSD:
    • Create a new 1GB partition.
    • DO NOT FORMAT IT. CHOOSE "Leave unformatted".
    • DO NOT CHOOSE A MOUNT POINT. This is important for setting up RAID 1 later.
  2. Set Up RAID 1 for /boot
  1. Select “Create software RAID (md)”.
  2. Choose both 1GB partitions you just created (one from each SSD).
  3. Set RAID Level to “RAID 1 (mirrored)”.
  4. Name it “bootraid” or something meaningful to you.
  5. Select “Create”, hit enter.

5.4 Create Root Partitions for RAID[edit | edit source]

  1. On each SSD:
    • Create a partition using all remaining space. Don’t fill in the “size” text box; it will automatically use the rest of the space on the drive.
    • DO NOT FORMAT IT. CHOOSE "Leave unformatted".
    • DO NOT CHOOSE A MOUNT POINT. This is important for setting up RAID 1 later.

5.5 Set Up RAID 1 for Root[edit | edit source]

  1. Select “Create software RAID (md)” again.
  2. Choose both large partitions you just created.
  3. Make sure RAID Level is set to “RAID 1 (mirrored)”.
  4. Name it “osdriveraid” or something meaningful to you.
  5. Go to “Create” & hit enter.

5.6 Configure the /boot Partition[edit | edit source]

  1. Select the “bootraid” you created.
  2. Format it as ext4.
  3. Set mount point to /boot.

5.7 Set Up LVM on Root RAID[edit | edit source]

  1. Select the “osdriveraid” you created.
  2. Choose “Create volume group”.
  3. Name it “ubuntuvolumegroup” or something meaningful to you.
  4. When selecting the device for the LVM, you’ll encounter a bug in the installer: > The installer will show multiple devices without clear identifiers. This is a known issue that persists in the non-beta release of a stable, mission very important server operating system. Welcome to the world of open source software; this is part of the fun of using open source! Remember: it wouldn’t be open source if it worked!
  5. To select the correct device:
    • Look for the option that’s around the size of your install (e.g., 231 GB for 250 GB SSDs).
    • Choose the largest option, which should correspond to your RAID 1 array for the root partition.
    • Ignore the smaller sizes, as they likely represent other partitions or devices.
    • Pray.
  6. After selecting the correct device, proceed with creating the volume group.

5.8 Create Encrypted Volume[edit | edit source]

  1. With the LVM volume group selected, choose “Create encrypted volume”.
  2. Set a strong password. Consider using a password manager.
  3. It’s recommended not to create a recovery key, as this could be a potential security risk.
  4. Optionally, create a recovery key. If you do this, realize the recovery key can be used to decrypt your volume. Don’t do this unless you have a place to hide it that not even your cat can get to!

5.9. Create Logical Volume for Root[edit | edit source]

  1. Select the encrypted volume you just created.
  2. Choose “Create logical volume”.
  3. Name it “ubunturootvolume” or something meaningful to you.
  4. Use the maximum available size.
  5. Format it as ext4.
  6. Set the mount point to / (root).

5.10 Review and Confirm[edit | edit source]

  1. Double-check your configuration. For two 250 GB SSDs, it should look like this:
    • Root (/): ~231GB on encrypted LVM which is on RAID 1
    • /boot: ~1GB on RAID 1
    • /boot/efi: 512MB on each SSD
  2. If everything looks correct, click “Done”.

5.11 Complete the Installation[edit | edit source]

  1. Carefully review the summary one last time. Remember we are erasing everything on these drives, to a point where even Rossmann Repair can’t recover it. If you create an encrypted volume, write over it, and then want the data back… good luck with that one.
  2. If you’re sure you want to proceed, click “Continue”.
  3. Follow the remaining Ubuntu Server installation prompts.
  4. Set up your username.
  5. Install OpenSSH server.

NOTE: Installing OpenSSH allows you to remotely access your machine to install things, use it, mess with it, etc, rather than sit in front of your server in your unairconditioned garage when it’s 117f outside. When you see me on video installing things via terminal, I am almost never in front of the actual machine(or vm) I am using, I am remoting in using ssh.

NOTE: Do not install Docker via Snap in the next menu when it asks you to. We will install Docker later, and it won’t be the miserable snap version of DOCKER_. If you install Docker using Snap accidentally, this is understandable. If you install docker via snap by CHOICE, you’ll be in hell, & you’ll have earned it.

5.12 Reboot & log in[edit | edit source]

  1. Click reboot now at the end.
  2. Once it is done shutting down Ubuntu Linux, unplug the installation USB.
  3. When it boots up, it will ask for the encryption password to unlock the root partition, type this in.

5.13 Set Up Static IP Mapping in pfSense (Post-Installation)[edit | edit source]

Set Up Static IP Mapping in pfSense[edit | edit source]

  1. Log into your pfSense router.
  2. Go to Diagnostics > ARP Table.
  3. Find the MAC address associated with your server’s IP (e.g., 192.168.5.2). Mine was e0:d5:5e:a8:7f:b5.
  4. Go to Services > DHCP Server.
  5. Scroll to the bottom and click “Add static mapping”.
  6. Enter the MAC address and IP address of your server.

Figure 17: This is what my setup looks like when I’m done configuring my partition structure. Yours should resemble mine. Ubuntu makes it as difficult as possible to use encrypted LVM with RAID 1 on boot devices, but we can beat their interface with some good ol’ ingenuity.

  1. Give it a descriptive name (e.g., “Happy cloud server static IP”).
  2. Save and apply changes.

Identifying Devices on Your Network[edit | edit source]

Let’s take a quick break to discuss the importance of static mappings, hostnames, and the DNS resolver.

What you type into the hostname field when setting the DHCP static mapping in DHCP server settings is what you can use to connect to the device instead of the IP address. For instance, if you set the hostname to happycloud, instead of having to type 192.168.5.2 to connect to this device, you can type happycloud.home.arpa.

By default, on pfSense installations, the default domain is home.arpa. When you combine the hostname of happycloud with the domain of home.arpa, you get happycloud.home.arpa.

This is more convenient for connecting to devices because it is easier to remember happycloud than it is to remember 192.168.5.2 for sane people, who reserve their brains for useful data rather than useless macbook trivia. Further, similar to dynamic DNS, if you change the IP address of this server later, all of your services & bookmarks that point to this server do not have to be changed!

You can name your servers however you want! You can choose IP addresses for your servers however you want! I will be using the same IP addresses & hostnames/domains throughout this guide so it is easy to follow, but you don’t HAVE to follow mine!

Why ISC DHCP Matters in pfSense (and How to Set It Up)[edit | edit source]

The world wants you to switch to Kea DHCP, but there’s a very good reason we’re using ISC instead. It does something important that new DHCP server doesn’t. Let’s get into it.

Why ISC DHCP Is Actually Useful[edit | edit source]

  1. Hostname Resolution: Use hostnames instead of memorizing IP addresses.
  2. Works with DNS Resolver: Registers DHCP stuff automatically! You know, like it should.
  3. Simplifies Things: Makes managing your network a lot easier.

Setting Up ISC DHCP in pfSense[edit | edit source]

1. Make Sure You’re Using ISC DHCP[edit | edit source]

  1. Log into pfSense.
  2. Go to System > Advanced > Networking.
  3. In DHCP Server, select ISC DHCP.
  4. If it complains about deprecation, just ignore it. Click the checkbox to ignore the annoying warning.
  5. Hit “Save”.

2. Configure DNS Resolver[edit | edit source]

  1. Go to Services > DNS Resolver.
  2. Check these boxes:
    • ☒ “Register DHCP leases in the DNS Resolver”
    • ☒ “Register DHCP static mappings in the DNS Resolver”
    • ☒ “Register connected OpenVPN clients in the DNS Resolver”
  3. Save and apply changes.

3. Set Your Domain[edit | edit source]

  1. Navigate to System > General Setup.
  2. Set your “Domain” (like “home.arpa” or “local”).
  3. Save if you made changes.

This setup lets you use hostnames for all your devices, static IPs, and even VPN clients. It’s simple, it works, and it’ll save you a headache later. Sometimes the old way just works.

Static mappings make sure that this IP address of 192.168.5.2 is reserved for this computer to connect to, so that no other device can take it (unless they are spoofing MAC addresses but if someone inside your house is doing just to mess with you, you have bigger problems, that likely end in them getting punched in the mouth).

NOTE: Static IP mappings aren’t a big deal when you have a few phones & game consoles attached to your network at home. IF you are running a server, you are running something where clients(aka other phones/computers) are going to want to consistently know where to access it.

Think of your server like your favorite store. When you visit, do you want to have to look through maps to figure out what address they changed to that day? You could… but… wouldn’t it be better if they were at the same place each time you needed to go?

You’ve now set up an Ubuntu Server with a redundant, encrypted secure storage configuration. This setup gives you:

  • Boot drive redundancy with RAID 1
  • Flexibility for future storage management with LVM – we can resize this later if we want to get a bigger drive setup.
  • Enhanced security with full-disk encryption (except /boot and /boot/efi).
  • There are the “uhm akshually” people who will say that if you don’t encrypt boot there’s no point in all of this… just shut up.

Why I Used Virtual Machines Instead of Docker for Some Parts of My System[edit | edit source]

FEEL FREE TO SKIP THIS SECTION & SCROLL DOWN TO “Understanding the basics of Docker” section[edit | edit source]

This is another section that is completely unnecessary to read if you simply want to get to a working system. Feel free to fast forward to the “Understanding the basics of Docker”. This is here to provide insight into why I structured the guide the way I did.

Docker is a great way to managing programs in lightweight, isolated environments. It changed how sysadmins deploy and maintain their systems. Virtual machines are going out of fashion for many sysadmins; but in this guide, you’ll notice that I’ve grouped certain services into virtual machines instead of using Docker for everything. Let me explain why

1. Building My System Piece by Piece[edit | edit source]

Back when I started getting into self managing my own servers 15 years ago, my setup wasn’t built all at once. It was cobbled together using the hardware I had lying around; old laptops, physical servers & spare drives. As these machines aged, broke, or were bashed in with a titanium nightstick when they frustrated me, I started turning their hard drives into virtual machines. This was as simple as using ddrescue to create an image of the working hard drive, then using Virtual Machine Manager to run that image.

  • Why not Docker? By the time docker even came out(around 2013), I already had 3 virtual machines that were created from disk images of machines that were running in my closet or my store. At the time, Docker didn’t even exist. Rebuilding everything using docker from scratch once it came out wasn’t an efficient use of time while running a business & wasting most of my spare time fighting my state’s incompetent government.

2. Time-Efficient Migration from Physical to Virtual[edit | edit source]

Taking a physical server and turning it into a virtual machine takes no effort.

  1. Pull drive out of physical server.
  2. Run ddrescue -f -d -r 3 /dev/sdb phonesystem.iso
  3. Open virtual machine manager
  4. Enter a few commands in terminal to create a bridge network interface so the virtual machines work; once done, I never have to do this again for any other virtual machine.
  5. Import the phonesystem.iso file as a virtual machine.
  6. Mess with BIOS/UEFI settings if necessary in virtual machine manager to get it to work.
  7. Assign the virtual machine the amount of CPU cores/RAM I think it should have based on what it is doing.
  8. Run it.
  9. Be happy.

It only takes a few seconds to type the commands & click the icons necessary in virtual machine manager. Compare that to re-architecting the entire system for Docker: it would have taken way more effort, downtime, etc. for an improvement in performance I will never notice as a person who has a few users for my server.

Some of these servers were running years before Docker was a thing, & fixing what wasn’t broken made no sense. Virtual machines offered a way to keep my systems running as they were once the hardware died & have them set to back up with no extra work on my part.

Over the years, I changed to using programs that were in docker exclusively. I went from a normal nextcloud deployment where I manually installed everything from scratch(including dependencies) to immich for images which was all docker. I went from self-managed email where the individual components(mailcot, dovecow, mysql, rspamd, etc.) were all installed from scratch to mailcow. Along the way, I just installed the dockerized version of the program on the virtual machine that was assigned to that “group” of services

3. Certain Programs Aren’t Built for Docker[edit | edit source]

Not every program is Docker-friendly. For example:

  • FreePBX (or PBXinaflash): You could theoretically create a custom Docker setup for these, but it would involve a ridiculous amount of work with little to no benefit for someone with my number of users(1, me, or 1 or 2 other people sometimes). For my use case (& most home users), the performance penalty of using FreePBX in a virtual machine instead of dockerized is as noticeable as using Gentoo Linux with emerge to compile the entire thing from a stage 1 tarball vs. using gentoo with apt to install programs.
  • Home Assistant: The developers themselves recommend using their pre-built VM image (HaOS) instead of Docker. If the devs think it’s better, I’m not going to argue. I can barely write a zfs health monitoring email script. Who am I to argue with the developers of the best home automation software on earth on the best way to run their program?

Even if I wanted to use Docker for everything, I’d still have at least two VMs running. Adding another one or two doesn’t bother me.

4. Idiotproof backups - the most important one[edit | edit source]

This is a beginner’s guide. Backing up Docker volumes, containers, networks, images, and configs is 100% doable. But let’s be honest, it requires some degree of competence. Backing up a virtual machine, on the other hand, is completely idiot-proof.

  • A virtual machine backup is just a single .qcow2 disk image and a single .xml configuration file. Drag and drop those two files to another system, import them into Virtual Machine Manager, and the virtual machine runs.
  • There’s no need to rebuild Docker containers, recreate volumes, or edit docker-compose.yml files. It’s so simple that someone with absolutely no technical expertise could do it in one click.
  • Infact it is so easy that even I can do it. If I can do it, it is truly idiotproof.

The backup script I provide is one I use myself. Once a week, it backs up all of my virtual machines as well as their configuration files to a ZFS pool which will continue running even if several hard drives fail. If I screw something up, it is two terminal commands or a few clicks in the GUI & I’m back up & running as if nothing stupid ever happened.

For beginners, when it comes to backups, simplicity is priceless.

Added complexity means you are less likely to use your backup system & less likely to understand how restoring from a backup works.

Why this guide uses virtual machines[edit | edit source]

In this guide, I’ve grouped services into virtual machines because it mirrors how I built my system over the past 15 years. This approach makes it easier for total beginners to back up and restore their setups without worrying about the complexities of Docker. Here’s how I’ve organized the VMs:

  1. Android Services: Alternatives to Google Drive, Google Photos, and Google Docs.
  2. Identity and Communication Services: Alternatives to Gmail, Google Calendar, Google Contacts, and Google Chrome’s password manager.
  3. Phone System: FreePBX for managing calls.
  4. Home Automation: Home Assistant for smart home management.

You do not have to do anything this way if you don’t want to.[edit | edit source]

You’re welcome to adapt this setup, or not. If you prefer Docker, you can combine many of these services into one host system. However, I still recommend using virtual machines for the following:

  • FreePBX: The extra effort required to make this work in Docker isn’t worth it.
  • Home Assistant: The HaOS image is the easiest and most reliable way to run Home Assistant, as per the developers’ recommendations.

This guide wasn’t about doing everything new - it was about all of you asking how I had set up my system. Since my system works for everything under the sun & has continued to for longer than I’ve been allowed to buy alcohol, I keep it going.

Understanding the basics of Docker[edit | edit source]

FEEL FREE TO SKIP THIS SECTION & SCROLL DOWN TO “Configuring Our Server’s Networking for Virtual Machines” section[edit | edit source]

You do not need to read this section to install the software in this guide. You can simply copy & paste along commands as I provide them to you, or follow the documentation from the program’s developers. This section is not required reading, but rather here to help you understand the how and the why behind the installation methods for the programs we’re installing so you learn as you go - if you’re interested. If not, skip ahead to “Configuring Our Server’s Networking for Virtual Machines”

We are going to use docker to install a program called mailcow. Before getting started installing mailcow, I want to go over what docker is & how it works.

You do not need to be a genius linux sysadmin at creating your own docker containers & setups to use it, but you should have some clue what it is or what happens when you type docker compose up to run something! Docker massively changed how sysadmins run & deploy software. It’s the engine behind many modern self-hosted solutions like Mailcow, Immich, Bitwarden, Frigate, & OnlyOffice. It gets rid of one of the single largest pain points of releasing(or using, or installing) software on Linux: dependencies. Before getting into what Docker is, let’s go over dependency hell.

What Are Dependencies and Why Do They Cause Problems?[edit | edit source]

Understanding Dependencies[edit | edit source]

A dependency is software/libraries/frameworks that have to be installed for the program you are installing to work. Let’s say you’re installing a web application written in PHP; it might need a specific PHP module or a specific version of PHP.

  • If you don’t have that version of PHP installed, the application won’t work.
  • If you don’t have PHP installed, the application won’t work.
  • If you want to use an application that requires a different version of PHP on the same machine….

and so on & so forth.

The Dependency Hell of the 1990s[edit | edit source]

Before modern package managers like apt used by Debian(and 6+ years later, ubuntu) or emerge (Gentoo), installing software on GNU/Linux would require manually finding & installing specific dependencies. Here’s what this hell was like:

  1. You downloaded a .tar.gz file that was the source code of the program you wanted to install, called rabbitholetohell.
  2. You ran ./configure & it told you you’re missing libshit.
  3. You foundlibshit, downloaded it, and discovered it required (libpiss).
  4. You found libpiss but learned that libpiss needed version 1.2 of libpuke and your computer had version 1.3 of libpuke installed.
  5. Downgrading from version 1.3 of libpuke to version 1.2 of libpuke breaks your entire system.
  6. User throws keyboard at wall & switches back to windows and says forget GNU/Linux for life.
  7. If the user is a sysadmin, they curse and figure out how to make it work because this is their job, wasting tons of time.

This was called dependency hell, where each dependency needed more dependencies. it’s what eli the computer guy would correctly call the rabbit hole to hell

Tools like apt came along in the late 90s. Instead of dependency hell, you typed apt install rabbitholetohell -y & it just installed rabbitholetohell. It installed all the dependencies, & their dependencies, and it installed the right ones. It was beautiful…

Yet, even with tools like apt to make installs simpler, problems came up if multiple applications needed different versions of the same dependency. For example:

  • PHP Example: Suppose you wanted to run two applications:
    • App 1 requires PHP 7.4.
    • App 2 requires PHP 8.1.
    • Your system can only have one version of PHP installed at a time, and switching between versions was a rabbit hole to hell

Why This Is a Nightmare for Software Maintenance[edit | edit source]

Dependencies can become a serious problem over time:

  1. Conflicting Requirements: If program A needs libshit version 1.2 & program B needs libshit version 2.0, your system can break when one application upgrades.
  2. Complex Upgrades: Updating dependencies for one application can & will cause another application to stop working. This is called dependency breakage and they are another common cause of chasing rabbits all the way to hell.
  3. System Decay: Over time, manually managing dependencies can lead to a bloated, unstable system full of broken packages, outdated libraries, & leftover files.
  4. Version pinning misery: apt lets you install specific versions of packages but managing version conflicts becomes timewasting, dangerous, & difficult when dependencies span dozens of packages with intricate relationships. As a newbie, you are likely going to break your system. As an experienced sysadmin… they still broke their systems….

How docker solves this mess[edit | edit source]

Docker containers solve these problems by isolating dependencies for each application. Here’s how it works:

  1. Per-Application Environments: Each Docker container includes everything an application needs to run from the application code, runtime, & all dependencies. These are packaged together in the Docker image.
    • Example: If one application needs PHP 7.4 and another needs PHP 8.1, you can run both simultaneously in separate containers without conflict, on the same computer.
    • I am not talking about on separate virtual machines. I mean on the SAME HOST OPERATING SYSTEM. Two versions of PHP; or ten if you wanted. and no issues. no conflicts. No rabbit, & no hell :)
  2. Immutable(unchangeable): Docker images are immutable snapshots. Once built, the dependencies in an image don’t change, so the application runs consistently every time. It’s not like an operating system update where package A may not be updated but package B is, and package A depends on a specific version of package B so everything breaks.
  3. No System-Wide Conflicts: Docker containers don't mess with each other on the host system. The PHP version inside the container for nextclouddoesn’t affect the PHP version on the host, or in the container for magento.
  4. Simple Upgrades: If you need to update an application you just type docker compose pull when it’s not running & it just updates… seamlessly. If it fails or the dev messed something up, you can go back to a previously installed image without messing up other applications.
  5. Portable: Docker makes sure that the program & its dependencies work the same way on ANY system; whether it’s your personal server, a cloud provider, or your friend’s gaming PC.

Why docker has exploded in popularity for small open source projects[edit | edit source]

Developers get less complaints from users[edit | edit source]

The biggest complaint by far that many open source projects get is I tried to install abc program & get xyz error. It is the bane of open source software developer’s existance, until they stop caring about their users entirely. This is often the only way to stay sane in a world where “users”(NOT “customers”), who pay the developers $0, expect unlimited tech support & handholding as well as a one year lesson in GNU/Linux systems administration so they can install a photo gallery.

This sucks.

With Docker, for a developer to hand off a program running on their server to your server, the dev only has to provide you the following:

  1. Docker image of your application
  2. The associated Docker Compose docker-compose.yml file
  3. Instructions or files to set up storage & networking.
  4. If you want to copy the files over that the service was saving that are unique to you, the docker volume.
  5. Tell you to edit xyz content in a docker-compose.yml file so the software is set to your specific need.
  6. Tell you to type docker compose pull & docker compose up -d
  7. Never hear complaints from you again.

The Docker image contains the program & its environment, which makes sure the software runs the same on their server as it does on yours.

AKA, the developers can provide me a COMPLETELY IDIOTPROOF copy of their software that is so easy to install even I can’t screw it up. Once they get it to install on THEIR system - they know it’ll install on mine.

The docker-compose.yml file explains to docker & your computer how to “deploy” the program & has details about Docker networks (e.g., how the containers communicate) & Docker volumes (for storing data that persists outside the container).

Docker makes what used to be miserable very easy[edit | edit source]

  • You can run Mailcow (which uses PHP 7.4 for its web interface) alongside OnlyOffice (which needs PHP 8.1) on the same server without issues.
  • When setting up something like Immich, you don’t need to worry about Node.js versions conflicting with other apps. The devs use Docker to bundle the correct version for you. You don’t have to RTFM to figure out the right version of libshit to install anymore - the developer does that once, and then it’s set for all of their users.
  • If a new version of Bitwarden requires updated dependencies, you update the Docker container, leaving the rest of your system untouched.

Docker turns what used to be a nightmare into a manageable, predictable process that isn’t miserable.

1. How Docker Works[edit | edit source]

Docker simplifies running software by packaging everything the software needs into one neat bundle. It does this using containers which are lightweight standalone environments that share the host system’s resources but remain isolated.

This is like a virtual machine, but without the baggage of needing its own operating system. Docker containers run on a shared kernel, making them much faster and lighter. If you ever enter a docker container, you will notice that almost no programs or commands are available besides the bare minimum necessary to do the job. See below:

louis@ultimatebauer:~$ docker exec -it frigate bash
root@174eb3845d50:/opt/frigate# nano file.log
bash: nano: command not found
root@174eb3845d50:/opt/frigate# vi file.log
bash: vi: command not found
root@174eb3845d50:/opt/frigate# vim file.log
bash: vim: command not found
root@174eb3845d50:/opt/frigate# emacs file.log
bash: emacs: command not found
root@174eb3845d50:/opt/frigate# ip addr show
bash: ip: command not found
root@174eb3845d50:/opt/frigate#  you really don't have shit in here besides exactly what you need to run the application, do you? run nano you prick!

root@174eb3845d50:/opt/frigate I’m afraid I can’t do that, dave

2. What Are Docker Images?[edit | edit source]

A Docker image is a blueprint on how to install the program. It has the instructions, files, & dependencies necessary to create a working environment for a piece of software. Think of it like a frozen dinner if they weren’t poisonous to your health. Everything you need is pre-packaged, & all you have to do is microwave it (or, in this case, “run” the image; please don’t try to microwave a GNU/Linux computer, as tempting as it might be when it doesn’t work) to get the app running.

  • Example: A Nextcloud Docker image includes the Nextcloud app, its web server, and everything else it needs to limp. I won’t use the word “run” to describe nextcloud…

3. What Are Docker Containers?[edit | edit source]

A Docker container is a running instance of a Docker image. Using the frozen dinner analogy, if the image is a boxed meal in a freezer, a container is a meal served hot & ready to eat. You can run many containers from the same image just like you could cook multiple identical dinners from the same recipe.

For instance, mailcow is not a mail “program” so much as it is an amalgamation of a bunch of programs necessary to run a mailserver.

On my mailserver, you can see a list of all the different containers that run for mailcow when I run docker ps -a

Example: mailcow container guide[edit | edit source]

Mail processing[edit | edit source]

  • postfix: The program that sends emails
  • dovecot: The program that receives emails & stores them & categorizes them by user, inbox, email address, folder, etc.
  • rspamd: anti-spam controls
  • clamd: scans attachments for viruses

Web & Interface[edit | edit source]

  • sogo: webmail dashboard for checking email/calendar/contacts in browser
  • phpfpm: for web interface

security & monitoring[edit | edit source]

  • watchdog: The health monitor
  • acme: Handles SSL certificates
  • netfilter: Blocks bad actors
  • unbound: helps route messages correctly

Helper Services[edit | edit source]

  • solr: Makes searching through your email faster
  • olefy
  • dockerapi:

Think of Docker containers like having separate tiny computers inside your main computer that are barebones and only include the minimum necessary for each function to work. They each work independent of each other to minimize the likelihood of something screwing up while also allowing you the ability to experiment without destroying your entire system.

Containers are not persistant. This means what happens in the containers stays in the container until you restart it. Once you restart the container, any changes to files you have made are GONE. PERSISTENT storage occurs in docker volumes.

Each container has its own:

  • Space to run programs
  • Network connection
  • File storage
  • Settings
  • Installed programs

Unlike full virtual machines (which are like having complete separate computers), containers share the main operating system’s foundation (the host’s operating system kernel), making them much lighter and faster to start up.

For example, in mailcow:

  • The postfix container only knows about sending/receiving mail
  • The rspamd container is only for filtering junk
  • The clamd container is only there to scan for viruses

They can’t interfere with each other, but they can communicate through specific “doorways” (network ports) when needed. If something goes wrong with one container, it doesn’t affect the others - just like one apartment’s plumbing problem doesn’t affect the other apartments (hopefully).

If you need to upgrade or fix something, you can work on one container without messing with everything else.

louis@mailserver:~$ docker ps -a
CONTAINER ID   IMAGE                    COMMAND                  CREATED       STATUS                  PORTS                                                                                                                                                                                                                               NAMES
aca88eab00b0   mailcow/watchdog:2.05    "/watchdog.sh"           11 days ago   Up 24 hours                                                                                                                                                                                                                                                 mailcowdockerized-watchdog-mailcow-1
012debb1f557   mailcow/acme:1.90        "/sbin/tini -g -- /s…"   11 days ago   Up 24 hours                                                                                                                                                                                                                                                 mailcowdockerized-acme-mailcow-1
d33aa2bb976b   nginx:mainline-alpine    "/docker-entrypoint.…"   11 days ago   Up 24 hours             0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp                                                                                                                                                            mailcowdockerized-nginx-mailcow-1
7bc85825c0b1   mailcow/rspamd:1.98      "/docker-entrypoint.…"   11 days ago   Up 24 hours                                                                                                                                                                                                                                                 mailcowdockerized-rspamd-mailcow-1
958d3ba45877   mcuadros/ofelia:latest   "/usr/bin/ofelia dae…"   11 days ago   Up 24 hours                                                                                                                                                                                                                                                 mailcowdockerized-ofelia-mailcow-1
a99f82d2b36a   mailcow/phpfpm:1.91.1    "/docker-entrypoint.…"   11 days ago   Up 24 hours             9000/tcp                                                                                                                                                                                                                            mailcowdockerized-php-fpm-mailcow-1
b8c6df6a7303   mailcow/dovecot:2.2      "/docker-entrypoint.…"   11 days ago   Up 24 hours             0.0.0.0:110->110/tcp, :::110->110/tcp, 0.0.0.0:143->143/tcp, :::143->143/tcp, 0.0.0.0:993->993/tcp, :::993->993/tcp, 0.0.0.0:995->995/tcp, :::995->995/tcp, 0.0.0.0:4190->4190/tcp, :::4190->4190/tcp, 127.0.0.1:19991->12345/tcp   mailcowdockerized-dovecot-mailcow-1
e3b09c799a7c   mailcow/postfix:1.77     "/docker-entrypoint.…"   11 days ago   Up 24 hours             0.0.0.0:25->25/tcp, :::25->25/tcp, 0.0.0.0:465->465/tcp, :::465->465/tcp, 0.0.0.0:587->587/tcp, :::587->587/tcp, 588/tcp                                                                                                            mailcowdockerized-postfix-mailcow-1
faece81357e3   mailcow/solr:1.8.3       "docker-entrypoint.s…"   11 days ago   Up 24 hours             127.0.0.1:18983->8983/tcp                                                                                                                                                                                                           mailcowdockerized-solr-mailcow-1
76c9f63fa50d   mariadb:10.5             "docker-entrypoint.s…"   11 days ago   Up 24 hours             127.0.0.1:13306->3306/tcp                                                                                                                                                                                                           mailcowdockerized-mysql-mailcow-1
930a7e0acff6   redis:7-alpine           "docker-entrypoint.s…"   11 days ago   Up 24 hours             127.0.0.1:7654->6379/tcp                                                                                                                                                                                                            mailcowdockerized-redis-mailcow-1
8bbcbe5ebefb   mailcow/clamd:1.66       "/sbin/tini -g -- /c…"   11 days ago   Up 24 hours (healthy)                                                                                                                                                                                                                                       mailcowdockerized-clamd-mailcow-1
9070a5ba3fb0   mailcow/olefy:1.13       "python3 -u /app/ole…"   11 days ago   Up 24 hours                                                                                                                                                                                                                                                 mailcowdockerized-olefy-mailcow-1
893f2ff1f952   mailcow/dockerapi:2.09   "/bin/sh /app/docker…"   11 days ago   Up 24 hours                                                                                                                                                                                                                                                 mailcowdockerized-dockerapi-mailcow-1
6781988f3409   mailcow/sogo:1.127.1     "/docker-entrypoint.…"   11 days ago   Up 24 hours                                                                                                                                                                                                                                                 mailcowdockerized-sogo-mailcow-1
464ca438b4c2   mailcow/unbound:1.23     "/docker-entrypoint.…"   11 days ago   Up 24 hours (healthy)   53/tcp, 53/udp                                                                                                                                                                                                                      mailcowdockerized-unbound-mailcow-1
373c1b7c5741   mailcow/netfilter:1.59   "/bin/sh -c /app/doc…"   11 days ago   Up 24 hours                                                                                                                                                                                                                                                 mailcowdockerized-netfilter-mailcow-1
6931fc976572   memcached:alpine         "docker-entrypoint.s…"   11 days ago   Up 24 hours             11211/tcp                                                                                                                                                                                                                           mailcowdockerized-memcached-mailcow-1
louis@mailserver:~$ 

4. What Are Docker Networks?[edit | edit source]

Docker allows containers to communicate with each other & the outside world using networks. By default, the containers can access the internet. Custom networks allow you to connect certain containers while keeping them separate from others.

For instance, in mailcow docker networks make sure the mail server can talk to the database container securely without exposing the database to the entire internet.

5. What Are Docker Volumes?[edit | edit source]

A Docker volume is where data generated by a container is stored. Think of a docker container like a computer booting up from a read only floppy disk. Whatever you ran in your programs is gone the second you reboot the computer. The docker volume is the second disk in the computer that you can write to so that you can save things. Containers are where programs run (postfix, dovecot), and volumes are where things are stored (emails, pictures, videos, etc.). Volumes make sure that important data persists even if the container is removed or restarted.

Volume examples with different programs:[edit | edit source]

The docker-compose.yml file is what tells docker how to set up everything. In frigate, we are not creating docker volumes. Rather, we tell docker to map a directory on the host computer inside the docker container. Look here:

docker program that does not use docker volumes[edit | edit source]

In this file, the container “frigate” specified on line 4 by container_name, we do not have any docker volumes specified. Under services we specify our containers. There are no docker volumes specified here. We have told the system that whatever is in /home/louis/Downloads/programs/frigate/config on the host system should show up inside the frigate container on the directory /config. Without this, the config.yml file within the /home/louis/Downloads/programs/frigate/config directory would not show up inside the container. Even if I logged into the container using docker exec -it frigate bash and created a config.yml file in /config, it would be gone when I restarted the container.

version: "3.9"
services:
  frigate:
    container_name: frigate
    privileged: true # this may not be necessary for all setups
    restart: unless-stopped
    image: ghcr.io/blakeblackshear/frigate:stable
    shm_size: "2048mb" # update for your cameras based on calculation above
    devices:
      - /dev/bus/usb:/dev/bus/usb # Passes the USB Coral, needs to be modified for other versions
      - /dev/apex_0:/dev/apex_0 # Passes a PCIe Coral, follow driver instructions here https://coral.ai/doc>
      - /dev/video11:/dev/video11 # For Raspberry Pi 4B
      - /dev/dri/renderD128:/dev/dri/renderD128 # For intel hwaccel, needs to be updated for your hardware
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /home/louis/Downloads/programs/frigate/config:/config
      - /drive1thru8/securitycam:/data/db
      - /drive1thru8/securitycam:/media/frigate
      - type: tmpfs # Optional: 1GB of memory, reduces SSD/SD Card wear
        target: /tmp/cache
        tmpfs:
          size: 1000000000
    ports:
      - "8971:8971"
      - "5000:5000" # Internal unauthenticated access. Expose carefully.
      - "8554:8554" # RTSP feeds
      - "8555:8555/tcp" # WebRTC over tcp
      - "8555:8555/udp" # WebRTC over udp
    environment:
      FRIGATE_RTSP_PASSWORD: "password"

docker program that DOES use docker volumes[edit | edit source]

Check out mailcow. This is not the full docker-compose.yml configuration file, just a part of it. Look at lines 25-28. For the container mysql-mailcow, we have two docker volumes. The docker volume mysql-vol-1 will show up inside the mysql-mailcow container (which is a tiny virtual computer that runs our programs, in this case, the mysql database. mysql databases usually contain data on users, configurations, product orders, etc). Whatever is in the mysql-vol-1 docker volume will show up inside the mysql-mailcow container at /var/lib/mysql.

It is using a docker volume instead of the main computer/operating system’s file system to store its files.

However, on line 28, we have - ./data/conf/mysql/:/etc/mysql/conf.d/:ro,Z which means that whatever is in the subfolder of our mailcow folder(where the docker-compose.yml file is that we used to install mailcow) under data/conf/mysql/ will show up inside the docker container at /etc/mysql/conf.d/

services:

    unbound-mailcow:
      image: mailcow/unbound:1.23
      environment:
        - TZ=${TZ}
        - SKIP_UNBOUND_HEALTHCHECK=${SKIP_UNBOUND_HEALTHCHECK:-n}
      volumes:
        - ./data/hooks/unbound:/hooks:Z
        - ./data/conf/unbound/unbound.conf:/etc/unbound/unbound.conf:ro,Z
      restart: always
      tty: true
      networks:
        mailcow-network:
          ipv4_address: ${IPV4_NETWORK:-172.22.1}.254
          aliases:
            - unbound

    mysql-mailcow:
      image: mariadb:10.5
      depends_on:
        - unbound-mailcow
        - netfilter-mailcow
      stop_grace_period: 45s
      volumes:
        - mysql-vol-1:/var/lib/mysql/
        - mysql-socket-vol-1:/var/run/mysqld/
        - ./data/conf/mysql/:/etc/mysql/conf.d/:ro,Z
      environment:
        - TZ=${TZ}
        - MYSQL_ROOT_PASSWORD=${DBROOT}
        - MYSQL_DATABASE=${DBNAME}
        - MYSQL_USER=${DBUSER}
        - MYSQL_PASSWORD=${DBPASS}
        - MYSQL_INITDB_SKIP_TZINFO=1
      restart: always
      ports:
        - "${SQL_PORT:-127.0.0.1:13306}:3306"
      networks:
        mailcow-network:
          aliases:
            - mysql

mailcow docker volume descriptions[edit | edit source]

Here are some docker volumes used for mailcow:

louis@mailserver:/opt/mailcow-dockerized$ docker volume ls
DRIVER    VOLUME NAME
local     mailcowdockerized_clamd-db-vol-1
local     mailcowdockerized_crypt-vol-1
local     mailcowdockerized_mysql-socket-vol-1
local     mailcowdockerized_mysql-vol-1
local     mailcowdockerized_postfix-vol-1
local     mailcowdockerized_redis-vol-1
local     mailcowdockerized_rspamd-vol-1
local     mailcowdockerized_sogo-userdata-backup-vol-1
local     mailcowdockerized_sogo-web-vol-1
local     mailcowdockerized_solr-vol-1
local     mailcowdockerized_vmail-index-vol-1
local     mailcowdockerized_vmail-vol-1

main data storage[edit | edit source]
  • vmail-vol-1: The emails & attachment files
  • mysql-vol-1: Database stuff like user accounts/settings
  • redis-vol-1: Temporary data for faster load times

email processing[edit | edit source]
  • postfix-vol-1: Mail server configuration & logs
  • rspamd-vol-1: spam filter rules & training data
  • clamd-db-vol-1: Virus scanning database

webmail & user data[edit | edit source]
  • sogo-userdata-backup-vol-1: Backups of user settings & data
  • sogo-web-vol-1: Web interface files
  • vmail-index-vol-1: Helps search through old email quickly

random technical volumes[edit | edit source]
  • crypt-vol-1: Encryption-related data
  • mysql-socket-vol-1: This assists database communication
  • solr-vol-1: Search engine data

This seems like a lot[edit | edit source]

If this is too much, realize this. 99% of installing programs that are packaged with docker means doing the following:

  1. Downloading a docker-compose.yml file
  2. Running the command docker compose pull to grab program
  3. Running the command docker compose up -d to start program.
  4. You’re done.
  5. If an idiot like me can do it, then so can you.

YOU DO NOT NEED TO BECOME AN EXPERT SYSTEMS ADMINISTRATOR OVERNIGHT.

The best way to learn is to try and understand things one part at a time. You do it like this:

  1. Set something up, have it work.
  2. Have no idea what you did.
  3. Mess around with it & enjoy it.
  4. Use the kick of dopamine from it working & enjoying it to get motivated.
  5. Read a piece of a config file just for the hell of it and see if it maps to anything in the program/what you’re doing.
  6. If it makes no sense, don’t worry about it, keep enjoying the program & increasing your stock of dopamine & happiness & satisfaction.
  7. Come back to it again later.
  8. Read a little bit.
  9. Read something on a forum/manual/guide that makes little sense to you, but maybe 1% more sense now than it did a week ago.
  10. Pat yourself on the back for understanding it even though you think this is kindergarten level & you’re an idiot & everyone else knows way more than you.
  11. Enjoy program more.
  12. Don’t crap on yourself because you don’t get everything.
  13. When bored sitting in a meeting you have no business wasting your time in, alt-tab over to your docker-compose.yml file.
  14. Google random parts & see what they do.
  15. Think about how that piece of software works. Google what the different words inside of the file do, what those programs are for, & how they relate to the program working as a whole.
  16. See if you understand 1% more now than before.
  17. Each percent you understand is not cumulative - it is exponential! Learning this stuff is a parabola. In the beginning, it is insanely slow. Once you get started & understand the foundation, learning increases at an exponential pace.
  18. You need to overcome that period where you feel like an imposter & a total idiot in order to get better.
  19. Realize that even complete experts know 0.0001% of what there is to know about all of this and usually specialize in one specific area, because to understand how everything works is damn near impossible.

Configuring Our Server’s Networking for Virtual Machines[edit | edit source]

What are virtual machines?[edit | edit source]

We are going to make use of virtual machines a lot. Virtual machines (VMs) are software-based computers running inside your physical server. This approach allows us to have separated, segmented computers running inside of our computer that are absolutely idiotproof to back up & restore. Key word there being idiotproof. Once I provide you with a working backup script, if you mess something up with any of the services (mailcow, freepbx, homeassistant syncthing, immich, nextcloud, etc.) all you have to do is:

  1. Shut down the existing messed up virtual machines.
  2. Restore a single .qcow2 file from backup.
  3. Start up the virtual machine.

And everything works again. No confusing command line stuff, no editing config files. Depending on the host & network you move it to, you might have to edit the IP address configuration & ports forwarded in the firewall; besides that it will just work. This is beautiful, and so idiotproof even someone like me can do it. If I mess up my phone system, I can restore it in seconds without having to mess with any other part of my system. Did I mention it’s idiotproof? This is the most important quality of a system when I’m the one using it.

If the server’s hardware or OS drive fails, it’s easy to move the VMs to another device. Insanely easy. Take a backup of a single file & move it over easy.

nerd note: Yes, docker allows for containerized installs of everything. Yes, it’s faster, yes, it makes more sense in an enterprise environment… this is a beginner’s guide. Having a very easy backup script that allows copying & pasting a qcow2 file when you break something means you’ll actually use your backups rather than give up, which is important in the beginning. We will create segmented VMs for each of our purposes (identity/email, android/cloud services & sync, home automation, etc.) and they will have programs & services running in docker within them; often several. The backup solution will be backing up these VMs because it is dirt easy for a beginner vs. managing backing up all associated docker containers & volumes. If you want to manage this, go set up Kubernetes at a mid-sized company for someone & stop reading a newbie guide.

By running each set of services in its own VM, we isolate them from each other—so if one service has an issue, it won’t bring down the entire system.

One problem: I need these virtual machines to connect to the internet… and since they’re virtual, they have no network interface card… so I can’t plug them into my switch.

Since VMs don’t come with physical network interface cards (NICs) like a regular server, we need to create a virtual network interface that allows them to connect to the network and access the internet. This virtual interface acts as a bridge between the VMs and your server’s physical network connection.

Step 1: Disable Cloud-Init’s Network Configuration[edit | edit source]

Before changing the network configuration, you need to stop cloud-init from managing it.

Create the file to disable cloud-init’s network management by running the following command: sudo nano /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg Add this to the file: network: {config: disabled} Save & close the file by typing Ctrl + X and hitting y to save it.

Step 2: Backup the Current Netplan Configuration[edit | edit source]

Make a backup of your current Netplan configuration.

Run the following command to back up the current 50-cloud-init.yaml file: sudo mv /etc/netplan/50-cloud-init.yaml /etc/netplan/50-cloud-init.yaml.bak

.bak makes sure that Netplan will not use it for creating a configuration. .bak also makes it easy to go “back” – you don’t have to remember the filename or the location of the original file. You just copy the backup file to the same filename in the same directory without the suffix .bak, there it is.

Step 3: Create a New Netplan Configuration[edit | edit source]

Since you disabled cloud-init, you can now modify the network configuration to create a bridge interface that your virtual machines can use.

  1. Find the name of your ethernet interface (the one the CAT5 cable plugs into):
[louis@livingroombauer ~]$ ls /sys/class/net
   enp4s0  lo

In my personal computer, enp4s0 is my network interface, and lo is the loopback interface. loopback allows the machine to talk to itself - other computers cannot contact this computer through the loopback interface. This is useful if there is a service we would like to run that we do not want to be accessible to other machines on the network. enp4s0 is my ethernet port. On my server, eno1 is my interface that the ethernet port plugs into, so I will use that below. When you see me using eno1 as I set up my server, replace eno1 with the actual name of your network interface.

  1. Create or edit the Netplan configuration file by running this command:

    sudo nano /etc/netplan/01-netcfg.yaml
  2. Replace the content with the following configuration. I’ve added a comment on each line so you know how many spaces there should be:

    network: # 0 spaces
      version: 2 # 4 spaces
      renderer: networkd # 4 spaces
      ethernets: # 4 spaces
        eno1: # 8 spaces
          dhcp4: no # 12 spaces
      bridges: # 4 spaces
        br0: # 8 spaces
          dhcp4: no # 12 spaces
          addresses: # 12 spaces
            - 192.168.5.2/24 # 16 spaces
          nameservers: # 12 spaces
            addresses: # 16 spaces
              - 192.168.5.1 # 20 spaces
          routes: # 12 spaces
            - to: default # 16 spaces
              via: 192.168.5.1 # 18 spaces
          interfaces: # 12 spaces
            - eno1 # 16 spaces
  3. Once done, remember to change the permissions of your netplan file so netplan does not yell at you:

    sudo chmod 600 /etc/netplan/01-netcfg.yaml

Explanation of the Configuration: - eno1 will be part of the bridge (br0), but will no longer have an IP address directly. - br0 is the bridge interface that will be assigned the static IP 192.168.5.2. - The br0 interface will be configured with the same gateway and nameserver settings as before. The gateway is our pfSense router, which is what it connects to to get an IP address and connect to the internet (a “gateway” to the world), and the nameserver is also our router, which is what it connects to to translate things like google.com into 142.250.138.101. - br0 is a virtual interface WE are creating. eno1 is an interface already present on this machine. eno1 is the ethernet port on my computer, simply put. Your network interface card will most likely be called something else; this is ok! Use what your network interface is called as it will be different for all machines

NOTE: You are probably used to old school configuration files where: pasv_enable=YES is the same as pasv_enable=YES That is not how a YAML do. A single space is all that stands between you having a working setup & happiness, and total misery. YAML is sensitive to spaces; indentation errors matter, and can cause the config file to not work. Some text editors are helpful in editing yaml files so that it is easier to notice mistakes & errors. Some are not.

Step 4: Apply the New Configuration[edit | edit source]

Now that the configuration is ready, apply it.

  1. Run the following command to apply the new Netplan configuration:

    sudo netplan apply

    NOTE: You may make an error because yaml files are evil; to make sure the configuration works, run netplan try before running netplan apply. While yoda had a point with the “do or not do there is no try”, he never dealt with linux documentation.

  2. Verify that the bridge interface is up and has the correct configuration by running this command:

    ip addr show br0
  3. You should see that br0 has the IP address 192.168.5.2.

  4. Check the routing table to make sure that the default route is correctly set by running:

    ip route show
  5. Verify that the default route points to 192.168.5.1.

Step 5: Test Network Configuration[edit | edit source]

Verify that your server can still access the network after the changes.

  1. Ping your router by running:

    ping 192.168.5.1
  2. Ping an external IP to make sure connectivity by running. This is Google’s DNS server, which should be up all the time:

    ping 8.8.8.8

Step 6: Add iptables rules for bridging[edit | edit source]

For the bridge to work correctly, you need to allow traffic forwarding on the br0 bridge interface. This requires creating iptables rules & making them persistent across reboots. This is a very important detail, often left out of guides on setting up bridge interfaces. Skipping this part will result in a setup that doesn’t work; you will be stuck in the hell of posting on GNU/Linux forums where people with IQs of 180+ will tell you to “RTFM”, a man page, that is 2000000+ pages long.

This is analogous to Derek Jeter telling you to “just keep your eye on the ball.” right.

You may wonder why that is the case. Setting up things with open source software is like MacBook board repair 12 years ago: it’s a club & you’re not in it. Most teachers know their subject matter. As a result, they forget what it was like to try something for the first time.

This is why I am building a machine from scratch as I do this. Telling you how I did it on my machine will never work. There will always be some small detail I subconsciously assume you will know; or perhaps a detail I forgot myself since some of these services I’m showing you were set up in my closet ten years ago!

By performing the tasks from what I have written, I am forced to provide you with instructions that actually work!

  1. Run the following commands to add the iptables rules:
 sudo iptables -I FORWARD 1 -i br0 -j ACCEPT
 sudo iptables -I FORWARD 1 -o br0 -j ACCEPT

NOTE: These iptables rules let traffic go through the bridge interface so your virtual machines can work on your network. Without them, your virtual machines will not be able to connect to anything, and you won’t be able to connect to them. If you see that your virtual machine received an IP address in virtual machine manager, but it can’t connect to anything, you likely skipped this step. > >The order of rules in iptables matters. Inserting rules at the top (using the ‘-I’) puts them at the top. If traffic forwarding does not work as expected, check rules & the order which you can do by running ‘sudo iptables -L -v -n’.

  1. Verify the iptables rules by running:

    sudo iptables -L

    You should see the rules for accepting traffic on br0.

Step 7: Make iptables Rules Persistent[edit | edit source]

To make sure the iptables rules are applied after a reboot, you need to save them and configure them to load automatically on startup.

  1. Install the iptables-persistent package:

    sudo apt install iptables-persistent
  2. During installation, you’ll be asked if you want to save the current iptables rules. Choose Yes.

  3. If you’re not prompted, you can manually save the rules by running:

    sudo netfilter-persistent save

    NOTE: Installing iptables-persistent is what allows your iptables rules to stick after a reboot. This is a server - you’re not going to turn this off very often. Nine months from now when you DO turn off this server, you’re not going to remember a single damn character from this guide; much less that iptables rule above! Nor will you remember that that rule not being present is why none of your virtual machines work.

  4. Confirm the rules are saved by checking the file at /etc/iptables/rules.v4.

With these changes, your bridge interface will now correctly allow traffic to flow through the virtual machines. The iptables rules will persist across reboots, and your virtual machines will be able to grab IP addresses from the same network as your host machine.

Preparing Ubuntu Server for Virtual Machine Management[edit | edit source]

Next, let’s set up Ubuntu Server for use with virtual machines using Virtual Machine Manager (virt-manager). We’ll cover everything from preparing the ISO file to configuring the virtual machine with a static IP address, including the installation of a lightweight GUI for easier management.

Step 1: Prepare the Ubuntu Server ISO[edit | edit source]

Before creating the virtual machine, you need to place the Ubuntu Server ISO file in the correct directory and set the proper permissions.

  1. Place the ISO file you used to create your installable Ubuntu USB onto your server. You can do this by attaching a disk to it, using SSH FTP and a program like filezilla to transfer it over. Or, if you’re an animal, you can download it again by going to ubuntu.com and downloading the LTS version of Ubuntu server again.

  2. Move the ISO file to /var/lib/libvirt/images/, obviously changing the source location & filename to whatever yours is. As long as the file ends up in /var/lib/libvirt/images/ we’re good:

    sudo mv ~/Downloads/ubuntu-server.iso /var/lib/libvirt/images/
  3. Change the ownership and group of the ISO file:

    sudo chown libvirt-qemu:libvirt /var/lib/libvirt/images/ubuntu-server.iso
  4. Set the correct permissions:

    sudo chmod 0640 /var/lib/libvirt/images/ubuntu-server.iso
  5. To apply these settings to all ISO files in the directory:

    sudo chown libvirt-qemu:libvirt /var/lib/libvirt/images/*.iso
    sudo chmod 0640 /var/lib/libvirt/images/*.iso

Note: These settings make sure that the libvirt-qemu user, which runs the QEMU processes, can read and write the file, while members of the libvirt group can read it. Other users will have no access, so virsh & related tools can access the ISO files but others can’t.

Step 2: Update Your System[edit | edit source]

Make sure your system is up to date:

sudo apt update ; sudo apt upgrade -y

Note: Some GNU/Linux distributions update during installation, but it’s always good to check.

Step 3: Install Openbox and Virtual Machine Manager[edit | edit source]

We’ll install a lightweight desktop environment (Openbox) and Virtual Machine Manager:

sudo apt install --no-install-recommends xorg openbox xorg xinit virtualbox virtinst qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils virt-manager

The --no-install-recommends flag makes sure only the core components are installed without any additional unnecessary packages.

Step 4: Enable and Start Libvirt[edit | edit source]

Enable libvirt to start on boot and start it immediately:

sudo systemctl enable libvirtd
sudo systemctl start libvirtd

Step 5: Add Your User to Necessary Groups[edit | edit source]

To allow your user to configure virtual machines, add yourself to the required groups:

sudo usermod -aG libvirt,kvm $USER

NOTE: Adding your user to the libvirt & kvm groups is useful so you do not have to become superuser/sudo for virt-manager(virtual machine manager GUI) orvirsh to work right. Log out & log back in to make sure you’re in the user group after doing this.

Step 6: Start the GUI[edit | edit source]

To start the graphical interface, use the following command:

startx

Note: We will be using the GUI for Virtual Machine Manager. Any time you are NOT using this, exit the GUI (right click the desktop & log out), then type exit from the command line terminal so your machine is not logged in. Even if someone breaks into your house, they’ll have physical access to your computer; but that doesn’t mean they have easy access to your encrypted data.

6.1(OPTIONAL): Remote Desktop Access with x11vnc and TigerVNC[edit | edit source]

x11vnc is like teamviewer if teamviewer were open source and 50x slower. x11vnc allows you to connect to your server’s GUI for remote access, as if you were right in front of the computer, without having to be in the room with your computer. Up until now, we have been SSHing into the machine in order to enter terminal commands, but normal SSH won’t work if we want to use the graphical user interface, see a mouse cursor, etc.

Note: If you are ok with sitting in front of your server computer with a keyboard, mouse, & monitor plugged into it when using virtual machine manager, this step is unnecessary and you can feel free to skip it.

This will walk you through setting up remote desktop access to your Ubuntu Server using x11vnc and TigerVNC. I like using virtual machine manager GUI to install virtual machines on the main server. Since we keep going to the GUI to install virtual machines & use virtual machine manager via the GUI, we’re stuck sitting in front of the server, which sucks if it’s in a closet or garage. Here’s how you can connect to it to view what is on its screen from another computer.

6.2 Installing x11vnc on Ubuntu Server[edit | edit source]

To install x11vnc, run the following command in your terminal:

sudo apt update && sudo apt install x11vnc

This will install the x11vnc package and its dependencies on your server.

6.3 Set a Password for VNC Authentication[edit | edit source]

x11vnc uses a password for authentication, and you can set this password as follows:

x11vnc -storepasswd

You will be prompted to enter a password. This password will be saved in the default location ~/.vnc/passwd.

6.4 Set x11vnc to Listen on All Interfaces on Port 5920[edit | edit source]

Open a terminal and run the following:

x11vnc -rfbport 5920 -usepw -auth ~/.Xauthority -display :0 -forever -norc -noxdamage -shared

Here is why this helps clients like Remmina connect:

1. -rfbport 5920

This sets the port on which the VNC server will listen for connections. VNC defaults to port 5900, but I like to use a non-standard one because I am strange.

2. -usepw

This option enables password authentication for VNC clients. It requires you to set a password using x11vnc -storepasswd beforehand. Use this to set a VNC password.

  • Password authentication is standard for VNC clients like Remmina. Without this, some clients might reject the connection for security reasons. And it’s just good practice.

3. -auth ~/.Xauthority

The -auth option tells x11vnc which authentication file to use to access your X session. The file path /run/user/$(id -u)/gdm/Xauthority refers to authorization for the user running the current display session managed by GDM (your display manager). If you’re using LightDM or another manager, the path might differ.

  • Why it helps: Instead of relying on -auth guess (which might not always find the right file), specifying the correct Xauthority file guarantees that x11vnc can properly access the graphical session. If x11vnc can’t authenticate the display, no client can connect.

4. -display :0 This option specifies which X display to serve via VNC. The display :0 is typically the primary display for your desktop session (the one you see on your monitor). It makes sure x11vnc is connecting to the right graphics session. If it were set to the wrong display, you’d either get a black screen or your client wouldn’t connect at all.

5. -forever Normally, x11vnc stops running after the client disconnects. The -forever flag keeps it running indefinitely. If you disconnect & reconnect it would suck to have to log back in each time. Without this, x11vnc would stop after Remmina disconnects, and you’d have to restart it manually for every new connection. I like stopping x11vnc once I am done manually.

6. -norc This option tells x11vnc not to load a configuration file (which might contain unwanted settings), we are only using the settings in this command line.

7. -noxdamage The Xdamage extension tracks changes to the screen, but sometimes it can cause display corruption or update issues in VNC clients. The -noxdamage flag disables this extension to avoid those problems. Some VNC clients who shall not be named sometimes fk up refreshing the screen when properly when Xdamage is enabled. Disabling it keeps artifacts/stuck screen issues.

8. -shared This option allows multiple clients to connect simultaneously to the VNC server. If this option isn’t set, only one client can connect at a time, and additional connection attempts (such as from Remmina) would fail. Enabling -shared makes sure that you can connect with multiple devices or clients without being disconnected when another connects.

6.5 Installing TigerVNC Viewer on the Client[edit | edit source]

To connect to the VNC server from a client machine, you need a VNC viewer. The following steps will install TigerVNC Viewer (also known as vncviewer) on the client (your GNU/Linux computer you are reading this on):

  1. Update the package list and install TigerVNC Viewer:

    sudo apt update && sudo apt install tigervnc-viewer -y
  2. Once installed, you can use vncviewer to connect to the server.

  3. If you use Windows or a Mac, you’re on your own, my friend. Find a VNC client that doesn’t suck.

6.6 Connecting to the VNC Server[edit | edit source]

Now that everything is set up, you can connect to your server.

  1. On your local machine, use the following command:

    vncviewer 192.168.5.2:5920 -SecurityTypes VncAuth
  2. Note: Replace 192.168.5.2 with your server’s actual IP address. In our case, we can also use the domain happycloud.home.arpa since we set up a static mapping earlier for our server in pfSense.

  3. When prompted, enter the VNC password you set earlier.

You should now have a remote desktop connection to your Ubuntu Server. Remember to start x11vnc after you have logged in & typed startx to start Openbox so it works.

Step 7: Using Openbox[edit | edit source]

Once you’ve installed Openbox and typed startx, Openbox starts:

  1. Right-click on the desktop to open the application menu.
  2. Navigate to System > Virtual Machine Manager; This is what we are going to be using to create virtual machines.

At this point, we have our dependencies set up for virtual machine management, and bridge networking configured so that our virtual machines can go online. We’re ready to set up our first virtual machine!

Creating a Virtual Machine[edit | edit source]

Our first virtual machine will be for mailcow and bitwarden. These provide the following:

Mailcow: - Self managed email server for sending & receiving mail - Integrated spam management & web interface - Calendar & contacts syncing with mobile devices - A lovely, “just works” mashup of - Postfix for sending mail - Dovecot for receiving mail - rspamd for killing spam - SoGo for webmail/calendar/contacts

Bitwarden: - Password management across devices, browsers, phones, computers, etc. - Alerts when your passwords have been found in a breach

Note: These instructions will carry over into many other virtual machine installs we will be doing. I will ask you to refer back to this section. Often, the only thing you will be doing is changing the RAM amount and CPU cores allotted to the VM, and the IP address you choose as you install.

Options for Virtual Machine Creation[edit | edit source]

When you start creating a new virtual machine, you’ll see several options. We’re going to use “local install media” because we’re working with the ISO image of the Ubuntu server we downloaded. But before getting into that, let me explain the “import existing disk image” option, which is pretty cool :)

Import Existing Disk Image[edit | edit source]

Imagine you’ve got a bunch of old laptops lying around, each running different servers. Maybe you’ve got a Dell Latitude D620 from 2006 or a piece of junk Lenovo with a dying northbridge running your entire business phone system; not that I ever did that.

But if you did, you could use a tool like ddrescue to make a disk image of each server. Then, you can import them into your virtual machine setup and keep them running without separate installations. It’s a useful method of consolidating everything onto one machine until you have time to set things up properly!

Local Install Media[edit | edit source]

This option expects us to choose a disk image (whether for a CD-ROM or a USB stick) that we will use to make a fresh installation onto our computer. This option is for when we want to create our own virtual machine from scratch, and is what we are going to be using.

Step 1: Setting up Virtual Machine Manager (virsh)[edit | edit source]

1.0 Create new virtual machine[edit | edit source]

In Virtual Machine Manager, click “Create a new virtual machine” (usually the first icon on the toolbar or select File > New Virtual Machine from the menu).

1.1 Choose Installation Media[edit | edit source]

  • Select “Local install media (ISO image or CDROM)” and click Forward.
  • Click Browse to select your Ubuntu Server ISO.
  • Choose the ISO file you prepared earlier (e.g., /var/lib/libvirt/images/ubuntu-server.iso) and click Forward.

1.2 Choose Operating System Version[edit | edit source]

  • Virtual Machine Manager may automatically detect the OS. If not, search for ubuntu and choose what is closest to your version. When in total doubt, linux generic 2022 works. Click Forward.

1.3 Configure Memory and CPU[edit | edit source]

  • Allocate the resources for your VM:
    • Set RAM: (e.g., 4096 MB).
    • Set vCPUs: (e.g., 2 CPUs max for what we are doing here).
  • Click Forward.

1.4 Configure Storage[edit | edit source]

  • Select “Create a disk image for the virtual machine”.
  • Allocate an initial disk size that is whatever you think the maximum amount of storage you will need for email, contacts, and calendar is (e.g., 25 GB). You’ll be able to resize this disk later, so make sure it’s large enough for your initial installation but leave room for growth.
  • Make sure the disk image format is QCOW2. This format supports resizing, and other cool features.
  • Click Forward.

NOTE: QCOW2 format has a lot of useful features. It supports snapshots, which we aren’t using for our virtual machine backups, but it’s nice to have if you choose to use that. More importantly, qcow2 supports “sparse file allocation.” aka, it only uses physical disk space as it needs. Just because you say a virtual machine has access to 300 gigabytes, doesn’t mean it creates an image that actually takes up 300 gigabytes.

1.5 Set Up Networking with the Bridge Interface[edit | edit source]

  • Choose “Bridge device” under “Network Selection”.
  • In the Device Name field, type br0 (or whatever name you have given your bridge interface).
  • This will allow the VM to grab a static IP from the same network as your host machine, making sure it acts like an independent hardware device.
  • Click “Finish”.

NOTE: Choosing “bridge device” allows the virtual machine to appear like a unique hardware device on your network. That’s the idea, for each of our virtual machines to seem like Pinnocchio; a real machine :)

1.6 Finish & Customize Before Installing[edit | edit source]

  • Name your virtual machine (e.g., “mailserver”), whatever you think makes sense for a contacts/calendar/mail machine.
  • Click “Finish”.

Step 2: Install Ubuntu Server as a Virtual Machine[edit | edit source]

Note: I will be blazing through the installing of Ubuntu here, since we already installed Ubuntu server once onto this physical server.

Keep in mind the following:

We are NOT using LUKS encryption here. There is no need since the image is going to be stored on an encrypted partition.

We are NOT using RAID – this is a disk image that is being stored on a RAID array, so we are not doing that.

We are configuring networking the same as we did before, but we will be using a different IP address!

NOTE: Setting a unique static IP address to each virtual machine is necessary. It’s like having 5 businesses in the same building and expecting the postman to deliver laptops to the right address.. that’s never a problem that plagued me in new york city.

If something else is using that IP address, you cannot use it again. You don’t want your packets ending up in Berwick Maine.

2.1 Start the installation process in the virtual machine[edit | edit source]

Choose your language and select “Try or install Ubuntu Server”.

Follow the installation prompts.

2.2 Configure Static IP Address[