HomeLab

i turned an older laptop into a private, always-on platform for hosting personal applications, observing how they behave and practicing the operational work that happens after software is written.

LinuxDocker ComposeNext.jsFastAPISQLiteTailscaleResticCloudflare R2

SYSTEM DESIGN

overview of the architecture

01 / client

Authorised devices

Mac and phone connected through the private Tailscale network.

02 / edge

Tailscale Serve

Private HTTPS terminates before traffic reaches a loopback-only dashboard.

03 / interface

Next.js dashboard

Renders system state and application links without exposing backend credentials.

04 / metrics

FastAPI monitoring

Collects current readings and serves bounded, validated history.

05 / storage

SQLite + named volumes

Persists monitoring history and keeps application state outside containers.

06 / applications

Separate private origins

CardScan and Open WebUI retain their own authentication and data boundaries.

why did i build this?

I'm a sentimental person and I don't like throwing things away, so I decided to repurpose my old laptop that I used as a kid to play games on. My personal applications originally stopped if either npm start wasn't on, or if my Mac was closed. I wanted a place where they could run independently, while learning how to deploy changes.

01 - laptop's first life
my laptop running Minecraft before it became a HomeLab server
me playing minecraft on my laptop (december 2018).
02 - a new life
A handwritten do not touch or unplug sign attached to the HomeLab ThinkPad
the same laptop in its new state, with a reminder (october 2026)

what i built

01 - Tailscale Serve

Private access layer

Responsibility: Terminates private HTTPS and forwards authorised requests to the dashboard's loopback-only port.

How it works: An authorised device resolves the private hostname, completes TLS with Tailscale Serve and is forwarded to the dashboard on the T460's loopback interface.

02 - Next.js

Dashboard

Responsibility: Renders current readings, historical charts and links to hosted applications for the browser.

How it works: Each server-rendered request calls the monitoring API through internal Docker DNS, translates its current and historical responses and returns a browser-safe view.

03 - FastAPI

Monitoring service

Responsibility: Samples available system and temperature readings every 30 seconds, validates them and serves current and historical API responses.

How it works: A leased periodic sampler collects one reading at a time, persists valid samples and exposes separate current, history, liveness and storage-readiness endpoints.

04 - SQLite

Metrics store

Responsibility: Retains seven days of timestamped samples in a persistent Docker volume using a single writer.

How it works: The collector commits samples to a WAL database, applies bounded retention and reads indexed time windows while the named volume survives container recreation.

05 - Docker Compose

Service runtime

Responsibility: Defines builds, health checks, resource limits, persistent volumes and the networks connecting each service.

How it works: Compose builds and starts the reviewed service definitions, waits on health contracts and connects only the services that need to communicate.

06 - restic + Cloudflare R2

Recovery pipeline

Responsibility: Creates application-consistent snapshots, encrypts them and stores an off-device copy.

How it works: Scheduled scripts capture consistent application state, restic encrypts and uploads it, and recovery rehearsals restore a copy into an isolated environment for verification.

the dashboard

03 - resource monitoring
HomeLab resource cards showing CPU, memory, filesystem, uptime, load and network readings
04 - resource use over time
HomeLab history charts for CPU, memory, disk usage and CPU temperature

engineering decisions

Docker Compose instead of host installed services

I used Docker Compose because this is one server with a small number of related services. Docker gives me repeatable builds, health checks, resource limits and isolated networks without the operational cost of Kubernetes. The tradeoff is that container visible metrics are not automatically whole host metrics, so every reading needs an explicit scope.

SQLite instead of a separate database server

I chose SQLite for monitoring history because one sampler owns the writes and the dataset is small and bounded. This avoids running PostgreSQL solely for telemetry on a memory-constrained laptop. SQLite's single-writer model is an accepted limit; a workload needing concurrent writers would justify a different database.

Tailscale instead of public ingress

I kept the dashboard on loopback and used Tailscale Serve for private HTTPS access. This lets my authorised devices reach the server without opening a router port or exposing an unauthenticated monitoring API to the internet.

A server side boundary for monitoring

The browser talks only to the Next.js dashboard; the dashboard calls FastAPI over an internal Docker network. This keeps the monitoring address and storage private, and separates presentation from collection. It adds one hop, but prevents the browser from becoming a trusted infrastructure client.

Restore tested off-device backups

I used restic with private object storage because a local copy on the same laptop would fail with the laptop's disk. Backups are encrypted before upload and restored into an isolated environment for verification. This costs more operational work, but it proves recovery instead of treating a successful upload as evidence that the data is usable.

current state

30 sec

monitoring interval

7 days

metric history

3

private web interfaces

03:30

daily backup time

The dashboard, monitoring service and hosted applications run independently of my Mac. Hardware temperatures and historical metrics are visible from authorised devices, while daily encrypted backups cover the application state currently included in the recovery contract.

This is still a personal platform: the T460 remains one physical machine, remote retention is intentionally conservative and some future workloads have not been approved or deployed yet.

what's nextlast edited ·

  • Improve backup and recovery, and set alert thresholds that account for the laptop's available resources.

    date TBC
  • Add a private media server for my favourite shows and movies.

    date TBC
  • Host a persistent Minecraft server and experiment with agent-controlled characters.

    date TBC
  • Buy a 2TB external SSD for the inevitable death of the T460.

    date TBC