Lean and Local: DNS, VPN, IRC and Ad Blocking

Lean and Local: DNS, VPN, IRC and Ad Blocking

Running a full-featured network stack on old hardware isn’t just possible, it’s efficient. This post covers how I configured Alpine Linux as a diskless system hosting DNS resolution, ad blocking, VPN access, and a local IRC server.


Why Diskless?

The system boots entirely from RAM using Alpine’s diskless mode. This eliminates wear on flash storage, improves startup speed, and ensures a clean slate on each reboot. Changes are persisted manually using lbu, making the setup both robust and disposable, perfect for low-power hardware with limited write endurance.


Base Setup

The machine is an old D-Class thin client with 4 GB RAM and an AMD G-T48E CPU. Alpine Linux 3.21 is installed with only essential packages (229 total). Services are configured via OpenRC and restored from compressed overlays on boot.


DNS and Ad Blocking: Blocky

Blocky handles all local DNS queries, with DoT upstreams, custom mappings, and deny lists per client.

Highlights:

  • Local resolution for custom domains like joren.blog
  • Cloudflare, Google as upstream resolvers
  • Per-IP blocking rules
  • Prometheus metrics for monitoring

Example config:

blocking:
  denylists:
    ads:
      - https://big.oisd.nl/domainswild
    vtm:
      - /home/joren/dns/vtmgo.txt
  clientGroupsBlock:
    default:
      - ads
    172.16.0.120:
      - vtm

VPN: WireGuard via PiVPN

WireGuard provides encrypted access to the local network, and PiVPN makes managing it trivial. While WireGuard itself is minimal, PiVPN adds essential automation without requiring a full control panel or web UI.

Key benefits in this setup:

  • Client management is simple and scriptable. Adding, disabling, or revoking peers takes seconds:

    pivpn -a  # add client
    pivpn -r  # remove client
    pivpn -off <name>  # temporarily disable
    
  • Mobile-friendly. Each config can be exported as a QR code with pivpn -qr, which is perfect for importing into the WireGuard mobile app.

  • Monitoring support. View live connections with pivpn -c, or audit all issued keys with pivpn -l.

  • Backups included. One command backs up all configs: pivpn -bk.

Example output:

::: Connected Clients List :::
Name     Remote IP           Virtual IP     Bytes Received     Bytes Sent     Last Seen
Phone    84.199.x.x:60042    10.60.150.2     439MiB             3.3GiB         May 29 2025 - 22:39:56

In this context, PiVPN reduces the friction of managing WireGuard while remaining fully compatible with Alpine’s diskless, CLI-centric philosophy. No services are wasted, and all changes remain under user control.


IRC: ngIRCd

For real-time messaging, I run a public-facing ngIRCd instance accessible over both plaintext (port 6667) and encrypted TLS (ports 6697, 6698). Despite its modest footprint, ngIRCd is stable, portable, and well-suited for both LAN and internet-facing deployments.

The server is configured to:

  • Autojoin clients to a default #General channel
  • Support cloaking for user privacy
  • Restrict joins per user/IP to prevent abuse
  • Provide operator access with predefined credentials
  • Disable DNS and Ident lookups for speed and reduced leakage

Here’s a snapshot of the active configuration:

[Global]
Name = irc.alpine4071
Info = RAM-only IRC Server
Listen = 0.0.0.0
Ports = 6667
MotdPhrase = "Welcome to our RAM-only IRC server!"
AdminInfo1 = IRC Server
AdminInfo2 = Anywhere On Earth
AdminEMail = admin@irc.alpine4071

[SSL]
CertFile = /home/joren/certs/fullchain1.pem
KeyFile = /home/joren/certs/privkey1.pem
Ports = 6697, 6698

Authentication and moderation are handled via the [Operator] block, and all users are dropped into a predefined channel:

[Channel]
Name = #General
Topic = General Channel
Autojoin = yes

Why ngIRCd?

  • It works well on memory-constrained systems.
  • It doesn’t require database backends or scripting engines.
  • It supports modern essentials like TLS, cloaking, and structured limits.
  • It’s simple to secure and configure, even when exposed to the open internet.

This makes ngIRCd a perfect fit for remote support, small private networks, or just having your own IRC node to tinker with, without touching a gigabyte of storage.

You can connect today via:

irc://alpine-4071.duckdns.org:6667   (plaintext)
ircs://alpine-4071.duckdns.org:6697  (TLS)

Making It Stick: LBU

To persist changes in a stateless system, I use Alpine’s lbu:

lbu include /etc/blocky/config.yml
lbu include /etc/ngircd/ngircd.conf
lbu commit -d

This updates the overlay that Alpine loads at each boot. All runtime data stays in RAM.


Runlevel Integration

All services are enabled through OpenRC:

rc-update add blocky default
rc-update add wg-quick default
rc-update add ngircd default
rc-update add iptables default

Boot time is under 10 seconds, with full network stack active by the time DHCP finishes.


Final Thoughts

Alpine’s diskless approach is perfect for resilient, low-maintenance edge systems. Combined with fast tools like Blocky and WireGuard, even minimal hardware becomes a capable, efficient network node, quietly resolving DNS, filtering traffic, handling VPN access, and hosting IRC.