There's something deeply satisfying about running infrastructure that costs literally nothing per month. No AWS bill. No DigitalOcean invoice. Just sunlight hitting a panel on your windowsill, powering a tiny computer serving real HTTP requests to real people.
I got nerd-sniped by a project on Hackaday recently — someone running a solar-powered Raspberry Pi as a web server with only 27MB of RAM in use. My first reaction was skepticism. My second was "I need to try this." My third was three days of yak-shaving that taught me more about Linux memory management than the previous eight years combined.
Here's what I learned about squeezing a web server into almost nothing.
The Problem: Modern Web Servers Are Absurdly Bloated
Spin up a basic Node.js Express server that serves a single HTML page. Check your memory usage. You're looking at 30-50MB minimum before a single request hits it. A default Apache install? Easily 100MB+. Nginx is leaner but still pulls in 10-20MB with its worker processes.
When your entire system has 512MB of RAM (Raspberry Pi Zero) and you need the OS, the network stack, and the server to coexist — while also surviving on intermittent solar power — every megabyte matters. The goal isn't just "make it work." It's "make it work reliably on a power budget that disappears every night."
Step 1: Strip the OS Down to Bare Minimum
Forget Raspberry Pi OS with a desktop. Forget Raspberry Pi OS Lite, even. We're going deeper.
Start with a minimal image like DietPi or build a custom image with Buildroot. The key is disabling everything you don't need:
# Disable services that eat RAM for breakfast
sudo systemctl disable bluetooth
sudo systemctl disable avahi-daemon
sudo systemctl disable triggerhappy
sudo systemctl disable apt-daily.timer
sudo systemctl disable apt-daily-upgrade.timer
sudo systemctl disable man-db.timer
# Disable swap — on SD cards, swap kills your storage
sudo dphys-swapfile swapoff
sudo systemctl disable dphys-swapfile
# Drop to single TTY instead of six
# Edit /etc/systemd/logind.conf
NAutoVTs=1
ReserveVT=0
After stripping services, a fresh DietPi install idles at around 20-25MB. That's your OS budget. Everything else needs to fit in the remaining space.
Step 2: Choose the Right Web Server
Nginx is the popular "lightweight" choice, but there are servers purpose-built for this constraint level:
BusyBox httpd — already included in most minimal Linux distros. Serves static files with almost zero overhead. We're talking kilobytes of memory, not megabytes.
# Serve static files from /var/www with BusyBox httpd
# -f keeps it in foreground, -p sets port, -h sets document root
busybox httpd -f -p 80 -h /var/www
darkhttpd — a single-file static HTTP server. One C file. Compiles to a ~30KB binary. Memory usage hovers around 150-300KB.
# Build from source — it's literally one file
gcc -O2 -o darkhttpd darkhttpd.c
# Run it
./darkhttpd /var/www --port 80 --daemon --log /var/log/darkhttpd.log
lighttpd — if you need CGI or some dynamic content, lighttpd runs comfortably at 1-2MB. Much heavier than the others, but still a fraction of Nginx.
For pure static serving, darkhttpd or BusyBox httpd are hard to beat.
Step 3: Make Your Content Tiny
Serving a 2MB React bundle from a solar-powered Pi is... a choice. Here's the approach that actually works:
<!-- Keep it simple. This entire page is under 5KB. -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>My Solar-Powered Site</title>
<style>
/* Inline CSS — one fewer HTTP request */
body {
font-family: system-ui, sans-serif; /* no web font download */
max-width: 42rem;
margin: 2rem auto;
padding: 0 1rem;
line-height: 1.6;
color: #1a1a1a;
}
/* Use prefers-color-scheme instead of JS theme toggles */
@media (prefers-color-scheme: dark) {
body { background: #111; color: #e0e0e0; }
}
</style>
</head>
<body>
<h1>Served by sunlight</h1>
<p>This page was delivered by a computer running on a solar panel.</p>
<!-- Server status injected by a simple CGI script or cron-generated file -->
<p>Battery: <!--#include virtual="/status.txt" --></p>
</body>
</html>
Key rules: inline your CSS, skip JavaScript entirely if possible, use system fonts, optimize images aggressively (or just don't use them).
Step 4: Survive the Night
This is where it gets interesting. Solar power means your server goes offline when the sun does — unless you plan for it.
The approach I've seen work best:
#!/bin/bash
# /usr/local/bin/power-monitor.sh
# Check battery voltage via ADC and gracefully shut down before power dies
VOLTAGE=$(cat /sys/class/power_supply/battery/voltage_now 2>/dev/null)
# Threshold in microvolts — 3.3V is the danger zone for most LiPo cells
THRESHOLD=3300000
if [ -n "$VOLTAGE" ] && [ "$VOLTAGE" -lt "$THRESHOLD" ]; then
logger "Battery critical ($VOLTAGE uV), initiating shutdown"
# Sync filesystem before power dies — SD card corruption is the real enemy
sync
shutdown -h now
fi
Run that via cron every minute. The real killer isn't the downtime — it's SD card corruption from unclean shutdowns. A read-only filesystem helps enormously here:
# Mount root filesystem as read-only
# Add 'ro' to your /etc/fstab root entry, then:
sudo mount -o remount,ro /
# Use tmpfs for anything that needs writes
tmpfs /var/log tmpfs defaults,noatime,nosuid,size=5m 0 0
tmpfs /tmp tmpfs defaults,noatime,nosuid,size=5m 0 0
A read-only root filesystem also means you can survive sudden power loss without corruption. The Pi just boots right back up when the sun returns.
Step 5: Get It on the Internet
Your Pi probably doesn't have a static IP or a port-forwarded router. Options:
-
Cloudflare Tunnel (formerly Argo Tunnel) — the
cloudflareddaemon is lightweight and gives you HTTPS for free. Memory cost is around 10-15MB though, which is significant at this scale. - WireGuard to a cheap VPS that reverse-proxies to your Pi. WireGuard's kernel module uses almost no userspace memory.
- Yggdrasil or Tailscale for overlay networking if you're okay with non-traditional access.
The Cloudflare Tunnel approach is the simplest. WireGuard + a $3/month VPS running Nginx as a reverse proxy gives you the most control with the least RAM impact on the Pi itself.
What 27MB Actually Looks Like
Here's a realistic memory breakdown on a stripped-down Pi Zero:
| Component | RAM Usage |
|---|---|
| Linux kernel | ~8MB |
| systemd (minimal) | ~5MB |
| Network stack + WiFi | ~6MB |
| darkhttpd | ~0.3MB |
| cloudflared | ~12MB |
| Headroom | ~0.7MB |
| Total | ~32MB |
Drop cloudflared in favor of WireGuard (kernel module, negligible userspace cost) and you're comfortably under 27MB. Replace systemd with something like s6 or plain old SysVinit and you shave off another 2-3MB.
Prevention Tips: Don't Learn This the Hard Way
-
Use
overlayfsor a read-only root. I cannot stress this enough. SD card corruption will ruin your weekend. -
Monitor with
free -hobsessively during testing. Memory leaks that are invisible on a 16GB laptop will crash your server in hours. - Set up a watchdog timer. The Pi has a hardware watchdog — use it. If your server hangs, it auto-reboots.
- Test your shutdown script by actually yanking power. Simulate the worst case before the worst case finds you.
- Log to tmpfs, not the SD card. Rotate or discard logs on reboot. If you need persistent logs, write them to a USB stick.
Is This Practical?
Honestly? For a personal site, a status page, or a sensor dashboard — absolutely. You're not going to serve a SaaS app this way, but that's not the point.
The point is understanding what your software actually needs versus what it lazily consumes. After going through this exercise, I started looking at every docker stats output differently. That 200MB container serving a landing page? Suddenly feels a little embarrassing.
Sometimes the best way to understand the ceiling is to start from the floor.
Top comments (0)