Port to NixOS: replace Helm chart with flake-based NixOS config
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)
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
{ 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" ];
|
||||
};
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user