I’m beautiful and tough like a diamond…or beef jerky in a ball gown.

– Titus Andromedon

  • 4 Posts
  • 54 Comments
Joined 1 year ago
cake
Cake day: July 15th, 2025

help-circle


  • Security is pretty minimal, not gonna lie.

    There’s a 50 GB LUKS partition that stays locked unless I’m actively using it. It’s got backup copies of my important/critical documents and password manager exports but the rest of it is just media and doesn’t really merit encryption.

    All applications have local accounts but I’m not using LDAP or any kind of SSO like I am with my main stack.

    At home, I keep the firewall disabled on the interface configured as “WAN” so I can access its services directly via their hostname (I point its wildcard DNS record to its local “WAN” IP) but do enable firewall when I’m using it on an untrusted network. Granted, I have to manually remember to do that, so that’s kind of a security risk if I forget. Generally, though, when I’m using it remotely, it’s using my secondary phone as a USB-tethered uplink so even if I leave its internal services exposed to WAN, the NAT from the phone blocks that. One of my goals, eventually, is to automate some of the firewall rules depending on where I’m using it.



  • I think it’s the other way around, at least in the US. The last time I flew, anything with a lithium battery had to be either on your person or in your carry-on and couldn’t exceed a certain amperage/watt-hour rating. I remember having to check specifically on that since I wasn’t sure if I could bring my vape.

    But mostly, a bare PCB with two 18650’s visible isn’t something I want to have to explain to airport security lol. They may let it pass, but it’s definitely going to be a hassle. Easiest to just use an approved power bank or just power it from my phone’s USB port. The UPS was mostly so I could make it mobile and use solar chargers with it.


  • It’s kind of a mix of things duct-taped together, but here’s the gist of what controls what. If you want to see any specific configs, let me know.

    Network Manager controls the “static” interfaces. I’ve got some udev rules for my known hardware (USB wifi/ethernet adapters) so that they get friendly names as opposed to ugly “predictable” names.

    The interfaces managed by NetworkManager are:

    • STA mode Wi-Fi (internet uplink)
    • LAN Bridge
    • Wired Ethernet (internet uplink)

    Connecting a USB-tethered smartphone is pretty plug and play. It automatically gets picked up as a USB ethernet interface, receives its IP address from DHCP, and gets set as the default gateway. So there’s nothing that needs to be configured when using that as the internet uplink other than making sure there’s no other active gateway that might interfere.

    The LAN segment is a generic Linux bridge called br-lan (I’m borrowing OpenWRT’s naming convention). In normal operation, it has wlan1 and usb0 as members (AP and USB ethernet gadget, respectively). If I need a wired ethernet port on the LAN side, I just plug a USB ethernet adapter in and add it with brctl addif br-lan eth{XXX}

    The usb0 ethernet gadget interface is brought up using a script that runs at boot via systemd to configure a libcomposite ethernet gadget before the network target. This ensures it’s available when the network comes up so it can be successfully added to the LAN bridge.

    When changing out of the default configuration, I just go into network manager to enable/disable the correct interfaces. e.g. If I want to use wired ethernet for uplink and internal wifi for client AP, I enable the wired ethernet and disable the internal wifi’s connection to the router. Then I swap hostapd conf files to use the one configured for the internal wifi instead of the USB one and update the members in the LAN bridge accordingly. e.g. brctl delif br-lan wlan1 ; brctl addif br-lan wlan0

    To add a LAN-side wired ethernet, I just make sure it’s not already configured for “WAN” in NetworkManager and add it to the LAN bridge. That, or setup a VLAN interface and use a single USB ethernet adapter for both (haven’t done that on this device but I know it works from having done that in the past).

    Thankfully, PiHole exposes the DHCP controls for its underlying dnsmasq and since I’m already running PiHole for ad blocking and DNS, it was natural to also use it for DHCP. It’s configured to advertise addresses to the br-lan interface only.

    Routing/NAT is all done directly with iptables. The VPNs dynamically update it as they connect/disconnect using their up and down hook scripts, and for the NAT used for client connections, it’s basically just iptables -t nat -A POSTROUTING -s $SOURCE_CIDR -o $OUT_INTERFACE -j MASQUERADE where SOURCE_CIDR=192.168.5.0/24 is the LAN segment address range and OUT_INTERFACE=wlan0 is the uplink interface (in the default configuration).

    I’ve got some ugly scripts to adjust the NAT rules depending on which interface is currently acting as the “WAN” interface.


  • I addressed that in a few ways:

    1. I bought a quality SD card to start with. A 1 TB card is a lot of eggs in one basket so I wasn’t about to cheap out on that part.
    2. The board has 32 GB of eMMC which is where the OS is installed
    3. There are very few writes to the SD card during normal operation (after initially loading content onto it). Running data (DBs, caches, log dirs, etc) for most applications is stored on the eMMC rather than the SD card or in some cases written to tmpfs (logs).
    4. The subset of content I loaded onto this from my main media server was all chosen because it has the most re-watch potential, so re-loading close to a TB of media isn’t something that’s going to happen too often. The largest write it sees is the semi-annual refresh of the full ~130 GB Wikipedia ZIM dump, but I may push that back to once a year. I’ve only updated it twice so far.
    5. Armbian assumes it’s going to run from SD card and does a pretty good job about minimizing the number of writes. Logs are all written to zram and only occasionally written to disk, it has no swap file, etc. If those are good enough to keep an SD card happy, they should keep an eMMC even happier.

    Basically, I tried my best to configure the SD card so that in day to day use it’s WORM (write once, read many) without actually going so far as mounting it read only. The data that gets synced daily from my main servers is incremental and usually has few changes.

    I’ve had PIs running for years without issue with the SD card mounted read only and retired them from service before the SD cards ever started showing issues. My Meshtastic EAS Alerter project is using one of those Pi Zero W2’s I retired from an older project and its 6 year old SD card.

    This is actually the second iteration. Originally I attached a 1 TB SSD via a USB->NVMe enclosure. That worked, but also made the unit sprawl which was something I wanted to trim down in the final version. It worked but had random glitches and instability that I initially chalked up to the board and/or Armbian. I didn’t realize it was EMI from the Wi-Fi coming in through the USB cable until after I switched to the 1 TB SD card. That’s why I added some ghetto shielding to the power cable for lack of having ferrite beads on hand lol.

    Should the SD card prove problematic over time, I can always go back to the USB->NVMe solution and lose its “keychain” form factor.

       /_\  _ _ _ __ | |__(_)__ _ _ _  
      / _ \| '_| '  \| '_ \ / _` | ' \ 
     /_/ \_\_| |_|_|_|_.__/_\__,_|_||_|
                                       
     v25.11.2 for BananaPi BPI-M4-Zero running Armbian Linux 6.12.58-current-sunxi64
    
     Packages:     Ubuntu stable (noble)
     Updates:      Kernel upgrade enabled and 52 packages available for upgrade 
     WiFi AP:      SSID: (BananaAP), channel 6 (2437 MHz), width: 20 MHz, center1: 2437 MHz
     IPv4:         (LAN) 192.168.5.1, 10.10.10.15 (WAN) 192.168.1.12
     Containers:   postgres_postgres_1
    
     Performance:  
    
     Load:         4%           	 Uptime:       18 weeks, 22 hours, 49 minutes	 Local users:  2           	
     Memory usage: 45% of 3.83G  	 Zram usage:    74% of 1.91G  	
     CPU temp:     63°C           	 Usage of /:   35% of 29G    	
     RX today:     6 GiB  
    

  • The original intent of this was a travel router that VPN’d back to home as well as providing PiHole ad blocking, but it ballooned when I just wanted to see how much could run on this little board. I didn’t have anything in mind when I made it other than if I could, so I had to invent reasons after the fact lol

    SHTF Server

    If shit ever hits the fan (evacuation order, natural disaster, house burns down, etc), this is something that’s easy to grab, takes up almost no space, can be powered for days with a power bank, and has all my important docs on an encrypted volume. If I’m stuck in a temporary shelter or whatever, I can keep myself entertained as well as a handful of other people who can connect to it.

    I’ve also got cron jobs to sync important files from my main servers to this one, so it stays up to date.

    Power Outages

    We don’t maintain any permanent streaming subscriptions (only when there’s something good on like a new season of Star Trek drops lol). So if the power goes out, and I can’t run the main servers, this can (and has!) run from a power bank for about 36 hours. Everyone in the house can stream something different to their phone if they want.

    Camping, Traveling, Etc.

    Kinda covered under offline access, but can keep the kids entertained without cell service or racking up data overages.

    I also made a couple of Snapcast receiver speakers with some old Pi Zero W’s so we can have wireless speakers around the campsite. I was able to add an RTL-SDR dongle to it and pipe the FM audio to Snapcast, so if there’s a game on, we can listen to that. I haven’t used that in practice, but it worked on the bench.

    Internet access isn’t a given

    I’ve had to travel places that have no or poor internet access and poor cell reception. Depends on where and why I have to travel and is always a crapshoot. This gives me a bare minimum environment that’s always on hand.

    Black Start

    If my homelab ever goes totally down or has a major malfunction, I’ve got copies of documentation, console passwords (that are normally kept in Vaultwarden hosted on the stack that’s unavailable in this scenario), and other resources to bring things back up. I host pretty much everything we use (including email) so when the lab is down (rare but does happen) it’s nice to have a backup stack.




  • Yeah, I was surprised as well by how many things can run concurrently and why this project ballooned like it did. It was originally just going to be a travel router and PiHole but I decided to see how much I could cram in there. There’s still room for more but I had to move on to other projects once winter was over. If/when I have time, I’d like to add a map tile server to the mix.

    The only limit I’ve run into is when I’m running the NextJS dev server and Jellyfin at the same time. That’s just a bit too much demand on the memory so one or the other crashes. So I can’t watch JF while I work, but considering what’s hosting these, I can forgive it.






  • If it’s a relatively recent laptop, it should be fine.

    Many of them will let you set custom charge limits. If yours supports that, limit it to like 60% or thereabouts. Long enough that you can get some UPS use out of it but not full enough it’s ever gonna go spicy pillow on you.

    If it won’t let you set a charge limit, they’ll still kind of float around full charge but not stay at 100% all the time. Even plugged in, mine will drop down from 100% to eventually 92% before it will start charging back to 100 again. That’s over the course of several days to a week.

    If the laptop is older than about 2017 or so, or still has a removable battery, you might want to just take the battery out and use an external UPS as those typically don’t have the extra charge management features newer ones do.

    To run them full time, you either want to remove the screen or “tent” them because a lot of heat is dissipated through the keyboard, and it’s normally expected to be open while running because of that. By “tent”, I mean open it halfway and put the screen facing down so it’s standing up and shaped like a tent.



  • Thanks, and yeah, it’s been fun putting that all together. Unfortunately I’m still learning FreeCAD so they’re not as integrated as I’d like yet, but as soon as I have time to hammer out a design, I hope to have all 3 of these and the UPS/power supply in a nice case.

    Yep, running/charging it from solar is why I ended up getting that chonky 18650-based UPS board. It’s the only one I could find that could combine 5V input and battery without dropping out (battery kicks in immediately if solar insufficient and draws the difference between input and output and charges and powers simultaneously otherwise).


  • Thanks!

    What are the use cases for taking it with you instead of just connecting to your homelab?

    I built it just to see how much I could cram onto a Pi Zero clone/how many self-hosted services I could have on something I can fit on my keychain, and the answer was “a lot”. It’s something of a travel server, travel router, emergency backup server, etc.

    I mainly just wanted a subset of my homelab services available in something I could take with me anywhere. Home lab could go down while away, power could go out, something to use while glamping, can take it with me if there’s ever an emergency where I have to evacuate, etc.

    What started out as a single unit has become a three unit portable stack lol. Yay feature creep!

    Services

    • Jellyfin (all content pre-transcoded so everything can direct stream)
    • CodeServer (setup for Python, NodeJS/Bun + React, and Platform.IO for ESP8266/ESP32 development)
    • Kiwix (including the full Wikipedia dump with images as well as offline docs for lots of code libraries I work with, etc)
    • SearxNG so I always have a sane search engine available
    • CalibreWeb with my whole ebook library
    • MPD+Snapcast+My whole music library. Also has myMPD web UI for controlling MPD. Snapcast clients can connect, and it can serve multi-speaker/multi-room audio
    • PiHole serving both ad blocking and local DNS as well as providing DHCP for the access point
    • PairDrop for sending/receiving files
    • NodeRED and Mosquitto MQTT for setting up ad-hoc automations
    • Nginx with real LetsEncrypt certs so all services have valid SSL certs and hostnames

    Networking

    • One USB port is configured in ethernet gadget mode. Can plug it into a host PC and get an IP address from it
    • One wifi adapter is setup as an AP and is bridged with the USB ethernet (a PC plugged in and a wifi client are on the same L2 plane).
    • The second wifi is the “WAN” connection if one is available. Can alternatively connect to USB tethering on my phone
    • If there is any kind of “WAN” upstream, the LAN bridge (USB ethernet/Wifi) will route to it
    • Wireguard to connect back to my homelab.

    File Services

    • Samba
    • Encrypted LUKS volume for critical docs (tax records, vet records for the dogs, etc)

    I’ve got a second unit that connects as a client to the main one with some additional backup services:

    • Email stack( Dovecot, Postfix, SpamAssassin, ClamAV, Webmail)
    • Matrix/Synapse stack
    • Asterisk
    • Snapcast client

    The second one is basically a backup to my main stack in case of disaster/power outage/etc. Those all tunnel to a cloud VPS + load balancer and only need an internet connection to setup the tunnels to receive traffic from the VPS (and route back out to it). Those services are stopped and a cron task keeps them in sync with the main ones in my homelab. If I need to fail over, I just SSH into the VPS and re-route traffic to them instead of my homelab endpoints.

    I self-host my own email and chat and phone services, so those have become critical services I want to always have online. Essentially these little Pi clones are a backup stack for my most used services and one that is both extremely low power and portable should I ever need to host them on the go (house burns down, have to evacuate due to emergency, etc).

    Third Unit (Still on the bench)

    I have a third unit that’s built on a PiZero2W but it’s still on the workbench (but functional!). Just haven’t gotten any kind of case at all built for it.

    It’s got two RTL-SDR units attached. One is tuned to the NOAA weather radio station and feeds into Snapserver on the main unit (so you can listen to the weather radio anywhere on the network) as well as piped into Meshtastic EAS-SANE alerter in order to forward emergency alerts to Meshtastic. There’s a USB-connected Meshtastic node attached as well for that.

    The second RTL-SDR is setup as a generic FM radio tuned to the local variety station. It’s just piped to Snapserver on the main unit to make it available on the network.

    I may convert the second SDR into a ADS-B listener, but for now, I like having the FM radio available.

    Photo

    I still don’t have a “full” case for it, but here is the core unit attached to a UPS circuit which gives it up to about 14 hours of runtime. I’m also planning to add a small USB hub with ethernet into that, but I’m still learning FreeCAD so I’m not quite ready to put it all together yet. The USB power cord is wrapped in aluminum foil and electrical tape due to RF from the Wifi adapter causing random glitches. I need to add some ferrite beads and route them away from that when I build it into an integrated case. For now it looks janky but works lol.

    Main Unit:

    Secondary Unit: This is an older photo and is also connected to my Bose radio acting as a Snapcast client to the server on the main unit.


  • I run Jellyfin on a Banana Pi M4 Zero. It’s a little less capable than the Pi4 but runs JF just fine. Specs on this one are quad core 1.5 GHz, 4 GB RAM, 32 GB eMMC on Armbian.

    The media files are all on the 1 TB SD card while the Jellyfin data directory (especially the SQLite DB) are on the eMMC. This seems to work much better as the DB file kept getting corrupted on SD. Should also help the SD card from wearing out since it’s pretty much only reading data from it most of the time.

    As you guessed, transcoding is not going to work (JF is removing the v4l2 hardware support anyway), so I pre-transcode them to H264 + yuv420p in an mp4 container before moving them to the SD card. I also scale them down to 720p to fit more on there, but that’s because this is a travel server and isn’t my main media source.

    Can’t speak for Paperless though.