(define-module (tw system vin) #:use-module (gnu) #:use-module (gnu bootloader grub) #:use-module (gnu packages databases) #:use-module (gnu services admin) ; unattended-upgrade-service-type #:use-module (gnu services databases) #:use-module (gnu services docker) #:use-module (gnu services dbus) #:use-module (gnu services desktop) ; elogind-service-type #:use-module (gnu services shepherd) #:use-module (gnu services syncthing) #:use-module (gnu system locale) #:use-module (gnu system nss) #:use-module (guix gexp) #:use-module (tw channels) #:use-module (tw packages finance) #:use-module (tw services dns) #:use-module (tw services grafana) #:use-module (tw services restic) #:use-module (tw services secrets) #:use-module (tw system)) ;; The device's BIOS does not support UEFI, sadly. It also doesn't recognise ;; NVME devices, so we can only use SATA hard disks, not the M.2 SSD. ;; /dev/sda1 is the https://en.wikipedia.org/wiki/BIOS_boot_partition for grub. (define grub-boot-disk ; must contain a BIOS boot partition "/dev/disk/by-id/wwn-0x5000cca39dd469de") ; this is /dev/sda, usually (define guixsd-root-partition ; /dev/sda2, 500 GB (uuid "86970883-b074-4673-a993-193287432352" 'btrfs)) (define backups-partition ; /dev/sdb1, 1000 GB (uuid "383ee9c7-b17e-43c9-9c39-447d63e22b94" 'btrfs)) (define-public %vin-system (operating-system (host-name "vin.twilken.net") (timezone "Europe/Paris") (locale "en_GB.utf8") (locale-definitions (list (locale-definition (name "en_GB.utf8") (source "en_GB")) (locale-definition (name "de_DE.utf8") (source "de_DE")) (locale-definition (name "fr_FR.utf8") (source "fr_FR")) (locale-definition (name "en_US.utf8") (source "en_US")))) ;; Allow resolution of '.local' host names with mDNS. (name-service-switch %mdns-host-lookup-nss) ;; Choose UK English console keyboard layout. (keyboard-layout %british-keyboard) ;; Packages installed system-wide. Users can also install packages ;; under their own account: use 'guix search KEYWORD' to search ;; for packages and 'guix install PACKAGE' to install a package. (packages %base-system-packages) ;; Below is the list of system services. To search for available ;; services, run 'guix system search KEYWORD' in a terminal. (services (cons* (service restic-server-service-type (restic-server-configuration (repository-path "/var/backups/restic") (bind-address (server-wireguard-address host-name 8181)) (append-only? #t) ; run cleanup jobs separately, using plain restic (private-repos-only? #t) ; require /user/ path prefix (prometheus? #t) (prometheus-auth? #f))) (service restic-cleanup-service-type (list (restic-scheduled-cleanup ;; Laptop backups run at "0 */2 * * *". (schedule #~"0 5 * * *") (repo (restic-local-repository (path "/var/backups/restic/timo/laptop"))) (password (restic-password-source (type 'file) (name "/etc/restic/timo-laptop"))) (user "restic") (keep-within "14d") (keep-weekly 52) (keep-monthly -1)) (restic-scheduled-cleanup (schedule #~"0 5 * * *") (repo (restic-local-repository (path "/var/backups/restic/timo/sync"))) (password (restic-password-source (type 'file) (name "/etc/restic/timo-sync"))) (user "restic") (keep-within "14d") (keep-weekly 52) (keep-monthly -1)) ;; Phone backups run with a new version of restic, which creates ;; v2 repos by default. Guix' older restic version can't read ;; these, so create the repo on the server before pushing to it. ;; Restic doesn't automatically upgrade the repo version. ;; ;; Phone backups run "daily" (modulo Android's throttling of ;; the Restic app; usually between midnight and 2am), so use ;; `keep-daily' instead of `keep-within' to discard duplicates. (restic-scheduled-cleanup (schedule #~"0 4 * * *") (repo (restic-local-repository (path "/var/backups/restic/timo/phone"))) (password (restic-password-source (type 'file) (name "/etc/restic/timo-phone"))) (user "restic") (snapshot-paths '("/storage/FF37-F8E6/SignalBackup")) ;; We only really care about the last signal backup, but guard ;; against accidental deletion by keeping more. (keep-daily 3)) (restic-scheduled-cleanup (schedule #~"0 4 * * *") (repo (restic-local-repository (path "/var/backups/restic/timo/phone"))) (password (restic-password-source (type 'file) (name "/etc/restic/timo-phone"))) (user "restic") (snapshot-paths '("/storage/emulated/0/Backups")) (keep-daily 14) (keep-monthly -1)) (restic-scheduled-cleanup (schedule #~"0 4 * * *") (repo (restic-local-repository (path "/var/backups/restic/timo/phone"))) (password (restic-password-source (type 'file) (name "/etc/restic/timo-phone"))) (user "restic") (snapshot-paths '("/storage/FF37-F8E6/OAndBackupX")) (keep-daily 14) (keep-monthly -1)))) (service secrets-service-type (secrets-configuration (secrets (list (secret (encrypted-file (local-file "files/restic/timo-laptop.enc")) (destination "/etc/restic/timo-laptop") (user "restic") (group "restic")) (secret (encrypted-file (local-file "files/restic/timo-phone.enc")) (destination "/etc/restic/timo-phone") (user "restic") (group "restic")) (secret (encrypted-file (local-file "files/restic/timo-sync.enc")) (destination "/etc/restic/timo-sync") (user "restic") (group "restic")) (secret (encrypted-file (local-file "files/restic/vin-grafana.enc")) (destination "/etc/restic/vin-grafana") (user "restic") (group "restic")))))) ;; For running the Grafana docker container. (service grafana-service-type (grafana-configuration (data-path "/var/lib/grafana") (bind-address (server-wireguard-address host-name)))) (service docker-service-type) ; required by `grafana-service-type' (service dbus-root-service-type) ; required by `docker-service-type' (service elogind-service-type) ; required by `docker-service-type' (simple-service 'grafana-restic-backup restic-backup-service-type (list (restic-scheduled-backup (schedule #~"0 3 * * *") (paths (list "/var/lib/grafana")) (repo (restic-local-repository (path "/var/backups/grafana"))) (password (restic-password-source (type 'file) (name "/etc/restic/vin-grafana")))))) (simple-service 'grafana-restic-cleanup restic-cleanup-service-type (list (restic-scheduled-cleanup (schedule #~"0 4 * * *") (repo (restic-local-repository (path "/var/backups/grafana"))) (password (restic-password-source (type 'file) (name "/etc/restic/vin-grafana"))) (keep-daily 30) (keep-monthly -1)))) ;; Personal statistics exporter: stores hledger data (and soon location ;; data?) in Postgres for Grafana to read. (service syncthing-service-type (syncthing-configuration (user "timo"))) (service postgresql-service-type (postgresql-configuration ;; hledger-dashboard is linked against libpq from postgresql@14. ;; Using a different Postgres server version is probably fine, though. (postgresql postgresql-14))) (simple-service 'hledger-dashboard-roles postgresql-role-service-type (list (postgresql-role (name "timo") (create-database? #t) (permissions '(login))))) (simple-service 'hledger-dashboard shepherd-root-service-type (list (shepherd-service (provision '(hledger-dashboard)) (requirement '(postgresql syncthing-timo)) (documentation "Monitor a ledger file and keep a database in sync with it.") (start #~(make-forkexec-constructor (list #$(file-append hledger-dashboard "/bin/hledger-dashboard") ;; This is where Syncthing puts the ledger file. "-f" "/home/timo/sync/ledger/ledger.journal") #:user "timo" #:group "users" #:environment-variables ;; Use an appropriate locale so that hledger-dashboard ;; can read the UTF-8 ledger file. (cons* (string-append "LC_ALL=" #$(locale-definition-name (car locale-definitions))) (default-environment-variables)))) (stop #~(make-kill-destructor))))) (service unattended-upgrade-service-type (unattended-upgrade-configuration (schedule "22 07 * * sat") ; 07:22 every Saturday morning (maximum-duration (* 40 60)) ; 40 minutes to allow for slow downloads (services-to-restart '(mcron wireguard-wg0 syncthing-timo postgresql hledger-dashboard prometheus-node-exporter restic-server)) (channels %system-channels) (operating-system-expression #~(@ (tw system vin) %vin-system)))) (service mythic-dynamic-dns-service-type (mythic-dynamic-dns-configuration (host-name host-name) (ipv4? #f))) ; have a static IPv4 address, so only update IPv6 (server-base-services host-name))) ;; The list of user accounts ('root' is implicit). (users %server-base-user-accounts) ;; Use the non-UEFI/legacy BIOS variant of GRUB with the boot header ;; installed on the system/root disk. (bootloader (bootloader-configuration (bootloader grub-bootloader) (targets (list grub-boot-disk)) (keyboard-layout keyboard-layout))) ;; The list of file systems that get "mounted". The unique ;; file system identifiers there ("UUIDs") can be obtained ;; by running 'blkid' in a terminal. (file-systems (cons* (file-system ; this is the smaller (500 GB) disk (mount-point "/") (device guixsd-root-partition) (flags '(no-atime)) (options (alist->file-system-options '(("compress" . "zstd")))) (type "btrfs")) (file-system ; this is the bigger (1000 GB) disk (mount-point "/var/backups") (create-mount-point? #t) (device backups-partition) (flags '(no-atime)) (options (alist->file-system-options '(("compress" . "zstd")))) (type "btrfs")) %base-file-systems)))) %vin-system