Raspberry Pi4 Home Server
BIND9 + PostgreSQL + NGINX + FreeRADIUS
This full setup guide builds your home server on Raspberry Pi4 at 10.10.10.250 for network 10.10.10.0/24. You will deploy BIND9, PostgreSQL, NGINX, and FreeRADIUS, make the Pi authoritative for codeandcore.home, and validate hostnames such as iphone.codeandcore.home.
Each lesson is explanation-first: objective, why it matters, exact commands, then verification checkpoints.
Install Raspberry Pi OS and Base Packages
Objective: prepare a stable Raspberry Pi OS foundation before installing DNS and database services.
Learning Focus: This lesson builds practical engineering judgment, not just task completion. As you run each step, connect the action to runtime behavior, failure signals, and design trade-offs so you can adapt the pattern in real systems.
Teaching Lens
This lesson teaches stable host preparation before service installation.
This lesson also focuses on operational reasoning: what healthy behavior looks like, what failure signals look like, and how this step protects the reliability of the lessons that come next.
Think of this stage as establishing deterministic runtime conditions. Package drift, missing utilities, and incorrect system time create subtle bugs later in DNS and database behavior, so this lesson is about reducing hidden variables before service deployment starts.
When this preparation is done correctly, every later troubleshooting step becomes clearer because logs are timestamp-accurate, dependency tools are present, and the base OS state is predictable across reboots and updates.
Run
This command block performs the mandatory preflight for reliable service behavior: package refresh, OS upgrades, baseline tools, and timezone alignment for consistent logs and SQL timestamps.
sudo apt-get update sudo apt-get full-upgrade -y sudo apt-get install -y curl wget git unzip htop jq dnsutils ca-certificates sudo timedatectl set-timezone Africa/Kampala timedatectl status
This lesson teaches base-system readiness for deterministic DNS and database behavior.
You verify no package errors and correct system time settings.
Checkpoint: system updates complete cleanly, tools install without dependency issues, and timedatectl reports the expected timezone.
Set Hostname and Static IP Identity (10.10.10.250)
Objective: assign predictable identity and fixed addressing so the Pi is always reachable as your DNS authority.
Learning Focus: This lesson builds practical engineering judgment, not just task completion. As you run each step, connect the action to runtime behavior, failure signals, and design trade-offs so you can adapt the pattern in real systems.
Teaching Lens
This lesson teaches stable network identity so clients and services can trust one DNS endpoint.
This lesson also focuses on operational reasoning: what healthy behavior looks like, what failure signals look like, and how this step protects the reliability of the lessons that come next.
Run
This block sets host identity and static interface configuration. The key outcome is a permanent resolver endpoint at 10.10.10.250 for the 10.10.10.0/24 network.
sudo hostnamectl set-hostname pi-dns-core echo "127.0.1.1 pi-dns-core" | sudo tee -a /etc/hosts # Configure static IP in dhcpcd sudo tee -a /etc/dhcpcd.conf >/dev/null <<'EOF' interface eth0 static ip_address=10.10.10.250/24 static routers=10.10.10.10 static domain_name_servers=10.10.10.250 EOF sudo systemctl restart dhcpcd ip -4 addr show eth0
Add a DHCP reservation for the Pi MAC so 10.10.10.250 is never reused by another device.
You verify hostname resolves locally and server IP remains stable.
Checkpoint: ip -4 addr show eth0 reports 10.10.10.250/24 and the host remains reachable after network restart.
Install BIND9
Objective: deploy and start the DNS engine that will serve recursive and authoritative requests.
Learning Focus: This lesson builds practical engineering judgment, not just task completion. As you run each step, connect the action to runtime behavior, failure signals, and design trade-offs so you can adapt the pattern in real systems.
Teaching Lens
This lesson teaches resolver installation with diagnostic tooling.
This lesson also focuses on operational reasoning: what healthy behavior looks like, what failure signals look like, and how this step protects the reliability of the lessons that come next.
Run
This install sequence brings in the resolver daemon and support tools, then enables the service for startup persistence.
sudo apt-get install -y bind9 bind9utils bind9-doc dnsutils sudo systemctl enable --now bind9 sudo systemctl status bind9 --no-pager
You verify bind9 service is active and named-checkconf is available.
Checkpoint: BIND9 is active and initial health commands return without fatal errors.
Configure BIND9 as Home Authoritative + Recursive Resolver
Objective: make the Pi authoritative for codeandcore.home while still resolving internet DNS for clients.
Learning Focus: This lesson builds practical engineering judgment, not just task completion. As you run each step, connect the action to runtime behavior, failure signals, and design trade-offs so you can adapt the pattern in real systems.
Teaching Lens
This lesson teaches trust boundaries, recursion behavior, and logging for learning analytics.
This lesson also focuses on operational reasoning: what healthy behavior looks like, what failure signals look like, and how this step protects the reliability of the lessons that come next.
Configure named.conf.options
This policy block limits recursion to trusted sources (127.0.0.1 and 10.10.10.0/24), defines upstream forwarders, and hardens transfer/update behavior.
acl "trusted" { 127.0.0.1; 10.10.10.0/24; }; options { directory "/var/cache/bind"; listen-on { any; }; listen-on-v6 { any; }; allow-query { trusted; }; allow-recursion { trusted; }; forwarders { 1.1.1.1; 8.8.8.8; 9.9.9.9; }; forward only; recursion yes; dnssec-validation auto; allow-transfer { none; }; notify no; };
Create authoritative local zone
This declaration tells BIND it owns the codeandcore.home namespace and where to read authoritative records.
zone "codeandcore.home" {
type master;
file "/etc/bind/db.codeandcore.home";
allow-update { none; };
};This zone file defines your local naming contract. Example hostnames such as iphone.codeandcore.home are resolved here, not upstream.
$TTL 86400
@ IN SOA ns1.codeandcore.home. admin.codeandcore.home. (
2026051201 ; serial
3600 ; refresh
1800 ; retry
1209600 ; expire
86400 ) ; minimum
@ IN NS ns1.codeandcore.home.
ns1 IN A 10.10.10.250
dns IN A 10.10.10.250
iphone IN A 10.10.10.50
router IN A 10.10.10.10
printer IN A 10.10.10.40Set DHCP to use this resolver
Clients must be pointed to 10.10.10.250 by DHCP, otherwise they will bypass your authoritative resolver and local names will fail on client devices.
1) Open IP -> DHCP Server -> Networks.
2) Open the 10.10.10.0/24 network entry.
3) Set Gateway to 10.10.10.10.
4) Set DNS Servers to 10.10.10.250.
5) Set Domain to codeandcore.home.
6) Apply and OK.
7) Renew DHCP lease on one client and confirm DNS is now 10.10.10.250.Enable query logging
Logging makes DNS activity observable for verification and analytics. Without this, troubleshooting and usage analysis become guesswork.
sudo mkdir -p /var/log/named sudo chown bind:bind /var/log/named logging { channel queries_log { file "/var/log/named/queries.log" versions 7 size 200m; severity dynamic; print-time yes; }; category queries { queries_log; }; category query-errors { queries_log; }; };
Validate and restart
This validation chain confirms both resolver modes: recursion (internet domains) and authoritative local zone answers.
sudo named-checkconf sudo systemctl restart bind9 dig @127.0.0.1 google.com A dig @10.10.10.250 iphone.codeandcore.home A sudo tail -n 20 /var/log/named/queries.log
You verify both internet recursion and local zone records (for example iphone.codeandcore.home) resolve correctly.
Checkpoint: clients in 10.10.10.0/24 resolve external domains and local hostnames such as iphone.codeandcore.home through 10.10.10.250.
Install PostgreSQL
Objective: install persistent relational storage for DNS analytics and metadata.
Learning Focus: This lesson builds practical engineering judgment, not just task completion. As you run each step, connect the action to runtime behavior, failure signals, and design trade-offs so you can adapt the pattern in real systems.
Teaching Lens
This lesson teaches database runtime installation before schema and analytics setup.
This lesson also focuses on operational reasoning: what healthy behavior looks like, what failure signals look like, and how this step protects the reliability of the lessons that come next.
Run
This block installs PostgreSQL and enables startup persistence, then confirms engine availability with a version query.
sudo apt-get install -y postgresql postgresql-contrib sudo systemctl enable --now postgresql sudo -u postgres psql -c "SELECT version();"
You verify PostgreSQL service is active and version query returns successfully.
Checkpoint: database service stays active and responds to administrative commands.
Create DNS Analytics Database
Objective: create least-privilege database credentials and dedicated analytics database.
Learning Focus: This lesson builds practical engineering judgment, not just task completion. As you run each step, connect the action to runtime behavior, failure signals, and design trade-offs so you can adapt the pattern in real systems.
Teaching Lens
This lesson teaches least-privilege database access for DNS ingestion and reporting.
This lesson also focuses on operational reasoning: what healthy behavior looks like, what failure signals look like, and how this step protects the reliability of the lessons that come next.
Run
This SQL block creates an application user and an isolated database so ingestion and reporting avoid superuser dependence.
CREATE USER dns_user WITH PASSWORD 'ChangeThisPassword!' NOSUPERUSER NOCREATEDB NOCREATEROLE LOGIN; CREATE DATABASE dns_analytics OWNER dns_user; GRANT ALL PRIVILEGES ON DATABASE dns_analytics TO dns_user; \q
You verify dns_user can connect to dns_analytics using password authentication.
Checkpoint: dns_user authenticates and database ownership/permissions are correct.
Make the Server Use Itself as DNS
Objective: force host-level lookups on the Pi to use local BIND for consistent resolver behavior.
Learning Focus: This lesson builds practical engineering judgment, not just task completion. As you run each step, connect the action to runtime behavior, failure signals, and design trade-offs so you can adapt the pattern in real systems.
Teaching Lens
This lesson teaches host-level resolver redirection to local BIND on loopback.
This lesson also focuses on operational reasoning: what healthy behavior looks like, what failure signals look like, and how this step protects the reliability of the lessons that come next.
The core concept is path control. If the host itself does not resolve through the same DNS path you designed for clients, behavior diverges and diagnostics become misleading. Redirecting to loopback aligns local and network-facing lookup behavior.
This also teaches verification discipline: first prove BIND answers locally, then change resolver plumbing, then confirm queries appear in logs. That order avoids masking resolver misconfiguration behind operating-system DNS defaults.
Run
This sequence first verifies local resolver answers, then rewires systemd-resolved to loopback and confirms both resolver status and query logging.
dig @127.0.0.1 google.com A sudo cp /etc/systemd/resolved.conf /etc/systemd/resolved.conf.bak sudo tee /etc/systemd/resolved.conf >/dev/null <<'EOF' [Resolve] DNS=127.0.0.1 FallbackDNS= Domains=~. EOF sudo systemctl restart systemd-resolved resolvectl status dig google.com A sudo tail -n 20 /var/log/named/queries.log
You verify resolvectl shows DNS=127.0.0.1 and host queries appear in BIND logs.
Checkpoint: host DNS path is local-first and still resolves both internet and local domains successfully.
Final Stack Validation
Objective: verify resolver, authoritative zone, and database operate correctly together after configuration.
Learning Focus: This lesson builds practical engineering judgment, not just task completion. As you run each step, connect the action to runtime behavior, failure signals, and design trade-offs so you can adapt the pattern in real systems.
Teaching Lens
This lesson teaches final proof that resolver and database are healthy on the Pi.
This lesson also focuses on operational reasoning: what healthy behavior looks like, what failure signals look like, and how this step protects the reliability of the lessons that come next.
Run
This final test confirms service health, recursion, local authority answers, and database responsiveness in one runbook.
sudo systemctl status bind9 --no-pager sudo systemctl status postgresql --no-pager dig @127.0.0.1 cloudflare.com A dig @10.10.10.250 iphone.codeandcore.home A psql -U dns_user -d dns_analytics -h localhost -c "SELECT NOW();"
You are complete when clients in 10.10.10.0/24 use 10.10.10.250 as DNS, local names like iphone.codeandcore.home resolve, and PostgreSQL is healthy after reboot.
Fast Internet Blueprint for Your Home
Objective: optimize perceived speed for YouTube, Prime Video, Netflix, and large device updates across your whole home.
Learning Focus: This lesson builds practical engineering judgment, not just task completion. As you run each step, connect the action to runtime behavior, failure signals, and design trade-offs so you can adapt the pattern in real systems.
Teaching Lens
This lesson teaches what actually improves speed in 2026 for encrypted streaming traffic.
This lesson also focuses on operational reasoning: what healthy behavior looks like, what failure signals look like, and how this step protects the reliability of the lessons that come next.
The performance model here is layered. DNS efficiency improves startup and connection setup, queue discipline preserves responsiveness under contention, and selective caching helps only where protocols allow reuse. Mixing those levers correctly is what users feel as "fast internet" in real homes.
This lesson also corrects a common misconception: throughput alone is not quality. Stability under concurrent family traffic is usually a latency and queueing problem, not a raw bandwidth problem.
Traditional generic web caching proxies are mostly low-value for modern HTTPS/QUIC streaming. The biggest wins are DNS cache quality, good Wi-Fi/wired backhaul, and SQM/QoS on the router.
Checkpoint: peak-time streaming remains stable, app launch DNS lookups are faster, and updates no longer saturate the whole network.
Tune BIND9 for Better Cache Behavior
Objective: reduce repeat lookup latency and improve resolver resilience under family traffic bursts.
Learning Focus: This lesson builds practical engineering judgment, not just task completion. As you run each step, connect the action to runtime behavior, failure signals, and design trade-offs so you can adapt the pattern in real systems.
Teaching Lens
This lesson teaches cache tuning that improves responsiveness without breaking correctness.
This lesson also focuses on operational reasoning: what healthy behavior looks like, what failure signals look like, and how this step protects the reliability of the lessons that come next.
Run
Apply these options inside /etc/bind/named.conf.options in your existing options { ... } block, then reload BIND.
options {
directory "/var/cache/bind";
listen-on { any; };
allow-query { trusted; };
allow-recursion { trusted; };
forwarders { 1.1.1.1; 8.8.8.8; 9.9.9.9; };
forward only;
dnssec-validation auto;
max-cache-size 256m;
min-cache-ttl 60;
max-cache-ttl 86400;
max-ncache-ttl 300;
serve-stale yes;
stale-answer-enable yes;
allow-transfer { none; };
notify no;
};sudo named-checkconf sudo systemctl restart bind9 for i in 1 2 3; do dig @10.10.10.250 youtube.com A +stats | grep "Query time"; done
This lesson teaches practical cache tuning for lower repeat-query latency and graceful stale serving.
You verify second/third query times are lower and resolver remains responsive during upstream hiccups.
Checkpoint: repeated domain lookups are faster and no resolver instability appears after restart.
Use Caching Where It Still Delivers Value
Objective: deploy caching only for traffic classes that still benefit in modern encrypted ecosystems.
Learning Focus: This lesson builds practical engineering judgment, not just task completion. As you run each step, connect the action to runtime behavior, failure signals, and design trade-offs so you can adapt the pattern in real systems.
Teaching Lens
This lesson teaches selective caching strategy, not one-size-fits-all proxying.
This lesson also focuses on operational reasoning: what healthy behavior looks like, what failure signals look like, and how this step protects the reliability of the lessons that come next.
Selective caching is a cost-benefit decision. Modern encrypted streaming generally prevents useful intermediary reuse, so generic proxy caching adds complexity without proportional gain. Update channels and package repositories remain the practical high-value targets.
The operational lesson is to measure before expanding scope. Start with one cacheable class, confirm measurable savings, then decide if additional layers are worth maintenance overhead.
Apt cache for Linux devices (high value if multiple Debian/Ubuntu clients)
This setup is worth running when many Linux clients repeat package downloads. It does not accelerate encrypted video streams, but it cuts repeated package bandwidth immediately.
sudo apt-get install -y apt-cacher-ng sudo systemctl enable --now apt-cacher-ng sudo systemctl status apt-cacher-ng --no-pager
echo 'Acquire::http::Proxy "http://10.10.10.250:3142";' | sudo tee /etc/apt/apt.conf.d/01proxy sudo apt-get update
Windows and Apple updates without Squid complexity
For non-Linux homes, modern update optimization is better done by platform-native mechanisms instead of a generic proxy cache. These methods are aware of signed content distribution rules and are more reliable than transparent proxy tricks.
Windows path: enable Delivery Optimization LAN sharing on all Windows PCs.
Apple path: enable Content Caching on an always-on Mac where available.
Android and TV path: rely on scheduled update windows plus QoS rather than proxy caching.
Policy decision: keep Squid optional for lab validation, not as a core streaming accelerator.YouTube, Prime Video, and Netflix are typically encrypted end-to-end with dynamic manifests and DRM. Generic caching proxies usually provide little to no benefit for that content.
For your home scenario, Squid is not a primary speed tool anymore. Keep it out of the critical path unless you have a narrow, measured use case that proves benefit.
You verify apt-cacher-ng hit counters increase when multiple clients update packages.
Checkpoint: update bandwidth drops for repeated Linux package downloads while streaming behavior remains unchanged.
Configure SQM/QoS for Real-World Speed Feel
Objective: eliminate bufferbloat so streams stay smooth and interactive traffic stays responsive during heavy downloads.
Learning Focus: This lesson builds practical engineering judgment, not just task completion. As you run each step, connect the action to runtime behavior, failure signals, and design trade-offs so you can adapt the pattern in real systems.
Teaching Lens
This lesson teaches that queue discipline usually beats caching proxy tricks for family streaming homes.
This lesson also focuses on operational reasoning: what healthy behavior looks like, what failure signals look like, and how this step protects the reliability of the lessons that come next.
Run (router side)
Apply Smart Queue Management on your WAN bottleneck at about 90-95% of real line speed. Keep FastTrack disabled for traffic that must be shaped, otherwise queue control is bypassed and latency spikes return under load.
Measure real WAN throughput (down and up) during quiet time.
Set SQM bandwidth caps to approximately 90-95% of measured rates.
Enable CAKE or FQ_CoDel according to platform support.
Prioritize latency-sensitive classes over bulk download classes.
Re-test while one device streams and another performs large updates.The RB2011 can struggle when advanced shaping is pushed near high WAN rates. Shape below line rate first, then increase carefully while watching CPU load and latency.
This lesson teaches queue control as the main lever for perceived speed under contention.
You verify stable stream quality and lower latency during concurrent downloads.
Checkpoint: family streaming remains smooth while updates/downloads run in parallel, with no severe latency spikes.
MikroTik RB2011 Production Profile (10.10.10.0/24)
Objective: apply a practical RB2011-specific baseline that enforces your Pi DNS at 10.10.10.250 and improves stream stability during updates.
Learning Focus: This lesson builds practical engineering judgment, not just task completion. As you run each step, connect the action to runtime behavior, failure signals, and design trade-offs so you can adapt the pattern in real systems.
Teaching Lens
This lesson teaches RB2011 controls that matter most: DHCP DNS policy, DNS hijack prevention, and conservative SQM.
This lesson also focuses on operational reasoning: what healthy behavior looks like, what failure signals look like, and how this step protects the reliability of the lessons that come next.
The goal is policy enforcement, not just configuration. DHCP advertisement sets the preferred DNS path, while NAT redirect rules enforce it for misconfigured clients. Together, those controls keep local-zone behavior consistent across your LAN.
The second lesson is capacity realism. RB2011 queueing can improve user experience significantly, but only when shaping targets are chosen with CPU limits in mind and validated under concurrent traffic.
Step 1: Enforce your DNS resolver in DHCP and NAT
This block makes the RB2011 advertise your Pi DNS and transparently redirects LAN DNS requests to 10.10.10.250 so client bypass attempts do not break local zone behavior.
DHCP policy:
1) Open IP -> DHCP Server -> Networks.
2) Edit the 10.10.10.0/24 network.
3) Set DNS Servers to 10.10.10.250.
4) Set Domain to codeandcore.home.
DNS redirect policy:
1) Open IP -> Firewall -> NAT.
2) Add a new dstnat rule for UDP port 53 from src-address 10.10.10.0/24.
3) Set action to dst-nat and To Addresses to 10.10.10.250.
4) Add a second dstnat rule with the same settings for TCP port 53.
5) Place both DNS redirect rules above broad accept/fasttrack-style rules.
6) Apply and test from a client that previously used external DNS.Step 2: Apply RB2011-friendly queue shaping
Start conservative to protect latency. Set limits around 85-90% of measured line rate, then tune upward only if CPU and latency stay healthy. Disable FastTrack for flows that must be shaped.
1) Run 3 speed tests at quiet time and write down stable down/up rates.
2) Open IP -> Firewall -> Filter Rules and disable FastTrack for traffic you want shaped.
3) Open Queues and create your WAN shaping policy for 10.10.10.0/24.
4) Set max limits to around 85-90% of measured down/up rates.
5) Choose fq-codel where available; otherwise use PCQ as fallback.
6) Apply changes and run a stream + update test to confirm lower latency spikes.If fq-codel is not available on your firmware, update RouterOS first. If you cannot update immediately, use PCQ queue types as a temporary fallback and keep conservative max-limit values.
PCQ fallback steps:
1) Open Queues -> Queue Types.
2) Create one PCQ type for download (classifier: destination address).
3) Create one PCQ type for upload (classifier: source address).
4) Open Queues -> Simple Queues and create a queue for target 10.10.10.0/24.
5) Set max limits to conservative values (about 85-90% of line rate).
6) Assign PCQ upload/download types to the queue and retest under load.Step 3: Verify under real family load
Validation should happen while one device streams and another runs updates. If video quality is stable and browsing latency stays responsive, your policy is working.
Confirm the client DNS server is 10.10.10.250.
Run simultaneous Netflix or YouTube streaming plus a large OS update.
Ping 1.1.1.1 from a client during load and monitor latency spread.
If latency spikes significantly, lower max-limit values and retest.
If stable and CPU headroom exists, raise caps in small controlled steps.RB2011 built-in Wi-Fi is an old 2.4 GHz radio. For modern streaming quality, use a dedicated dual-band AP (AC/AX) on Ethernet and keep RB2011 focused on routing/QoS.
This lesson teaches a production-ready RB2011 baseline tailored to your 10.10.10.0/24 network.
You verify DNS policy enforcement and stable stream quality during parallel update traffic.
Checkpoint: all clients resolve through 10.10.10.250, local names stay reliable, and streaming remains smooth during heavy background updates.
Install NGINX and Publish the Service
Objective: install NGINX as your home web front-end and expose it safely on your LAN.
Learning Focus: This lesson builds practical engineering judgment, not just task completion. As you run each step, connect the action to runtime behavior, failure signals, and design trade-offs so you can adapt the pattern in real systems.
Teaching Lens
This lesson teaches clean web service installation, startup persistence, and first health validation.
This lesson also focuses on operational reasoning: what healthy behavior looks like, what failure signals look like, and how this step protects the reliability of the lessons that come next.
This lesson frames NGINX as infrastructure entrypoint rather than a simple web page host. You are establishing a front-end service that can terminate TLS, proxy internal apps, and provide a stable access surface for future services.
Health validation at this stage should include both service state and socket exposure so you know the daemon is not only running but actually reachable from the network context where clients will consume it.
Run
This block installs NGINX, enables boot-time startup, and validates that it is listening before you build custom site definitions.
sudo apt-get install -y nginx sudo systemctl enable --now nginx sudo systemctl status nginx --no-pager ss -tulpn | grep -E ':80|:443'
You verify NGINX is active and listening on HTTP/HTTPS sockets.
Checkpoint: browsing to http://10.10.10.250 from LAN returns the default NGINX page.
Build Your NGINX Home Site and Reverse Proxy
Objective: create a maintainable NGINX site profile for local domains and internal app fronting.
Learning Focus: This lesson builds practical engineering judgment, not just task completion. As you run each step, connect the action to runtime behavior, failure signals, and design trade-offs so you can adapt the pattern in real systems.
Teaching Lens
This lesson teaches virtual host structure, static home portal serving, and reverse proxy routing patterns.
This lesson also focuses on operational reasoning: what healthy behavior looks like, what failure signals look like, and how this step protects the reliability of the lessons that come next.
Create site content and server block
This configuration gives you a primary site at home.codeandcore.home and an example reverse proxy endpoint for a local app running on port 3000.
sudo mkdir -p /var/www/home-core cat <<'EOF' | sudo tee /var/www/home-core/index.html >/dev/null <!doctype html> <html lang="en"> <head><meta charset="utf-8"><title>Home Core</title></head> <body><h1>home.codeandcore.home</h1><p>NGINX is live.</p></body> </html> EOF cat <<'EOF' | sudo tee /etc/nginx/sites-available/home-core >/dev/null server { listen 80; server_name home.codeandcore.home; root /var/www/home-core; index index.html; location / { try_files $uri $uri/ =404; } location /app/ { proxy_pass http://127.0.0.1:3000/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } EOF sudo ln -sf /etc/nginx/sites-available/home-core /etc/nginx/sites-enabled/home-core sudo rm -f /etc/nginx/sites-enabled/default sudo nginx -t sudo systemctl reload nginx
This lesson teaches how to host your own local site and proxy internal services behind one NGINX endpoint.
You verify home.codeandcore.home resolves and serves your custom page from NGINX.
Checkpoint: http://home.codeandcore.home opens your custom page and /app/ forwards to your local app if it is running.
Add TLS and Baseline NGINX Hardening
Objective: protect local credentials and admin pages by serving HTTPS with sane defaults.
Learning Focus: This lesson builds practical engineering judgment, not just task completion. As you run each step, connect the action to runtime behavior, failure signals, and design trade-offs so you can adapt the pattern in real systems.
Teaching Lens
This lesson teaches local-certificate creation, HTTPS virtual hosts, and minimum hardening headers.
This lesson also focuses on operational reasoning: what healthy behavior looks like, what failure signals look like, and how this step protects the reliability of the lessons that come next.
Create local certificate and HTTPS server block
This block generates a private certificate for your local domain and upgrades NGINX from plain HTTP-only to HTTPS-first serving.
sudo mkdir -p /etc/nginx/certs sudo openssl req -x509 -nodes -newkey rsa:4096 -days 825 \ -keyout /etc/nginx/certs/home-core.key \ -out /etc/nginx/certs/home-core.crt \ -subj "/CN=home.codeandcore.home" cat <<'EOF' | sudo tee /etc/nginx/sites-available/home-core >/dev/null server { listen 80; server_name home.codeandcore.home; return 301 https://$host$request_uri; } server { listen 443 ssl http2; server_name home.codeandcore.home; ssl_certificate /etc/nginx/certs/home-core.crt; ssl_certificate_key /etc/nginx/certs/home-core.key; ssl_protocols TLSv1.2 TLSv1.3; add_header X-Frame-Options SAMEORIGIN always; add_header X-Content-Type-Options nosniff always; add_header Referrer-Policy no-referrer-when-downgrade always; root /var/www/home-core; index index.html; location / { try_files $uri $uri/ =404; } location /app/ { proxy_pass http://127.0.0.1:3000/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } EOF sudo nginx -t sudo systemctl reload nginx
Self-signed certs are expected to warn in browsers until you import your local CA/certificate into trusted stores.
Checkpoint: https://home.codeandcore.home loads and HTTP redirects automatically to HTTPS.
Install FreeRADIUS and Enable Core Modules
Objective: deploy FreeRADIUS as your AAA backend for MikroTik authentication and accounting.
Learning Focus: This lesson builds practical engineering judgment, not just task completion. As you run each step, connect the action to runtime behavior, failure signals, and design trade-offs so you can adapt the pattern in real systems.
Teaching Lens
This lesson teaches FreeRADIUS package deployment, service startup, and module activation for practical home-lab AAA.
This lesson also focuses on operational reasoning: what healthy behavior looks like, what failure signals look like, and how this step protects the reliability of the lessons that come next.
Run
This command sequence installs the server, utilities, and SQL support package, then confirms runtime status.
sudo apt-get install -y freeradius freeradius-utils freeradius-postgresql sudo systemctl enable --now freeradius sudo systemctl status freeradius --no-pager sudo freeradius -CX
You verify FreeRADIUS starts cleanly and config sanity check passes.
Checkpoint: FreeRADIUS service is active with no syntax errors from freeradius -CX.
Configure FreeRADIUS Clients, Users, and SQL Logging
Objective: define MikroTik as a trusted RADIUS client, create test users, and store accounting data in PostgreSQL.
Learning Focus: This lesson builds practical engineering judgment, not just task completion. As you run each step, connect the action to runtime behavior, failure signals, and design trade-offs so you can adapt the pattern in real systems.
Teaching Lens
This lesson teaches identity policy in FreeRADIUS and how to align AAA data with your existing PostgreSQL stack.
This lesson also focuses on operational reasoning: what healthy behavior looks like, what failure signals look like, and how this step protects the reliability of the lessons that come next.
Define MikroTik client and local test identities
This configuration tells FreeRADIUS which NAS can send requests and creates a starter user policy for authentication testing.
sudo cp /etc/freeradius/3.0/clients.conf /etc/freeradius/3.0/clients.conf.bak cat <<'EOF' | sudo tee /etc/freeradius/3.0/clients.d/mikrotik-rb2011.conf >/dev/null client rb2011 { ipaddr = 10.10.10.10 secret = ChangeThisRadiusSecret! shortname = rb2011 nastype = mikrotik } EOF sudo cp /etc/freeradius/3.0/mods-config/files/authorize /etc/freeradius/3.0/mods-config/files/authorize.bak cat <<'EOF' | sudo tee -a /etc/freeradius/3.0/mods-config/files/authorize >/dev/null homeadmin Cleartext-Password := "ChangeThisStrongPassword!" Reply-Message := "Welcome %{User-Name}" EOF
Enable PostgreSQL accounting
These steps connect FreeRADIUS to your existing PostgreSQL instance so accounting events are queryable with your analytics workflows.
sudo -u postgres psql -c "CREATE DATABASE radius OWNER dns_user;" sudo -u postgres psql radius -f /etc/freeradius/3.0/mods-config/sql/main/postgresql/schema.sql sudo cp /etc/freeradius/3.0/mods-available/sql /etc/freeradius/3.0/mods-available/sql.bak sudo sed -i 's/dialect = "sqlite"/dialect = "postgresql"/' /etc/freeradius/3.0/mods-available/sql sudo sed -i 's#driver = "rlm_sql_${dialect}"#driver = "rlm_sql_${dialect}"#' /etc/freeradius/3.0/mods-available/sql sudo sed -i 's/server = "localhost"/server = "127.0.0.1"/' /etc/freeradius/3.0/mods-available/sql sudo sed -i 's/login = "radius"/login = "dns_user"/' /etc/freeradius/3.0/mods-available/sql sudo sed -i 's/password = "radpass"/password = "ChangeThisPassword!"/' /etc/freeradius/3.0/mods-available/sql sudo sed -i 's/radius_db = "radius"/radius_db = "radius"/' /etc/freeradius/3.0/mods-available/sql sudo ln -sf /etc/freeradius/3.0/mods-available/sql /etc/freeradius/3.0/mods-enabled/sql sudo systemctl restart freeradius sudo systemctl status freeradius --no-pager
Rotate all placeholder secrets immediately: RADIUS shared secret, test user password, and SQL password entries.
Checkpoint: FreeRADIUS starts with SQL module enabled and no module-load failures in journal logs.
Connect RB2011 to FreeRADIUS Using GUI Only
Objective: link MikroTik authentication to your Pi RADIUS server without RouterOS CLI syntax.
Learning Focus: This lesson builds practical engineering judgment, not just task completion. As you run each step, connect the action to runtime behavior, failure signals, and design trade-offs so you can adapt the pattern in real systems.
Teaching Lens
This lesson teaches WinBox/WebFig navigation for RADIUS server registration and service binding.
This lesson also focuses on operational reasoning: what healthy behavior looks like, what failure signals look like, and how this step protects the reliability of the lessons that come next.
MikroTik GUI flow
Use this procedure to register your RADIUS server and assign it to services such as Hotspot, PPP, Login, or Wireless security depending on your deployment.
1) Open Radius from the left menu. 2) Add a new server entry. 3) Address: 10.10.10.250. 4) Secret: same value as FreeRADIUS client secret. 5) Authentication Port: 1812, Accounting Port: 1813. 6) Timeout: start with 300ms to 600ms on LAN. 7) Check services you need: Login, Hotspot, PPP, Wireless. 8) Apply and save. If using Wi-Fi enterprise profile in MikroTik GUI: 1) Open Wireless -> Security Profiles. 2) Edit your enterprise profile. 3) Set authentication to WPA2-EAP or WPA3-EAP (if supported). 4) Enable RADIUS MAC/Auth as required by your design. 5) Select the Radius server entry you just created. 6) Apply and reconnect one test client.
You verify MikroTik shows Access-Accept replies and successful accounting starts in logs.
Checkpoint: a test account authenticates through RB2011 against FreeRADIUS on 10.10.10.250.
End-to-End Validation and Operations Runbook
Objective: prove NGINX and FreeRADIUS are production-usable and survive reboot with clear troubleshooting paths.
Learning Focus: This lesson builds practical engineering judgment, not just task completion. As you run each step, connect the action to runtime behavior, failure signals, and design trade-offs so you can adapt the pattern in real systems.
Teaching Lens
This lesson teaches repeatable validation for web, AAA, and log visibility so changes are safe over time.
This lesson also focuses on operational reasoning: what healthy behavior looks like, what failure signals look like, and how this step protects the reliability of the lessons that come next.
Run full validation
This runbook confirms service health, local HTTPS response, RADIUS authentication, and SQL accounting availability in one pass.
sudo systemctl status nginx --no-pager sudo systemctl status freeradius --no-pager curl -kI https://home.codeandcore.home radtest homeadmin ChangeThisStrongPassword! 127.0.0.1 0 ChangeThisRadiusSecret! sudo journalctl -u freeradius -n 80 --no-pager psql -U dns_user -d radius -h localhost -c "SELECT COUNT(*) FROM radacct;"
You are complete when NGINX serves your local HTTPS site, RADIUS test authentication succeeds, MikroTik GUI service binding works, and accounting rows appear in PostgreSQL.
Common Issues on Raspberry Pi4
# BIND9 syntax and logs sudo named-checkconf sudo journalctl -u bind9 -n 50 # PostgreSQL logs sudo journalctl -u postgresql -n 50 # Resolver path resolvectl status cat /etc/resolv.conf
Check service ordering and ensure both bind9 and systemd-resolved are enabled. If needed, restart in this order: bind9, then systemd-resolved.