fixed backup schedules

This commit is contained in:
chaos 2024-03-09 22:18:26 +00:00
parent 145eec0333
commit bde2d41b9f
No known key found for this signature in database
27 changed files with 95 additions and 496 deletions

28
data/backupSchedules.nix Normal file
View file

@ -0,0 +1,28 @@
rec {
restic = let
OnStartupSec = "5m";
in {
low = {
inherit OnStartupSec;
OnUnitActiveSec = "12h";
};
medium = {
inherit OnStartupSec;
OnUnitActiveSec = "8h";
};
high = {
inherit OnStartupSec;
OnUnitActiveSec = "2h";
};
};
music = {
OnStartupSec = "2m";
OnUnitActiveSec = "2h";
};
remoteBackups = {
OnStartupSec = "30m"; # Should give enough time for all to sync current data
OnUnitActiveSec = "1h20m"; # Should give enough time to make small incremental syncs
};
}

View file

@ -1,4 +1,4 @@
{nixosConfig, ...}: {
{...}: {
wayland.windowManager.sway.extraSessionCommands = ''
export KITTY_CACHE_DIRECTORY="/tmp/kitty";
'';

View file

@ -1,8 +1,10 @@
{
self,
pkgs,
config,
...
}: let
backupSchedules = import "${self}/data/backupSchedules.nix";
secrets = config.services.secrets.secrets;
in {
environment.systemPackages = with pkgs; [
@ -27,13 +29,7 @@ in {
passwordFile = "${secrets.restic_password.path}";
environmentFile = "${secrets.restic_env.path}";
pruneOpts = [
"--keep-last 50"
];
timerConfig = {
OnBootSec = "1m";
OnCalendar = "4h";
};
pruneOpts = ["--keep-last 50"];
timerConfig = backupSchedules.restic.high;
};
}

View file

@ -1,8 +1,10 @@
{
self,
pkgs,
config,
...
}: let
backupSchedules = import "${self}/data/backupSchedules.nix";
secrets = config.services.secrets.secrets;
in {
environment.systemPackages = with pkgs; [
@ -27,13 +29,7 @@ in {
passwordFile = "${secrets.restic_password.path}";
environmentFile = "${secrets.restic_env.path}";
pruneOpts = [
"--keep-last 50"
];
timerConfig = {
OnBootSec = "1m";
OnCalendar = "4h";
};
pruneOpts = ["--keep-last 50"];
timerConfig = backupSchedules.restic.high;
};
}

View file

@ -1,9 +1,11 @@
{
self,
pkgs,
config,
...
}: let
secrets = config.services.secrets.secrets;
backupSchedules = import "${self}/data/backupSchedules.nix";
in {
environment.systemPackages = with pkgs; [
restic
@ -27,13 +29,7 @@ in {
passwordFile = "${secrets.restic_password.path}";
environmentFile = "${secrets.restic_env.path}";
pruneOpts = [
"--keep-last 5"
];
timerConfig = {
OnBootSec = "10m";
OnCalendar = "8h";
};
pruneOpts = ["--keep-last 30"];
timerConfig = backupSchedules.restic.high;
};
}

View file

@ -1,9 +1,11 @@
{
self,
pkgs,
config,
...
}: let
secrets = config.services.secrets.secrets;
backupSchedules = import "${self}/data/backupSchedules.nix";
in {
environment.systemPackages = with pkgs; [
restic
@ -27,13 +29,7 @@ in {
passwordFile = "${secrets.restic_password.path}";
environmentFile = "${secrets.restic_env.path}";
pruneOpts = [
"--keep-last 5"
];
timerConfig = {
OnBootSec = "10m";
OnCalendar = "8h";
};
pruneOpts = ["--keep-last 10"];
timerConfig = backupSchedules.restic.low;
};
}

View file

@ -1,8 +1,10 @@
{
self,
pkgs,
config,
...
}: let
backupSchedules = import "${self}/data/backupSchedules.nix";
secrets = config.services.secrets.secrets;
mailConfig = config.services.mailserver;
@ -31,13 +33,7 @@ in {
passwordFile = "${secrets.restic_password.path}";
environmentFile = "${secrets.restic_env.path}";
pruneOpts = [
"--keep-last 100"
];
timerConfig = {
OnBootSec = "1m";
OnCalendar = "8h";
};
pruneOpts = ["--keep-last 60"];
timerConfig = backupSchedules.restic.medium;
};
}

View file

@ -25,7 +25,7 @@
pathInContainer = path: "/var/lib/nixos-containers/${containerName}" + path;
in {
nixpkgs.overlays = [
(final: prev: {
(final: _prev: {
mpd = final.mpd-headless;
})
];
@ -58,7 +58,7 @@ in {
++ (with hosts.hetzner-arm.containers.music.profiles; [
mpd
musicSync
#soulseek
#soulseek # takes up too much ram :(
]);
networking.firewall.allowedTCPPorts = with ports; [

View file

@ -1,7 +1,13 @@
{pkgs, ...}: let
{
self,
pkgs,
...
}: let
inherit (pkgs) writeShellScriptBin;
inherit (builtins) toFile;
backupSchedules = import "${self}/data/backupSchedules.nix";
rcloneConfig = toFile "rclone.conf" ''
[Music]
type = webdav
@ -37,7 +43,7 @@ in {
systemd.timers.music-sync = {
wantedBy = ["timers.target"];
partOf = ["music-sync.service"];
timerConfig.OnCalendar = "hourly";
timerConfig = backupSchedules.music;
};
systemd.services.mpd = {

View file

@ -1,8 +1,10 @@
{
self,
pkgs,
config,
...
}: let
backupSchedules = import "${self}/data/backupSchedules.nix";
secrets = config.services.secrets.secrets;
in {
environment.systemPackages = with pkgs; [
@ -27,13 +29,7 @@ in {
passwordFile = "${secrets.restic_password.path}";
environmentFile = "${secrets.restic_env.path}";
pruneOpts = [
"--keep-last 5"
];
timerConfig = {
OnBootSec = "10m";
OnCalendar = "8h";
};
pruneOpts = ["--keep-last 5"];
timerConfig = backupSchedules.restic.low;
};
}

View file

@ -1,4 +1,4 @@
{pkgs, ...}: {
{...}: {
services.secrets = {
enable = true;

View file

@ -1,11 +1,6 @@
{
self,
hostPath,
...
}: let
wireguardData = import "${self}/data/wireguard/chaosInternalWireGuard.nix";
wireguardHosts = wireguardData.hosts;
{hostPath, ...}: let
#wireguardData = import "${self}/data/wireguard/chaosInternalWireGuard.nix";
#wireguardHosts = wireguardData.hosts;
localContainersAddresses = import "${hostPath}/data/containerAddresses.nix";
in {
services.postgresql = {

View file

@ -1,9 +1,11 @@
{
self,
pkgs,
config,
...
}: let
secrets = config.services.secrets.secrets;
backupSchedules = import "${self}/data/backupSchedules.nix";
backupPrepareCommand = "${
(pkgs.writeShellScriptBin "backupPrepareCommand" ''
@ -34,14 +36,8 @@ in {
passwordFile = "${secrets.restic_password.path}";
environmentFile = "${secrets.restic_env.path}";
pruneOpts = [
"--keep-last 5"
];
timerConfig = {
OnBootSec = "1m";
OnCalendar = "daily";
};
pruneOpts = ["--keep-last 10"];
timerConfig = backupSchedules.restic.high;
inherit backupPrepareCommand;
};

View file

@ -1,8 +1,10 @@
{
self,
pkgs,
config,
...
}: let
backupSchedules = import "${self}/data/backupSchedules.nix";
secrets = config.services.secrets.secrets;
in {
environment.systemPackages = with pkgs; [
@ -18,6 +20,8 @@ in {
services.restic.backups.quassel = {
user = "root";
paths = [
# it's only backing up initial setup / credentials
# so no matter what DB is restored to it should work
"/home/quassel/.config/quassel-irc.org"
];
@ -27,13 +31,7 @@ in {
passwordFile = "${secrets.restic_password.path}";
environmentFile = "${secrets.restic_env.path}";
pruneOpts = [
"--keep-last 5"
];
timerConfig = {
OnBootSec = "1m";
OnCalendar = "daily";
};
pruneOpts = ["--keep-last 5"];
timerConfig = backupSchedules.restic.low;
};
}

View file

@ -1,9 +1,11 @@
{
self,
pkgs,
config,
...
}: let
secrets = config.services.secrets.secrets;
backupSchedules = import "${self}/data/backupSchedules.nix";
# Because gotosocial-admin isn't a seporate package we need to generate a seperate config
# and duplicate the wrapper for use in a systemd unit
@ -55,14 +57,9 @@ in {
passwordFile = "${secrets.restic_password.path}";
environmentFile = "${secrets.restic_env.path}";
pruneOpts = [
"--keep-last 10"
];
timerConfig = {
OnBootSec = "1m";
OnCalendar = "daily";
};
pruneOpts = ["--keep-last 10"];
# Don't want to cause too much downtime and take too long to prune media
timerConfig = backupSchedules.restic.medium;
inherit backupPrepareCommand;
inherit backupCleanupCommand;

View file

@ -1,4 +1,6 @@
{...}: {
{self, ...}: let
backupSchedules = import "${self}/data/backupSchedules.nix";
in {
services.rclone-sync = {
enable = true;
user = "storage";
@ -9,10 +11,7 @@
after = ["auto-secrets.service"];
wants = ["auto-secrets.service"];
};
timerConfig = {
OnStartupSec = "120";
OnCalendar = "4h";
};
timerConfig = backupSchedules.remoteBackups;
extraArgs = [
"--fast-list"
"--check-first"

View file

@ -1,9 +0,0 @@
{
mpd = 6600;
mpd-opus-low = 4242;
mpd-opus-medium = 4243;
mpd-opus-high = 4244;
mpd-flac = 4245;
slskd = 5000;
slskd-web = 5001;
}

View file

@ -1,123 +0,0 @@
{
self,
hostPath,
tree,
lib,
inputs,
pkgs,
config,
...
}: let
inherit (lib.modules) mkMerge;
inherit (lib.lists) forEach;
containerName = "music";
containerAddresses = import "${hostPath}/data/containerAddresses.nix";
hostIP = containerAddresses.host;
containerIP = containerAddresses.containers.${containerName};
ports = import ./data/ports.nix;
# these secrets should probs be in host but im lazy
containerSecrets = config.containers.${containerName}.config.services.secrets.secrets;
pathInContainer = path: "/var/lib/nixos-containers/${containerName}" + path;
in {
containers.music = {
autoStart = true;
privateNetwork = true;
hostAddress = hostIP;
localAddress = containerIP;
specialArgs = {
inherit inputs;
inherit tree;
inherit self;
inherit hostPath;
};
config = {...}: {
nixpkgs.pkgs = pkgs;
imports = with tree;
[
presets.nixos.containerBase
profiles.nginx
profiles.firewallAllow.httpCommon
./secrets.nix
]
++ (with hosts.hetzner-arm.containers.music.profiles; [
mpd
musicSync
soulseek
]);
networking.firewall.allowedTCPPorts = with ports; [
mpd
mpd-opus-low
mpd-opus-medium
mpd-opus-high
mpd-flac
slskd
slskd-web
];
home-manager.users.root.home.stateVersion = "23.05";
system.stateVersion = "23.05";
};
};
services.nginx.virtualHosts."soulseek.owo.monster" = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://${containerIP}:${toString ports.slskd-web}";
proxyWebsockets = true;
};
};
services.nginx.virtualHosts."mpd.owo.monster" = let
extraConfig = ''
auth_basic "Music Password";
auth_basic_user_file ${pathInContainer containerSecrets.music_stream_passwd.path};
'';
in {
forceSSL = true;
enableACME = true;
locations = mkMerge [
{
"/flac" = {
proxyPass = "http://${containerIP}:${toString ports.mpd-flac}";
inherit extraConfig;
};
}
(mkMerge (forEach ["low" "medium" "high"] (quality: {
"/opus-${quality}" = {
proxyPass = "http://${containerIP}:${toString ports."mpd-opus-${quality}"}";
inherit extraConfig;
};
})))
];
};
networking = {
nat.forwardPorts = [
{
sourcePort = ports.mpd;
destination = "${containerIP}\:${toString ports.mpd}";
}
{
sourcePort = ports.slskd;
destination = "${containerIP}\:${toString ports.slskd}";
}
];
firewall.allowedTCPPorts = with ports; [
mpd
slskd
];
};
}

View file

@ -1,69 +0,0 @@
{
lib,
pkgs,
config,
...
}: let
inherit (lib.strings) concatStringsSep;
inherit (lib.lists) forEach;
ports = import ../data/ports.nix;
secrets = config.services.secrets.secrets;
in {
environment.systemPackages = with pkgs; [
mpc_cli
];
services.mpd = {
enable = true;
network.listenAddress = "0.0.0.0";
musicDirectory = "/Music";
credentials = [
{
passwordFile = "${secrets.mpd_control_password.path}";
permissions = ["read" "add" "control" "admin"];
}
];
extraConfig =
''
host_permissions "127.0.0.1 read,add,control,admin"
samplerate_converter "0"
metadata_to_use "title,artist"
auto_update "yes"
audio_buffer_size "4096"
replaygain "track"
audio_output_format "44100:16:2"
''
+ concatStringsSep "\n" (forEach ["low" "medium" "high"] (quality: let
bitrates = {
"low" = "64";
"medium" = "96";
"high" = "128";
};
bitrate = bitrates.${quality};
in ''
audio_output {
type "httpd"
name "HTTP Opus ${bitrate}k"
encoder "opus"
port "${toString ports."mpd-opus-${quality}"}"
bitrate "${bitrate}000"
format "44100:16:2"
always_on "yes"
tags "yes"
signal "music"
}
''))
+ ''
audio_output {
type "httpd"
name "HTTP FLAC"
encoder "flac"
port "${toString ports.mpd-flac}"
format "44100:16:2"
always_on "yes"
tags "yes"
}
'';
};
}

View file

@ -1,49 +0,0 @@
{pkgs, ...}: let
inherit (pkgs) writeShellScriptBin;
inherit (builtins) toFile;
rcloneConfig = toFile "rclone.conf" ''
[Music]
type = webdav
url = https://storage-webdav.owo.monster/MusicRO/
vendor = other
'';
in {
environment.systemPackages = with pkgs; [
rclone
(writeShellScriptBin "rclone-music" ''
rclone --config ${rcloneConfig} "$@"
'')
];
systemd.tmpfiles.rules = [
"d /Music - mpd mpd"
];
systemd.services.music-sync = {
wantedBy = ["multi-user.target"];
after = ["network.target"];
partOf = ["mpd.service"];
path = with pkgs; [bash rclone];
script = ''
set -e
rclone --config ${rcloneConfig} sync Music: /Music
chown -R mpd:mpd /Music
'';
};
systemd.timers.music-sync = {
wantedBy = ["timers.target"];
partOf = ["music-sync.service"];
timerConfig.OnCalendar = "hourly";
};
systemd.services.mpd = {
after = ["music-copy.service"];
serviceConfig = {
ReadOnlyPaths = "/Music";
};
};
}

View file

@ -1,43 +0,0 @@
{
lib,
config,
...
}: let
ports = import ../data/ports.nix;
secrets = config.services.secrets.secrets;
inherit (lib.modules) mkForce;
in {
services.slskd = {
enable = true;
openFirewall = true;
environmentFile = secrets.slskd_env.path;
settings = {
remote_configuration = false;
remote_file_management = true;
soulseek = {
username = "chaoticryptidz";
description = "chaos's soulseek";
listen_port = ports.slskd;
};
web = {
port = ports.slskd-web;
authentication = {
username = "chaos";
};
};
shares.directories = [
"/Music"
];
};
nginx = {
enable = true; # I don't think this is even cheked
domainName = "soulseek.owo.monster";
};
};
services.nginx.virtualHosts."soulseek.owo.monster" = {
forceSSL = mkForce false;
enableACME = mkForce false;
};
}

View file

@ -1,57 +0,0 @@
{pkgs, ...}: {
services.secrets = {
enable = true;
vaultLogin = {
enable = true;
loginUsername = "hetzner-arm-container-music";
};
autoSecrets = {
enable = true;
};
requiredVaultPaths = [
"api-keys/data/mpd"
"api-keys/data/music-stream"
"passwords/data/soulseek"
"passwords/data/slskd"
];
packages = with pkgs; [
apacheHttpd
];
secrets = {
vault_password = {
manual = true;
};
mpd_control_password = {
user = "mpd";
group = "mpd";
fetchScript = ''
simple_get "/api-keys/mpd" .password > "$secretFile"
'';
};
music_stream_passwd = {
user = "nginx";
group = "nginx";
fetchScript = ''
username=$(simple_get "/api-keys/music-stream" .username)
password=$(simple_get "/api-keys/music-stream" .password)
htpasswd -bc "$secretFile" "$username" "$password" 2>/dev/null
'';
};
slskd_env = {
fetchScript = ''
soulseek_password=$(simple_get "/passwords/soulseek" .password)
slskd_password=$(simple_get "/passwords/slskd" .password)
echo > "$secretFile"
echo "SLSKD_SLSK_PASSWORD=$soulseek_password" >> "$secretFile"
echo "SLSKD_PASSWORD=$slskd_password" >> "$secretFile"
'';
};
};
};
}

View file

@ -1,9 +1,11 @@
{
self,
pkgs,
config,
...
}: let
secrets = config.services.secrets.secrets;
backupSchedules = import "${self}/data/backupSchedules.nix";
in {
services.restic.backups.vault = {
user = "root";
@ -11,10 +13,8 @@ in {
"/var/lib/vault"
"/var/lib/private/step-ca"
];
timerConfig = {
OnBootSec = "1m";
OnCalendar = "6h";
};
timerConfig = backupSchedules.restic.high;
# env contains fixed repository with auth
repository = "rest:https://storage-restic.owo.monster/Vault";
passwordFile = "${secrets.restic_password.path}";

View file

@ -65,8 +65,8 @@ in {
timerConfig = mkOption {
type = types.attrs;
default = {
OnStartupSec = "60";
OnCalendar = "4h";
OnStartupSec = "1m";
OnUnitActiveSec = "2h";
};
};
serviceConfig = mkOption {

View file

@ -4,7 +4,6 @@
inherit (lib.attrsets) mergeAttrsList recursiveUpdate;
inherit (lib.lists) foldl' forEach filter;
inherit (lib.strings) optionalString;
hosts = import ./hosts inputs;
in

View file

@ -23,7 +23,7 @@ final: prev: rec {
# this includes a bunch of unneeded files
# but we cba to go through the bat file
# the author uses to figure out what all the 7z commands do)
grocy = prev.grocy.overrideAttrs (old: let
grocy = prev.grocy.overrideAttrs (_old: let
version = "4.1.0";
hash = "";
in rec {
@ -38,51 +38,6 @@ final: prev: rec {
patches = [./grocy-env-variables.diff];
});
# Remove when fixed in upstream
jellyfin-ffmpeg =
(prev.ffmpeg_6-headless.override {
withAribcaption = false; # FIXME remove when updating past version 6.1
})
.overrideAttrs (old: rec {
pname = "jellyfin-ffmpeg";
version = "6.0.1-1";
src = final.fetchFromGitHub {
owner = "jellyfin";
repo = "jellyfin-ffmpeg";
rev = "v${version}";
hash = "sha256-LMwGxx++z6TpZLnpeRGraid4653Mp8T4pY5EP4Z7GXY=";
};
patches = [];
buildInputs = old.buildInputs ++ [prev.chromaprint];
configureFlags =
old.configureFlags
++ [
"--extra-version=Jellyfin"
"--disable-ptx-compression" # https://github.com/jellyfin/jellyfin/issues/7944#issuecomment-1156880067
"--enable-chromaprint"
];
postPatch = ''
for file in $(cat debian/patches/series); do
patch -p1 < debian/patches/$file
done
${old.postPatch or ""}
'';
meta = with final.lib; {
description = "${old.meta.description} (Jellyfin fork)";
homepage = "https://github.com/jellyfin/jellyfin-ffmpeg";
license = licenses.gpl3;
maintainers = with maintainers; [justinas];
pkgConfigModules = ["libavutil"];
};
});
mpd-headless =
(prev.mpdWithFeatures.override {
ffmpeg = final.ffmpeg_6-headless;

View file

@ -35,8 +35,8 @@ in {
systems = ["aarch64-linux"];
supportedFeatures = ["native-arm64"];
publicHostKey = "c3NoLWVkMjU1MTkgQUFBQUMzTnphQzFsWkRJMU5URTVBQUFBSUk5cGM0REU1UlV4UUp2T1pwenFOQWVac0JlRW1kcmp4OFlnV3orVXBMckcgcm9vdEBoZXR6bmVyLWFybQo=";
maxJobs = 4;
speedFactor = 3;
maxJobs = 2;
speedFactor = 2;
}
]))
(mkIf (currentHostname != "vault") (mkMerge [