How I Built a Home Server That's Always On
A 12-year-old Sony Vaio. One working RAM stick. A dead screen. No battery.
This is the server running my production backend.
I didn't set out to build infrastructure. I was trying to host a side project without paying for a cloud server. The Vaio was in the cupboard gathering dust. I figured: how hard could it be?
Phase 1: Getting It to Boot
The first challenge was the hardware itself. The screen showed nothing — total black. The flashlight test ruled out a dead backlight; the panel itself had failed. I hooked it up to the TV via HDMI just to see what I was doing.
Getting it to boot was its own puzzle. I swapped RAM sticks around until the HDD light finally flickered. Something was alive.
The Windows backup process was where the display gave up entirely. I transferred 7GB of old data over USB 2.0 — four hours staring at a progress bar. Then I wiped the drive and installed Ubuntu Server.
The display came back to life after Linux was installed. Make of that what you will.
Then the ISO wouldn't boot. Modern Ubuntu Server ISOs don't play nice with legacy BIOS. The USB would just give a blinking cursor. The fix: re-flash the drive with Rufus in DD Image Mode — writing raw binary instead of the default ISO method. That finally worked.
Phase 2: The Rate-Limit Wall
The first working deployment was a Node.js football tactical board, managed with PM2. I could SSH into it from across the house. That felt like enough.
Then the StockStash backend got rate-limited.
StockStash is an investment tracker with a Python backend that pulls from Yahoo Finance. The backend was hosted on Render. Render's shared IPs are well-known to commercial APIs — Yahoo Finance was blocking requests. Moving it to a residential IP fixed the problem entirely.
This is when the Vaio became a real server.
The Stack
The current setup runs two services:
- Node.js (football tactical board) — managed by PM2 - tacticalboard.sarvaeshhh.com
- Python (StockStash backend) — containerized with Docker - stockstashapi.sarvaeshhh.com
PM2 handles process management and auto-restart for the Node app. Docker isolates the Python backend and lets me enforce guardrails I wouldn't otherwise bother with on a home machine.
Two guardrails worth calling out. First, Docker log rotation — the Vaio has a 12-year-old hard drive, and unbounded logs will fill it:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
Second, Firebase credentials mounted as read-only. If a container is ever compromised, it can read the credentials but can't modify or delete them:
volumes:
- ./firebase-credentials.json:/app/credentials.json:ro
Secrets go to the server via SCP. Nothing sensitive lives in the repo.
Cloudflare Tunnels Instead of Port Forwarding
I didn't want to open ports on my router. Port forwarding is fine, but it exposes your home IP and requires router config I'd rather not touch. Cloudflare Zero Trust tunnels solve this cleanly: cloudflared runs as a daemon on the server, establishes an outbound connection to Cloudflare's network, and your service is reachable at a subdomain you control. No inbound ports.
Setup is mostly through the Cloudflare Zero Trust dashboard. Once you configure the tunnel, you install the daemon:
sudo cloudflared service install
From that point, the tunnel comes up automatically on every boot.
CI/CD from the Living Room
The server runs a self-hosted GitHub Actions runner. I push code from my laptop; the Vaio pulls, builds, and deploys automatically. A Personal Access Token embedded in the Ubuntu Git config means the daemon never stalls waiting for authentication.
One problem early on: GitHub Actions' shared queue introduced unpredictable delays for scheduled jobs. The StockStash backend sends emails at 9:30 AM — queue lag made that unreliable. I replaced the scheduled workflow with a local Linux cronjob that pings the container directly:
30 9 * * * curl -s http://localhost:8000/send-email
Fires exactly on time, every time.
Making It Resilient
The Vaio's BIOS is locked — no Wake-on-AC option. If power cuts, the server stays off until I press the button. My workaround: make recovery manual but fast.
I hardcoded a static IP via Netplan so the address is always predictable:
network:
version: 2
ethernets:
eth0:
dhcp4: no
addresses: [192.168.1.150]
gateway4: 192.168.1.1
nameservers:
addresses: [1.1.1.1, 8.8.8.8]
Every daemon — PM2, Docker, cloudflared — is set to auto-start on boot. After a power cut: press the power button once, and everything is back in under 60 seconds.
What This Actually Is
The Vaio runs two production services, a CI/CD pipeline, and a secure public tunnel — on hardware that cost nothing and draws maybe 15 watts. It's not impressive infrastructure by any enterprise standard. But it's mine, it's real, and it's taught me more about how systems actually work than any managed service ever did.
If you have an old laptop in a cupboard: give it a new brain.