My home setup has two machines with deliberately different jobs.
kalc-server is the flexible one. It runs applications, containers, databases, AI tools, automations, and the control plane I use for development. kalc-nas is the conservative one. It stores the data, provides snapshots, and feeds a separate offline backup.
That split sounds obvious now. It was not how the setup began. Like many homelabs, mine grew one useful service at a time until it became infrastructure I actually depend on—for files, photos, projects, workouts, finances, media, and AI-assisted work.
This is a snapshot from 19 August 2026. It is an inventory and a design explanation, not a claim that the system is finished or perfectly secure. I have intentionally omitted addresses, ports, account names, private routes, detailed firewall rules, and exact software versions that would create risk or age immediately.
The short version
kalc-server | kalc-nas | |
|---|---|---|
| Hardware | EliteMini-series mini PC | Synology DS723+ |
| Main role | Compute, applications, automation, and control plane | Durable storage, snapshots, and backup staging |
| Operating system | Ubuntu 26.04 LTS | Synology DSM 7.3 |
| Processor | AMD Ryzen 9 PRO 8945HS, 8 cores / 16 threads | AMD Ryzen R1600 |
| Memory | 32 GB DDR5 | 2 GB DDR4 ECC |
| Primary storage | 2 TB NVMe SSD | Two 4 TB disks in RAID1, Btrfs, plus a 500 GB NVMe device |
| Workload style | Docker, Podman, and systemd services | SMB storage and native Synology services |
The NAS currently has about 3.5 TiB of usable space. At this snapshot it was 73% full, with just under 1 TiB available. Both disks were present in a clean mirror.
The architecture
phones, laptops, browsers and TVs
|
HTTPS or private access
|
Caddy on kalc-server
|
applications and databases
|
SMB + encrypted backups
|
kalc-nas
Btrfs + mirrored disks
|
encrypted offline USB backup
The central rule is simple:
The server is the compute layer. The NAS is the durable storage layer.
Application code, databases, indexes, queues, and other latency-sensitive state normally live on the server's NVMe drive. Large and durable files live on the NAS. The server mounts only the storage it needs.
I avoid putting live databases directly on SMB. A database expects filesystem behavior and latency that a network share may not provide. Instead, the application runs locally, then produces an application-consistent dump for backup. This also gives me a much clearer restore path than copying a live database directory and hoping it is usable.
kalc-server: the application host
The server is an AMD mini PC with a Ryzen 9 PRO 8945HS, integrated Radeon graphics, 32 GB of memory, and a 2 TB Samsung NVMe drive. It runs Ubuntu 26.04 LTS.
It does not have a discrete GPU, and I do not pretend that an integrated GPU is a large-model inference machine. Lightweight embedding and utility models can run locally, while larger Qwen inference is delegated to a separate GPU host through an authenticated private connection. That keeps the application server focused on orchestration, storage, APIs, and user-facing workloads.
At the time of the audit, the server was running around 100 containers across Docker and Podman. That dramatic number needs context: many are PostgreSQL, Redis, vector databases, queues, workers, or one component of a larger application. It is still more operational surface than I would recommend starting with.
Core infrastructure
- Caddy is the web entry point and reverse proxy. Most application backends bind locally instead of exposing their container ports directly.
- Tailscale provides private remote access governed by a central policy.
- Cloudflare Tunnel handles ingress for a small number of deliberately public applications.
- AdGuard Home provides network-level DNS filtering.
- Pocket ID handles sign-in for some applications, while Bitwarden Lite is my self-hosted password vault.
- Forgejo hosts Git repositories and development workflows.
AI and agent tooling
This is the part of the setup that changes fastest.
- LibreChat is my private AI interface, with retrieval, file search, tool discovery, and isolated code execution.
- T3 Code, OpenCode, Codex, and Hermes give me several ways to run and supervise coding or general-purpose agents.
- Qdrant, pgvector, Meilisearch, and a custom knowledge-base service support retrieval and durable context.
- IBM MCP Context Forge provides a gateway for Model Context Protocol tools.
- My MCP services cover knowledge, Git hosting, task management, shopping lists, and web research.
- A self-hosted research stack combines SearXNG, Firecrawl, browser rendering, queues, and storage so an agent can search and extract information through a controlled layer.
- Ollama handles smaller local utility and embedding models; large generation remains separate from this host.
The interesting part is not any single model. It is the boundary around the model: what context it receives, which tools it may call, which actions need approval, what is logged, and how I verify that a task really finished.
Personal cloud and photos
- Nextcloud AIO is my private cloud for files, contacts, calendar, sync, and related workflows.
- Immich is the main photo and video experience.
Both applications run on kalc-server, but their large file data ultimately depends on NAS storage. Their databases and application state remain on local NVMe. That division gives the applications fast local state without turning the server's internal disk into the only copy of my files.
One rule matters here: I do not casually write files directly into Nextcloud's managed storage. Files should normally enter through Nextcloud so its index, versions, trash, and metadata stay coherent.
Media and reading
- Jellyfin for the media library and playback
- Navidrome for music
- Calibre-Web Automated and LazyLibrarian for ebooks and device delivery
- Seerr, Sonarr, Radarr, Lidarr, and Prowlarr for lawful media discovery and library automation
- Transmission, isolated behind a VPN, for permitted downloads
The applications and metadata live on the server; the bulk libraries live on the NAS.
Daily tools and personal applications
- Vikunja for goals, projects, and tasks
- Anki sync and web tooling for study
- Koffan for a shared shopping list
- Actual Budget for cash flow and budgeting
- Ghostfolio for investment tracking
- wger, FreeReps, and a health-data ingestion service for training and fitness
- Wardrowbe for wardrobe inventory and outfit recommendations
- Timeo worker services for scheduling and document parsing
- Spiaggia Oggi, a source-aware Italian beach information product
This category is why self-hosting works for me. These are not merely weekend demos. Several sit inside ordinary routines, so backups, upgrades, and failure states matter.
Operations
- Homepage is the service launchpad.
- Uptime Kuma checks endpoints.
- Beszel collects host and container metrics.
- ntfy delivers private push notifications.
- DIUN watches container image updates.
- Smartmontools, security audits, vulnerability scans, and scheduled backup checks cover some of the less glamorous work.
The server also has recurring application-specific backups for services whose databases or state need special handling.
kalc-nas: boring on purpose
The NAS is a Synology DS723+ running DSM 7.3. It has two 4 TB disks mirrored with RAID1 and a Btrfs volume. A roughly 500 GB NVMe device is also present; an earlier verified inventory identifies it as a read-only cache, although I did not re-confirm the current cache mode during this audit.
Its job is intentionally narrow:
- provide SMB storage to the server;
- hold durable file, photo, and media data;
- provide Btrfs recovery points—the daily snapshot schedule was last verified in July rather than during this audit;
- run Hyper Backup to a separate encrypted disk;
- monitor the UPS, with shutdown behavior kept as something to test rather than assume;
- keep a few Synology utilities available as fallbacks.
DSM has many packages installed, including Synology Drive, Photos, Cloud Sync, Calendar, Contacts, Container Manager, Tailscale, and Web Station. I am deliberately not presenting every installed package as a currently used service. "Installed," "running," and "part of my current workflow" are three different states.
No containers were running on the NAS during the audit. For my workload, keeping containers off the NAS makes the storage layer easier to reason about; the DS723+ remains storage-first.
Backups: four different jobs
I used to think about "the backup" as if it were one object. It is more useful to separate four failure modes.
1. RAID1 keeps storage available
The two NAS disks are mirrored. If one disk fails, the array is designed to remain available while I replace it.
RAID is not backup. It does not protect me from accidental deletion, ransomware, application bugs, or mistakes faithfully copied to both disks.
2. Btrfs snapshots help undo mistakes
Snapshots provide fast recovery from some accidental changes without waiting for an external restore. They are another local recovery layer, not an independent copy of the machine.
3. kalc-server creates application-consistent backups
The server creates database dumps and other service-aware backup inputs, then sends selected state into an encrypted Restic repository. Scheduled jobs also perform repository maintenance, subset integrity checks, and automated restore tests.
At the time of writing, the main backup, integrity-check, and restore-test services all reported success. That matters more than merely seeing that a timer exists.
4. The NAS is copied to an offline disk
An encrypted, versioned Hyper Backup goes to a separate 12 TB USB disk. The latest verified run completed successfully on 11 August 2026. The disk was then safely ejected and was still disconnected during this audit.
An unplugged backup is less convenient and more useful against some classes of failure. I verify both completion and ejection; a successful "start backup" command proves neither.
This is a good recovery stack, but I would not call it a complete 3-2-1 strategy until I have independently verified an off-site copy. Offline and off-site are not synonyms.
Power and network resilience
The NAS, server, and their local network path are powered through a CyberPower UPS. Live monitoring on the NAS showed the UPS online with a full battery during the audit.
That does not make the setup highly available. It gives the machines time to ride through short interruptions or shut down cleanly. Home internet, one physical location, one primary server, and one NAS are still obvious shared failure domains.
Security choices—and an honest caveat
My default pattern is to bind application services locally, put Caddy in front of them, and use private access unless a service has a reason to be public. The host firewall defaults to denying inbound traffic. SSH uses keys rather than passwords, and direct root login is disabled. Secrets live outside Git.
Backups, automatic updates, audit logging, vulnerability scanning, and recurring security checks exist. They are controls, not proof of security.
The audit also produced findings that remain in my private remediation queue. That is the point of these checks: they generate work rather than certify that the system is secure. The user-facing containers that declare health checks were not unhealthy, and the backup/restore checks were successful, but I am not going to translate "the apps respond" into "the server is fully hardened."
The broader lesson is worth publishing: a monitoring system that stays green by hiding unresolved work is worse than a dashboard that makes you uncomfortable.
What I would improve next
Reduce operational sprawl
Around 100 containers is a sign that experiments, production services, databases, and QA helpers need a clearer lifecycle. I want a routine that identifies workloads which are running only because nobody stopped them.
Add memory and plan storage capacity
The NAS still has its small stock memory configuration, while the volume is already 73% full. It works, but neither number leaves much room for careless growth. Capacity planning should happen before the array crosses the uncomfortable threshold, not after.
Make off-site recovery explicit
The offline disk protects against several important scenarios. A documented and tested off-site copy would cover another set of them.
Standardize deployment
Docker, rootless Podman, rootful Podman, system services, and user services all have legitimate uses here. Together they also make discovery, upgrades, and backup coverage harder. New services should have to justify a new deployment pattern.
Publish less infrastructure detail, not more
Documentation is valuable, but a public post should explain decisions rather than publish an attacker's map. I keep a more exact private inventory with routes, ports, restore commands, and credentials locations. This article is intentionally not that document.
What this setup taught me
- Separate compute from durable storage. The two machines can evolve at different speeds.
- Keep databases local and back them up through application-aware exports. A copied directory is not automatically a recovery plan.
- RAID, snapshots, encrypted backups, and offline copies solve different problems. None replaces the others.
- Private by default is easier than securing accidental exposure later. Public ingress should be a product decision.
- Installed does not mean used, and running does not mean needed. Inventory needs lifecycle, not just discovery.
- A successful health endpoint is only one signal. Backups, restore tests, security findings, capacity, and user-visible behavior tell different parts of the story.
My homelab is not minimal, and it is not a template I would copy service for service. The part I would reuse is the division of responsibility: a replaceable compute host, a deliberately boring storage appliance, and recovery layers that are tested instead of assumed.
Sources and further reading
- Synology DS723+ data sheet
- Synology Hyper Backup documentation
- Synology guide to backing up a NAS
- Restic documentation
- Caddy HTTPS documentation
- Nextcloud documentation
- Immich documentation
- Tailscale documentation
AI disclosure: I used AI agents to perform parallel read-only inventories of both machines, reconcile live state with my private operational notes, challenge stale claims, and edit this draft. I reviewed the findings and remain responsible for what is published.