2f0d0b5e4c
Replaces the Helm/k3s setup with a declarative NixOS configuration targeting
a Raspberry Pi 4. Services run as podman containers under systemd, with data
on an external HD at /mnt/data. Key components:
- flake.nix: multi-host flake with pi-main (aarch64) and a placeholder for a
second machine
- modules/common.nix: shared system config (nix, podman, sops, SSH)
- modules/storage.nix: external HD mount with per-service subdirs
- modules/caddy.nix: Caddy with cloudflare DNS-01 ACME + authelia forward_auth
- modules/cloudflared.nix: Cloudflare tunnel for remote access
- modules/backup.nix: restic daily backups with NC maintenance mode pre-hook
- modules/services/{openldap,authelia,gitea,nextcloud,phpldapadmin}.nix: core services
- modules/services/{jellyfin,transmission}.nix: media services (disabled by default)
- secrets/: sops-nix scaffold with .sops.yaml age key config
- hosts/pi-main/: hardware config + service selection for the Pi
- PORTING.md: step-by-step migration guide (SD card → data restore → verify)
151 lines
4.8 KiB
Nix
151 lines
4.8 KiB
Nix
{ config, lib, pkgs, homeyConfig, ... }:
|
|
|
|
# Restic backup module.
|
|
#
|
|
# Backs up all service data directories from the external HD.
|
|
# Schedule: daily at 03:00, keep 7 daily / 4 weekly / 6 monthly snapshots.
|
|
#
|
|
# Before a backup, Nextcloud is put into maintenance mode and postgres is
|
|
# pg_dump'd to a file. This ensures consistent DB backups.
|
|
#
|
|
# Secrets consumed from sops:
|
|
# restic/password
|
|
#
|
|
# The backup repository URL is set per-host in default.nix:
|
|
# homey.backup.repository = "sftp:user@nas:/backups/homey";
|
|
#
|
|
# Restore:
|
|
# restic -r <repo> restore latest --target /mnt/data
|
|
# (or restore a single path: --include /mnt/data/openldap)
|
|
|
|
let
|
|
cfg = config.homey.backup;
|
|
dataDir = config.homey.storage.mountPoint;
|
|
in
|
|
{
|
|
options.homey.backup = {
|
|
enable = lib.mkEnableOption "Restic backup jobs";
|
|
|
|
repository = lib.mkOption {
|
|
type = lib.types.str;
|
|
example = "sftp:user@nas.local:/backups/homey";
|
|
description = ''
|
|
Restic repository URL. Examples:
|
|
sftp:user@host:/path
|
|
b2:bucket-name:prefix
|
|
rclone:remote:path
|
|
/local/path (for testing)
|
|
'';
|
|
};
|
|
|
|
schedule = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "03:00";
|
|
description = "systemd OnCalendar expression for the daily backup.";
|
|
};
|
|
|
|
pruneRetention = lib.mkOption {
|
|
type = lib.types.attrsOf lib.types.str;
|
|
default = {
|
|
daily = "7";
|
|
weekly = "4";
|
|
monthly = "6";
|
|
};
|
|
};
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable {
|
|
# -----------------------------------------------------------------------
|
|
# Secrets
|
|
# -----------------------------------------------------------------------
|
|
sops.secrets."restic/password" = { owner = "root"; };
|
|
|
|
# -----------------------------------------------------------------------
|
|
# Pre-backup hook: pg_dump + nextcloud maintenance mode
|
|
# -----------------------------------------------------------------------
|
|
systemd.services."homey-backup-pre" = {
|
|
description = "Pre-backup hooks (pg_dump, NC maintenance mode)";
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
ExecStart = pkgs.writeShellScript "backup-pre" ''
|
|
set -euo pipefail
|
|
|
|
# Put Nextcloud into maintenance mode (if running)
|
|
if systemctl is-active --quiet podman-nextcloud.service; then
|
|
podman exec nextcloud php occ maintenance:mode --on || true
|
|
fi
|
|
|
|
# Dump postgres (if running)
|
|
if systemctl is-active --quiet podman-nextcloud-postgres.service; then
|
|
install -d -m 700 ${dataDir}/nextcloud/db-dump
|
|
podman exec nextcloud-postgres \
|
|
pg_dump -U postgres nextcloud_db \
|
|
> ${dataDir}/nextcloud/db-dump/nextcloud.sql
|
|
fi
|
|
'';
|
|
};
|
|
};
|
|
|
|
systemd.services."homey-backup-post" = {
|
|
description = "Post-backup hooks (take NC out of maintenance mode)";
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
ExecStart = pkgs.writeShellScript "backup-post" ''
|
|
set -euo pipefail
|
|
if systemctl is-active --quiet podman-nextcloud.service; then
|
|
podman exec nextcloud php occ maintenance:mode --off || true
|
|
fi
|
|
'';
|
|
};
|
|
};
|
|
|
|
# -----------------------------------------------------------------------
|
|
# Restic backup service
|
|
# -----------------------------------------------------------------------
|
|
services.restic.backups.homey = {
|
|
repository = cfg.repository;
|
|
passwordFile = config.sops.secrets."restic/password".path;
|
|
cacheDir = "${dataDir}/restic-cache";
|
|
|
|
paths = [
|
|
"${dataDir}/openldap"
|
|
"${dataDir}/authelia"
|
|
"${dataDir}/gitea"
|
|
"${dataDir}/nextcloud"
|
|
# media and transmission config included when those services are enabled:
|
|
"${dataDir}/jellyfin"
|
|
"${dataDir}/transmission"
|
|
# Deliberately excluded: media/* (large, can be re-downloaded)
|
|
];
|
|
|
|
# Exclude Nextcloud's raw DB directory in favour of the pg_dump file
|
|
exclude = [
|
|
"${dataDir}/nextcloud/db"
|
|
"${dataDir}/restic-cache"
|
|
];
|
|
|
|
timerConfig = {
|
|
OnCalendar = cfg.schedule;
|
|
Persistent = true; # run on next boot if missed
|
|
};
|
|
|
|
pruneOpts = [
|
|
"--keep-daily ${cfg.pruneRetention.daily}"
|
|
"--keep-weekly ${cfg.pruneRetention.weekly}"
|
|
"--keep-monthly ${cfg.pruneRetention.monthly}"
|
|
];
|
|
};
|
|
|
|
# Wire the pre/post hooks around the restic job
|
|
systemd.services."restic-backups-homey" = {
|
|
requires = [ "homey-backup-pre.service" ];
|
|
after = [ "homey-backup-pre.service" ];
|
|
};
|
|
|
|
systemd.services."homey-backup-post" = {
|
|
after = [ "restic-backups-homey.service" ];
|
|
wantedBy = [ "restic-backups-homey.service" ];
|
|
};
|
|
};
|
|
}
|