All work

Home Server: A Private Cloud from an Old PC

An old desktop PC, cleaned and rebuilt into a Debian home server that runs my own cloud, office suite, mail, private DNS and an automated media library: 21 containers, every app private to my own devices over a VPN.

Started
April 2026
Timeline
Ongoing since April 2026
Complexity
High
Type
Infrastructure, DevOps, Security
The screenshots come from the live server. Addresses, account names and anything personal are left out or blurred on purpose: the first rule of running a server is not to publish a map of it.

An old desktop PC was gathering dust in a corner. I took it apart, cleaned it piece by piece, checked that every part still worked, and rebuilt it as a home server running Debian 13, with no desktop environment: only a terminal. Today it runs 21 containers and a few native services: my own cloud storage and office suite, a mail server, private DNS with network-wide ad blocking, shared drives for every computer in the house, and a media library that runs itself. I reach all of it from my laptop or my phone, from anywhere, and apart from incoming mail none of it is open to the internet.

It began as a hobby and became the most complete systems course I have taken: Linux administration, containers, networking, DNS and certificates, security hardening and, above all, what it really means to be responsible for a machine that has to keep running.

At a glance

  • 21 containers and 4 native services on a 2014 Core i5 with 8 GB of DDR3 and a 500 GB hard drive.
  • Private by default: the web apps are served over a WireGuard VPN, through one reverse proxy with real HTTPS. The proxy refuses devices that are only on the home Wi-Fi.
  • 35 GB of photos, documents and media moved off my phones and laptops into my own cloud, with automatic photo backup from the phone.
  • 18 web apps under private names, covered by one wildcard certificate that renews itself.
  • A free alternative to Microsoft 365: documents, spreadsheets, slides and PDFs edited and shared in the browser.
  • A hands-free media library: a request from a phone ends as a subtitled file streaming on any screen.
  • Hardened with an AI assistant that audited the setup through its own account, with every change backed up first and nothing deleted without my approval.

Why I built it

Two questions had followed me since I was twelve, assembling gaming PCs and learning what every part does: how many services can one machine run, and how many people can it serve? Running a server of my own answered both, and the answers were not where I expected them. The CPU and the RAM were rarely the limit. The disk, the network and the power were.

There was a practical reason too. My files were spread across a laptop, an old Android phone, an iPhone and free cloud accounts that kept running out of space. I wanted one place that I control, with no subscription and no storage cap except the size of the disks.

The machine

The server is an ASUS desktop from around 2014: an Intel Core i5-4460S with 4 cores, 8 GB of DDR3, a 500 GB 7,200 rpm hard drive and a gigabit network card, wired to the router with Ethernet. After cleaning it and checking every component, I wiped it and installed Debian 13 as a minimal server. Everything since then was done from the command line: users, groups and permissions, processes and services with systemd, packages, networking, shell scripts, and troubleshooting from the logs.

Architecture

Home server architecture: my devices reach the server through a Tailscale VPN, AdGuard Home answers private names, Nginx Proxy Manager terminates HTTPS and checks an allow-list, and Docker stacks for cloud and office, mail, media automation, streaming, operations and shared storage sit on an internal network, with the Debian host, Cloudflare, the mail edge and remote power-on underneath

Every request takes the same path. A device on the VPN asks for an app by its private name. The VPN’s DNS setting sends the question to AdGuard Home on the server, which answers with the server’s private VPN address. The request then travels inside the encrypted tunnel to Nginx Proxy Manager, which presents a valid HTTPS certificate, checks that the caller is on the VPN, and forwards the request over an internal Docker network to the right container. The proxy is the single front door: the apps behind it need no ports of their own.

Foundations first

Before any application, I built the base that everything else stands on, in an order that kept the server safe at every step.

Remote access and a dashboard

First came SSH and the basic network setup, so the server could live without a screen or a keyboard. Then Cockpit: a web console with CPU, memory, disk and network graphs, system logs, services, updates and a terminal in the browser. It is still the first thing I open when something feels slow.

Cockpit’s metrics page: CPU, memory, disk and network usage right now, the services using the most, and a timeline of the day’s spikes

Tailscale: the front door

The classic way to reach a home server from outside is port forwarding: telling the router to send a port from the internet to the server. It works, and it puts that service in front of the whole internet, where automated scanners find a newly opened port within minutes and every flaw in the app becomes public-facing. I wanted the opposite.

Tailscale builds an encrypted WireGuard mesh between my own devices. My laptop, PC and phones reach the server as if they were on the same network, from home, from work or over mobile data, and only the devices I approve can join. It needs no port opened on the router, the home connection’s changing IP address does not matter, and a lost device is removed in one click. It came first because every service after it depends on being reachable safely.

Docker and Portainer: a clean host

Every application runs in a Docker container, described in a Compose stack and deployed through Portainer. The host stays a clean Debian install: no competing PHP or database versions, no packages left behind by an app I tried once, no dependency conflicts between services. Data lives in mapped folders and volumes, apart from the containers, so an app can be upgraded, rebuilt or removed without touching its data or anything else on the system.

Portainer is the control room: stack deployments, container logs and resource usage, internal networks and volumes, all in one place. Every image is pinned to an exact version, so nothing changes until I decide to update, and each update is a deliberate step with a backup first.

Portainer’s stack list: the nine Docker Compose stacks that run the server’s services

The services

Every service has a tile on one start page, with live CPU, memory and disk figures at the top.

The server’s start page: every service grouped under Infrastructure, Media, Downloads and Productivity, with live CPU, memory and disk figures at the top

A reverse proxy and real certificates

Nginx Proxy Manager is the receptionist: it receives every web request, proves the server’s identity with a certificate, decides who may enter, and sends each visitor to the right app. Building it taught me how TLS really works: certificate authorities and the chain of trust, why a browser warns about a self-signed certificate, how HTTP/2 and websockets pass through a proxy, and which headers carry the real client’s address.

Because the server is private, Let’s Encrypt cannot reach it to verify the domain the usual way. So I used the DNS-01 challenge instead: the proxy proves ownership by creating a temporary DNS record through Cloudflare’s API. One wildcard certificate now covers every app, renews itself, and keeps the individual app names out of the public certificate-transparency logs.

Nginx Proxy Manager’s proxy hosts, with names and targets blurred: every host has a Let’s Encrypt certificate and the VPN-only access list, and all are online

Private DNS and network-wide ad blocking

AdGuard Home is the server’s phone book. It answers the private app names that public DNS knows nothing about, and it blocks ads, trackers and malicious domains for every device that uses it, phones included, with nothing to install on them. Because the VPN hands it out as the DNS server, the protection follows my devices wherever they are.

AdGuard Home’s dashboard: about 8,900 DNS queries in a day, 19% of them blocked by filters, with client addresses blurred

Shared drives: a NAS for the house

Samba turns the server’s disk into network drives that every PC opens like a local folder: a shared folder for everyone and a media folder for the library. It is the simplest service on the server, and the one that made it feel like part of the house.

My own cloud and office suite

Nextcloud replaced the free tiers of commercial cloud storage. 35 GB of photos, documents and media from every device I own now live on hardware I control, and my phone uploads new photos on its own. The only storage limit is the disk.

Paired with the OnlyOffice document server, Nextcloud edits Word, Excel and PowerPoint files in the browser, lets several people work on the same document at once, and, the part I find most useful, edits PDFs for free. Together they are a self-hosted alternative to a paid Microsoft 365 plan, reachable from any of my devices. Making the two talk to each other, the editor fetching a file and calling back to save it, taught me the difference between the addresses a browser uses and the ones containers use inside Docker.

Mail on my own domain

A complete mail server (docker-mailserver) with a Roundcube webmail serves addresses on my own domain. Mail is the hardest service to self-host: home connections often sit on spam blocklists and many providers block outgoing mail traffic, so my outgoing mail goes through a reputable relay, while SPF and DMARC records tell other servers which machines may send for the domain. Cloudflare manages the domain’s DNS, and a small container keeps the records up to date whenever the home connection’s public address changes.

The fun part: a media library that runs itself

One stack of eleven containers, plus Jellyfin on the host, works as a single pipeline. I request a film or a series from my phone in Seerr; Sonarr (series) and Radarr (films) search for it through Prowlarr’s indexers and hand it to qBittorrent; Bazarr fetches the subtitles; Tdarr re-encodes files to save space; and Jellyfin streams the finished library to any screen, next to Navidrome for music and Audiobookshelf for audiobooks and podcasts. No step is manual.

It works better than I expected, and it taught me more about service-to-service APIs, internal networking, storage layout and Linux file permissions than any tutorial: each app talks to the others by container name over a private Docker network, never through the outside.

Security, seen from the attacker’s side

I hold a certificate in ethical hacking, and I audited the server the way an attacker would: what is listening, on which interface, who can reach it, and what each part trusts. The answers shaped the design.

  • No port forwarding for apps. Apart from incoming mail, nothing on the server answers the internet. Remote access goes through the VPN.
  • The Wi-Fi is not a pass. The proxy refuses devices that are merely on the home Wi-Fi: being inside the house is not the same as being trusted.
  • One front door. Sixteen container ports that had been published to the whole network were closed, moving those apps behind the proxy, with an SSH tunnel as the emergency way in if the proxy itself is down.
  • Docker and the firewall. Docker writes its own firewall rules, so a published container port can slip past the host’s firewall rules. The reliable fix is not to publish the port at all.
  • Every login checked. I went through the apps one by one; one that held other services’ API keys had no login, and got one.
  • Versions pinned, backups first. Every image is pinned to an exact version, and every migration starts with a configuration backup and a full database dump.

Working with an AI assistant

Later on, I gave Claude, Anthropic’s AI assistant, its own account and SSH key on the server. It audited the setup end to end and found what I had missed: container ports open to the whole network, an app holding other services’ API keys without a login, an office connector silently disabled by an upgrade, a media app crashing on a one-line configuration error. It explained every finding, proposed the fix and applied it with me.

The rules were strict. It never typed a password: I signed in to each dashboard myself and it worked in that session. Stack changes ran as dry runs first, configuration was backed up before every change, and nothing was deleted without my approval. It also left me a written guide for every procedure, so I can redo any of it myself. It turned a mountain of documentation into a guided course in hardening.

What the hardware and the network taught me

A network is a chain

Data moves at the speed of the slowest link in the chain: the network card, the cable, the switch or router port, the Wi-Fi link and the internet connection. My own server proves it. Its network card is gigabit, but the router’s ports only offer 100 Mb/s, so 100 Mb/s is the ceiling for everything it does on the network, whatever the CPU could handle. For wired devices inside the house, a gigabit switch would lift that ceiling more than any CPU upgrade could.

Three numbers are easy to confuse. Link speed is what two ports negotiate: 10, 100 or 1,000 Mb/s, then 2.5 and 10 Gb/s. Bandwidth is the capacity of a link. Throughput is what actually gets through after protocol overhead, congestion and interference, and it is the only one users feel. Ethernet beats Wi-Fi for a server for the same reason: a cable gives a stable, full-duplex link, while Wi-Fi shares the air with every other device and every wall in the way.

Upload matters more than download

Home connections are sold on download speed, but a server lives on its upload: every file I open remotely, every film streamed and every page served leaves the house through the upload link, often several times narrower than the download. It is also why cloud providers charge for egress (data leaving their network) and rarely for ingress (data coming in): outbound bandwidth is the scarce, expensive direction.

Why businesses pay for enterprise network gear

A free router from the internet provider is built for a family’s browsing, not for hundreds of sessions, separated networks and guaranteed uptime. Running my own server showed me what each piece of a professional network is for:

  • Enterprise routers and SD-WAN: several internet links with automatic failover, traffic prioritised by application, and one policy managed centrally across every site.
  • Managed and Layer 3 switches: VLANs and subnets that keep staff, guests, cameras and servers apart, routing between them at full speed, link aggregation and per-port monitoring.
  • Next-generation firewalls (NGFW): inspection up to the application layer, intrusion prevention, and rules based on users and applications rather than only ports and IP addresses.
  • Wireless access points: many clients per access point, seamless roaming between them, and guest networks that cannot see internal systems.
  • Network access control (NAC, 802.1X): a device must prove who it is before a switch port or a Wi-Fi network lets it in at all.
  • Power and supporting infrastructure: UPS units, redundant power supplies, racks, cooling and structured cabling, because none of the rest works without them.

My setup does not need most of this yet: subnetting, guest networks and NAC pay off at scale, which is why they cost what they cost. But VPN-only access and the allow-list on the proxy apply the same idea at home: trust comes from identity, not from being plugged in.

Power is the real enemy

In Tunisia, summer heat brings power cuts and voltage drops. A sudden cut is the worst thing that can happen to a server: writes interrupted half-way can corrupt a database or a file system, a spinning hard drive suffers from repeated hard power losses, and surges can kill a power supply. A UPS (uninterruptible power supply) rides through short cuts, smooths spikes and sags, and gives the server time to shut down cleanly during a long one. It is the next piece of hardware on my list.

Until then, a connected smart plug lets me power the server back on from anywhere after an outage, and from the server I can wake any other computer on the network with Wake-on-LAN, a “magic packet” addressed to its network card.

The OSI model, for real

Networking theory stopped being abstract once every layer had a job on my own server:

  • Layer 1, physical: cables, ports and negotiated link speeds; the bottleneck chain.
  • Layer 2, data link: Ethernet frames and MAC addresses; Wake-on-LAN’s magic packet is addressed to a MAC address.
  • Layer 3, network: IP addresses, private ranges, subnets and NAT; the WireGuard tunnel encrypts traffic here.
  • Layer 4, transport: TCP and UDP ports, the doors that firewalls open and close.
  • Layers 5 to 7: TLS encryption on top of TCP, then the protocols people actually use: HTTP, DNS, SMTP and IMAP for mail, SMB for file sharing.

Firewalls work at several of these layers too: packet filters at layers 3 and 4, stateful firewalls that track whole connections, and application-layer filtering, from a reverse proxy blocking common exploits to a full NGFW.

The limits I found

I also tried hosting my own websites on the server, and that is where I found its real limits. Not the CPU or the RAM, but the spinning hard drive (slow on many small random reads), the home connection’s upload speed, and power cuts that take a site offline without warning. Each has a fix: an SSD, a faster connection or a CDN in front, and a UPS. Together they explain why public sites belong in a data centre, and why my public projects run in the cloud while the server keeps everything private.

A server is a commitment

The cloud hides the hardest part of running a server. At home, nobody else watches the disk fill up, notices a container restarting in a loop, plans the updates, checks the backups or turns the machine back on after a power cut. Owning a physical server is a responsibility, closer to looking after something alive than to clicking through a console. That, more than any single tool, is what this project taught me.

Tech stack

  • System: Debian 13, systemd, SSH, Bash, Linux users, groups and permissions, Cockpit.
  • Containers: Docker, Docker Compose, Portainer.
  • Networking: Tailscale (WireGuard), Nginx Proxy Manager, AdGuard Home, Docker networks, Cloudflare DNS, dynamic DNS.
  • Security: Let’s Encrypt wildcard certificates (DNS-01), proxy access lists, SSH keys, pinned images, backups before every change.
  • Cloud and office: Nextcloud, MariaDB, OnlyOffice Document Server.
  • Storage: Samba (SMB).
  • Mail: docker-mailserver, Roundcube, SPF, DMARC, an SMTP relay.
  • Media: Jellyfin, Seerr, Sonarr, Radarr, Prowlarr, Bazarr, qBittorrent, FlareSolverr, Tdarr, Navidrome, Audiobookshelf.
  • Dashboards: Homepage, Cockpit, Portainer.
  • Hardware: a smart plug for remote power-on, Wake-on-LAN.
  • AI: Claude Code as an operations and security assistant.

Outcome

Since April 2026, a desktop that was gathering dust has been running my files, documents, mail, DNS and media for every device I own, privately, for the cost of its electricity. Next on the list: a UPS for the summer power cuts, an SSD for the system and the databases, and a self-hosted password manager on the same private network.