Declarative NixOS homelab running on an Intel NUC. Everything is defined in code -- fork this repo, customize, and deploy.
| Service | What It Does | Access |
|---|---|---|
| AdGuard Home | Blocks ads and malware at the DNS level | http://adguard.home.lan |
| Home Assistant | Home automation hub | http://hass.home.lan |
| Caddy | Reverse proxy -- gives services friendly URLs | Automatic |
| Homepage | Dashboard showing all services and system stats | http://home.home.lan |
| CrowdSec | Intrusion detection & prevention (firewall bouncer) | Localhost only (API on 127.0.0.1:8080) |
| Cloudflare Tunnel | Secure remote access via your Cloudflare domain | adguard.yourdomain.com, hass.yourdomain.com |
- Intel NUC (or any x86_64 machine) with 8GB+ RAM
- USB drive (2GB+) for the NixOS installer
- A router where you can change the DNS server setting
- A domain managed by Cloudflare (for remote access)
- A computer to flash the USB and SSH from
Fork this repo and edit hosts/nuc/default.nix:
# Set your NUC's static IP
networking.interfaces.eno1.ipv4.addresses = [{
address = "192.168.1.50"; # <- your IP
prefixLength = 24;
}];
networking.defaultGateway = "192.168.1.1"; # <- your router
# Set your timezone
time.timeZone = "America/New_York"; # <- your timezone
# Add your SSH public key
homelab.sshKeys = [
"ssh-ed25519 AAAA... you@laptop" # <- your key
];
# Your Cloudflare-managed domain
homelab.domain = "yourdomain.com"; # <- your domainIf your NUC uses an NVMe drive instead of SATA, also update hosts/nuc/disk.nix:
device = lib.mkDefault "/dev/nvme0n1"; # default is /dev/sdaBefore installing, create a tunnel so you have the credentials ready:
# Install cloudflared on your laptop
brew install cloudflare/cloudflare/cloudflared # macOS
# or: nix-shell -p cloudflared
# Login to Cloudflare
cloudflared tunnel login
# Create the tunnel
cloudflared tunnel create homelab
# Note the credentials file path (e.g., ~/.cloudflared/<uuid>.json)
# You'll need this during the setup script
# Create DNS records pointing to the tunnel
cloudflared tunnel route dns homelab adguard.yourdomain.com
cloudflared tunnel route dns homelab hass.yourdomain.comDownload the NixOS minimal ISO and flash it to a USB drive:
# macOS
sudo dd if=nixos-minimal-*.iso of=/dev/diskN bs=4M status=progress
# Linux
sudo dd if=nixos-minimal-*.iso of=/dev/sdX bs=4M status=progress- Plug the USB into the NUC and boot from it
- Once booted, connect to your network (Ethernet recommended)
- Find the NUC's IP:
ip addr - From your laptop, SSH in:
ssh nixos@<nuc-ip>(password is empty on the live ISO) - Clone your fork:
sudo su
nix-env -iA nixos.git
git clone https://github.com/YOUR_USER/homelab.git /tmp/homelab
cd /tmp/homelab- Run the setup script:
bash scripts/setup.shThe script will:
- Ask which disk to use and partition it
- Generate hardware config for your specific NUC
- Optionally copy your Cloudflare Tunnel credentials
- Install NixOS
- Remove the USB drive and reboot
After the NUC reboots:
- AdGuard Home: Visit
http://<nuc-ip>:3000and complete the setup wizard (set admin password, configure filters) - Home Assistant: Visit
http://<nuc-ip>:8123and create your account - Router DNS: Set your router's DNS server to the NUC's IP. All devices on your network now get ad blocking.
- Cloudflare Tunnel (if you skipped during setup): Copy the credentials JSON to
/etc/nixos/secrets/cloudflared-tunnel.jsonon the NUC and restart the service
Once AdGuard Home is running, add DNS rewrites so you can use adguard.home.lan instead of IP addresses on your local network:
- Open AdGuard Home at
http://<nuc-ip>:3000 - Go to Filters -> DNS rewrites
- Add a rewrite:
*.home.lan-><nuc-ip>(e.g.,192.168.1.50)
Now you can access locally:
http://adguard.home.lan-> AdGuard Homehttp://hass.home.lan-> Home Assistant
Remotely (via Cloudflare Tunnel):
https://adguard.yourdomain.com-> AdGuard Homehttps://hass.yourdomain.com-> Home Assistant
The homepage dashboard (home.yourdomain.com) is exposed to the internet via Cloudflare Tunnel and shows service status including CrowdSec alerts. It is protected with HTTP basic auth via Caddy.
Run the setup script to generate credentials, deploy, and store them in 1Password:
bash scripts/rotate-dashboard-password.shThis generates a random username and password, hashes it with bcrypt via Caddy on the NUC, updates caddy.nix, deploys, and saves the credentials in 1Password under "Homelab Dashboard (home.danielmschmidt.de)". Run it again anytime to rotate credentials.
Edit the Nix config on your laptop, then deploy:
# Enter the dev environment (gives you colmena, nil, nixpkgs-fmt)
nix develop
# Edit a module
vim modules/adguard.nix
# Deploy to the NUC
./deploy.shIf a deployment breaks something, SSH into the NUC:
sudo nixos-rebuild switch --rollbackOr select a previous generation from the boot menu at startup. NixOS keeps all previous configurations in the bootloader.
- Create a new module:
modules/myservice.nix - Import it in
hosts/nuc/default.nix - Add a Caddy virtual host in
modules/caddy.nix - Optionally add a Cloudflare Tunnel ingress in
modules/cloudflared.nix - Deploy:
./deploy.sh
Tests run as NixOS VMs -- they boot a real (virtual) NixOS system and verify services work.
# Run on a Linux machine or in CI:
nix flake check # all checks
nix build .#checks.x86_64-linux.adguard --print-build-logs # single test
nix build .#checks.x86_64-linux.integration --print-build-logs # full stackCI runs all tests automatically on every push and PR.
This repo contains zero secrets. Sensitive data lives only on the NUC:
| Secret | Location on NUC | How It Gets There |
|---|---|---|
| Cloudflare Tunnel credentials | /etc/nixos/secrets/cloudflared-tunnel.json |
Setup script or manual copy |
| Cloudflare origin cert | /etc/nixos/secrets/cloudflared-cert.pem |
Setup script or manual copy |
| Restic backup password | /etc/nixos/secrets/restic-password |
Auto-generated, save in password manager |
| AdGuard admin password | AdGuard Home's own database | Setup wizard on first boot |
| Home Assistant account | Home Assistant's own database | Setup wizard on first boot |
Restic backs up service data to the USB stick (/mnt/backup) daily at 3am. Retention: 7 daily, 4 weekly, 6 monthly snapshots.
What's backed up:
/var/lib/AdGuardHome— AdGuard Home config, filter lists, query logs/var/lib/hass— Home Assistant config, automations, database/etc/nixos/secrets— Cloudflare credentials, restic password, certs
# List snapshots
ssh nuc 'sudo restic -r /mnt/backup/restic --password-file /etc/nixos/secrets/restic-password snapshots'
# Run a backup manually
ssh nuc 'sudo systemctl start restic-backups-usb.service'If you need to set up the NUC from scratch and restore from the USB backup:
-
Install NixOS as normal (boot from USB, run
scripts/setup.sh) -
Reboot into the installed system and SSH in:
ssh nuc -
Restore secrets from 1Password (if you used
secrets-to-op.sh):eval $(op signin) bash scripts/secrets-from-op.sh
This restores restic password, Cloudflare credentials, and certs. Skip to step 5.
If you don't use 1Password, continue manually:
-
Mount the backup USB (should auto-mount, verify with
mount | grep backup) -
List available snapshots:
sudo restic -r /mnt/backup/restic --password-file /etc/nixos/secrets/restic-password snapshots
-
Stop services before restoring:
sudo systemctl stop home-assistant adguardhome
-
Restore the data:
# Restore everything from the latest snapshot sudo restic -r /mnt/backup/restic --password-file /etc/nixos/secrets/restic-password restore latest --target / # Or restore a specific snapshot (use ID from step 5) sudo restic -r /mnt/backup/restic --password-file /etc/nixos/secrets/restic-password restore <snapshot-id> --target /
This restores files to their original paths:
/var/lib/AdGuardHome— AdGuard skips the setup wizard, keeps your filters and settings/var/lib/hass— Home Assistant keeps your automations, integrations, and history/etc/nixos/secrets— Cloudflare Tunnel credentials and certs are restored
-
Restart services:
sudo systemctl start adguardhome home-assistant
-
Verify — open AdGuard Home and Home Assistant in your browser. Your settings, accounts, and data should all be there.
Store all secrets in 1Password so a fresh install can pull them automatically:
# After initial setup — store secrets from NUC into 1Password
eval $(op signin)
bash scripts/secrets-to-op.sh
# During a fresh install — restore secrets from 1Password to NUC
eval $(op signin)
bash scripts/secrets-from-op.shIf you don't use 1Password, save the restic password manually:
ssh nuc 'sudo cat /etc/nixos/secrets/restic-password'Save it in your password manager. Without it, the backup is encrypted and unrecoverable.
flake.nix # Entry point -- inputs, host config, deployment, tests
hosts/nuc/
default.nix # NUC config -- customize this for your setup
hardware.nix # Hardware-specific (generated during setup)
disk.nix # Disk partitioning layout
modules/
common.nix # Base system: SSH, users, firewall, packages
adguard.nix # AdGuard Home DNS blocking
caddy.nix # Caddy reverse proxy
home-assistant.nix # Home Assistant automation
cloudflared.nix # Cloudflare Tunnel for remote access
homepage.nix # Homepage dashboard
backup.nix # Restic backups to USB stick
tests/
adguard-test.nix # VM test: DNS + web UI
caddy-test.nix # VM test: proxy routing
integration-test.nix # VM test: all services together
scripts/
setup.sh # Bootstrap from NixOS live USB
setup-apps.sh # Create AdGuard Home + Home Assistant accounts, store in 1Password
secrets-to-op.sh # Store all secrets in 1Password
secrets-from-op.sh # Restore secrets from 1Password to NUC
rotate-dashboard-password.sh # Generate, deploy, and store dashboard credentials
restore-backup.sh # Restore service data from restic USB backup
deploy.sh # Deploy changes from laptop
MIT