Compare commits

..

1 commit

Author SHA1 Message Date
085443ab0c
Added script to backup Vaultwarden to Backblaze 2026-06-26 17:15:12 +03:00
7 changed files with 100 additions and 548 deletions

View file

@ -2,10 +2,10 @@
description = "Wekuz's NixOS config";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
home-manager = {
url = "github:nix-community/home-manager/release-26.05";
url = "github:nix-community/home-manager/release-25.11";
inputs.nixpkgs.follows = "nixpkgs";
};
@ -18,11 +18,6 @@
url = "github:nix-community/disko/latest";
inputs.nixpkgs.follows = "nixpkgs";
};
gpgKey = {
url = "https://keys.openpgp.org/vks/v1/by-email/wekuz%40duck.com";
flake = false;
};
};
outputs =
@ -32,7 +27,7 @@
sops-nix,
disko,
...
}@inputs:
}:
{
nixosConfigurations = {
plexy = nixpkgs.lib.nixosSystem {
@ -43,7 +38,6 @@
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.extraSpecialArgs = { inherit inputs; };
home-manager.users.wekuz = import ./hosts/plexy/home.nix;
}
sops-nix.nixosModules.sops

View file

@ -41,7 +41,7 @@
configurationLimit = 8;
};
efi.canTouchEfiVariables = true;
timeout = 0;
timeout = 1;
};
nixpkgs.hostPlatform = "x86_64-linux";
@ -49,20 +49,11 @@
hardware.graphics = {
enable = true;
extraPackages = with pkgs; [
intel-media-driver # VAAPI
intel-compute-runtime # OpenCL
libvdpau-va-gl # VDPAU
intel-media-driver
intel-vaapi-driver
];
};
security.wrappers.btop = {
enable = true;
owner = "root";
group = "root";
source = "${pkgs.btop}/bin/btop";
capabilities = "cap_perfmon=ep";
};
time.timeZone = "Europe/Tallinn";
networking = {
@ -73,16 +64,7 @@
22
80
443
873 # rsyncd
5055 # Seerr
5201 # iperf3
8096 # Jellyfin
15835 # Glance
15836 # qBittorrent (Web UI)
15837 # Radarr
15838 # Sonarr
15839 # Prowlarr
17650 # qBittorrent (torrent)
];
};
@ -95,18 +77,9 @@
secrets = {
"vaultwarden.env" = { };
"rsyncd.secrets" = { };
};
};
virtualisation.docker.enable = true;
environment.systemPackages = [
pkgs.jellyfin
pkgs.jellyfin-web
pkgs.jellyfin-ffmpeg
];
services = {
openssh = {
enable = true;
@ -116,33 +89,6 @@
iperf3 = {
enable = true;
};
rsyncd = {
enable = true;
settings = {
globalSection = {
address = "0.0.0.0";
gid = "users";
"max connections" = 5;
uid = "wekuz";
};
sections = {
media = {
path = "/storage/media";
comment = "Media storage";
"read only" = false;
"auth users" = "wekuz";
"secrets file" = config.sops.secrets."rsyncd.secrets".path;
};
torrents = {
path = "/storage/torrents";
comment = "Torrents storage";
"read only" = false;
"auth users" = "wekuz";
"secrets file" = config.sops.secrets."rsyncd.secrets".path;
};
};
};
};
vaultwarden = {
enable = true;
environmentFile = config.sops.secrets."vaultwarden.env".path;
@ -156,164 +102,21 @@
LOG_LEVEL = "warn";
};
};
glance = {
enable = true;
settings = import ./glance.nix;
};
jellyfin = {
enable = true;
};
seerr = {
enable = true;
};
qbittorrent = {
enable = true;
torrentingPort = 17650;
webuiPort = 15836;
serverConfig = {
Application.FileLogger = {
Enabled = true;
Path = "/var/log/qBittorrent";
Backup = true;
MaxSizeBytes = 65536;
DeleteOld = true;
Age = 14;
AgeType = 0;
};
BitTorrent.Session = {
AddTorrentStopped = false;
Preallocation = true;
AddExtensionToIncompleteFiles = false;
DisableAutoTMMByDefault = false;
MaxConnections = 1000;
MaxConnectionsPerTorrent = 200;
MaxUploads = 64;
MaxUploadsPerTorrent = 26;
GlobalDLSpeedLimit = 6000;
GlobalUPSpeedLimit = 6000;
AlternativeGlobalDLSpeedLimit = 0;
AlternativeGlobalUPSpeedLimit = 0;
BandwidthSchedulerEnabled = true;
DefaultSavePath = "/storage/torrents";
MaxActiveCheckingTorrents = 1;
QueueingSystemEnabled = true;
MaxActiveDownloads = 3;
MaxActiveUploads = 10;
MaxActiveTorrents = 200;
IgnoreSlowTorrentsForQueueing = true;
SlowTorrentsDownloadRate = 500;
SlowTorrentsUploadRate = 100;
SlowTorrentsInactivityTimer = 60;
GlobalMaxRatio = -1;
GlobalMaxSeedingMinutes = -1;
GlobalMaxInactiveSeedingMinutes = -1;
ShareLimitAction = "Stop";
Interface = "";
InterfaceAddress = "";
InterfaceName = "";
};
Network = {
PortForwardingEnabled = false;
};
Preferences = {
General = {
Locale = "en";
StatusbarExternalIPDisplayed = true;
};
Scheduler = {
end_time = "@Variant(\\0\\0\\0\\xf\\x1\\x65\\xe@)"; # 02:00
start_time = "@Variant(\\0\\0\\0\\xf\\0m\\xdd\\0)"; # 06:30
};
WebUI = {
Address = "*";
Username = "admin";
Password_PBKDF2 = "@ByteArray(IM7ih6pLNXBv6it48lI1Lg==:VyczL0q0C89RNfXkzcvdZemfXjdG53xBSY66gqIl56dA0OcrvvxOQdW8jOzvY3lFjR+WDBG3Q/ejsG4w8O5RRA==)";
LocalHostAuth = false;
};
};
Core.AutoDeleteAddedTorrentFile = "never";
LegalNotice.Accepted = true;
};
};
radarr = {
enable = true;
settings = {
server.port = 15837;
};
};
sonarr = {
enable = true;
settings = {
server.port = 15838;
};
};
prowlarr = {
enable = true;
settings = {
server.port = 15839;
};
};
flaresolverr = {
enable = true;
port = 15840;
};
};
environment.variables.EDITOR = "nvim";
users = {
groups = {
media = { };
};
users = {
wekuz = {
isNormalUser = true;
extraGroups = [
"wheel"
"networkmanager"
"docker"
"media"
];
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBs3aPxyJpVGytuVSO3va2WybKNFMR241o8DCJQbBEWV"
];
};
jellyfin = {
extraGroups = [
"media"
];
};
qbittorrent = {
extraGroups = [
"media"
];
};
radarr = {
extraGroups = [
"media"
];
};
sonarr = {
extraGroups = [
"media"
];
};
};
users.users.wekuz = {
isNormalUser = true;
extraGroups = [
"wheel"
"networkmanager"
"docker"
];
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBs3aPxyJpVGytuVSO3va2WybKNFMR241o8DCJQbBEWV"
];
};
systemd.tmpfiles.rules = [
"d /storage 0755 root root -"
"d /storage/media 2775 wekuz media -"
"d /storage/media/movies 2775 wekuz media -"
"d /storage/media/tv 2775 wekuz media -"
"d /storage/torrents 2775 wekuz media -"
];
system.stateVersion = "26.05";
system.stateVersion = "25.11";
}

View file

@ -54,33 +54,6 @@
};
};
};
storagedisk = {
type = "disk";
device = "/dev/disk/by-id/ata-HGST_HUS722T2TALA604_WMC6N0P31NKZ";
content = {
type = "gpt";
partitions = {
primary = {
size = "100%";
content = {
type = "btrfs";
extraArgs = [ "-f" ]; # Override existing partition
subvolumes = {
"@storage" = {
mountOptions = [
"compress=zstd"
"noatime"
];
mountpoint = "/storage";
};
};
mountpoint = "/part-storage";
};
};
};
};
};
};
};
}

View file

@ -1,283 +0,0 @@
{
server = {
host = "0.0.0.0";
port = 15835;
assets-path = "/var/lib/glance/assets";
};
theme = {
light = false;
background-color = "225 10 10";
primary-color = "120 100 37";
negative-color = "0 100 37";
contrast-multiplier = 1.3;
};
pages = [
{
name = "Home";
columns = [
{
size = "small";
widgets = [
{
type = "calendar";
hide-header = true;
}
{
type = "rss";
limit = 10;
collapse-after = 3;
cache = "20m";
feeds = [
{
url = "https://vitalik.eth.limo/feed.xml";
}
{
url = "https://blog.benjojo.co.uk/rss.xml";
}
{
url = "https://selfh.st/rss/";
}
{
url = "https://ersei.net/en/blog.atom";
}
{
url = "https://ersei.net/en/notes.atom";
}
{
url = "https://mbrizic.com/blog/feed.xml";
}
{
url = "https://samwho.dev/rss.xml";
}
{
url = "https://feeds.feedburner.com/martinkl";
}
{
url = "https://cocomelonc.github.io/feed.xml";
}
{
url = "https://www.da.vidbuchanan.co.uk/blog/rss.xml";
}
{
url = "https://kibty.town/blog.rss";
}
{
url = "https://textslashplain.com/feed";
}
{
url = "https://microsoftedge.github.io/edgevr/feed.xml";
}
{
url = "https://eieio.games/feed.xml";
}
{
url = "https://www.da.vidbuchanan.co.uk/blog/rss.xml";
}
{
url = "https://www.rugu.dev/en/index.xml";
}
{
url = "https://ericmigi.com/rss.xml";
}
{
url = "http://www.gridsagegames.com/blog/feed/";
}
{
url = "https://backdrifting.net/rss";
}
{
url = "https://env.fail/blog.rss";
}
{
url = "https://cblgh.org/articles.xml";
}
{
url = "https://cblgh.org/posts.xml";
}
{
url = "https://www.devever.net/~hl/index.feed";
}
{
url = "https://vit.baisa.cz/index.xml";
}
{
url = "https://chrismcleod.dev/follow/blog/feed.rss";
}
{
url = "https://www.winstoncooke.com/blog/atom.xml";
}
{
url = "https://conduition.io/rss2.xml";
}
{
url = "https://xeiaso.net/blog.rss";
}
{
url = "https://brycev.com/rss.xml";
}
{
url = "https://uptime.zen-browser.app/history.rss";
}
{
url = "https://endtimes.dev/feed.xml";
}
{
url = "https://shkspr.mobi/blog/feed/atom";
}
{
url = "https://www.evilsocket.net/atom.xml";
}
{
url = "https://bobdahacker.com/feed.xml";
}
{
url = "https://jorianwoltjer.com/blog/rss.xml";
}
{
url = "https://blog.jaisal.dev/rss.xml";
}
{
url = "https://planetscale.com/blog/feed.atom";
}
{
url = "https://lyra.horse/blog/posts/index.xml";
}
{
url = "https://jasonwryan.com/atom.xml";
}
{
url = "https://jvns.ca/atom.xml";
}
{
url = "https://zacoons.com/index.xml";
}
{
url = "https://notnite.com/blog/rss.xml";
}
{
url = "https://tonsky.me/atom.xml";
}
{
url = "https://heitorpb.github.io/atom.xml";
}
{
url = "https://precondition.github.io/feed.xml";
}
{
url = "https://www.adyxax.org/blog/index.xml";
}
{
url = "https://words.filippo.io/rss";
}
{
url = "https://yashgarg.dev/index.xml";
}
];
}
{
type = "twitch-channels";
sort-by = "viewers";
channels = [
"dreamsofcode_dev"
"tsoding"
"sphaerophoria"
"theprimeagen"
"randy"
];
}
];
}
{
size = "full";
widgets = [
{
type = "reddit";
subreddit = "selfhosted";
}
];
}
{
size = "small";
widgets = [
{
type = "weather";
units = "metric";
hour-format = "24h";
location = "Tallinn Airport";
hide-header = true;
}
{
type = "releases";
repositories = [
"cloudflare/cloudflared"
"dani-garcia/vaultwarden"
"linkwarden/linkwarden"
"glanceapp/glance"
"codeberg:Forgejo/forgejo"
"linuxserver/docker-qbittorrent"
"jellyfin/jellyfin"
"Fallenbagel/jellyseerr"
"Radarr/Radarr"
"Sonarr/Sonarr"
"Prowlarr/Prowlarr"
];
}
{
type = "custom-api";
title = "Epic Games";
cache = "1h";
url = "https://store-site-backend-static.ak.epicgames.com/freeGamesPromotions?locale=en&country=US&allowCountries=US";
template = "<div>
{{ if eq .Response.StatusCode 200 }}
<div class=\"horizontal-cards-2\">
{{ range .JSON.Array \"data.Catalog.searchStore.elements\" }}
{{ $price := .String \"price.totalPrice.discountPrice\" }}
{{ $hasPromo := gt (len (.Array \"promotions.promotionalOffers\")) 0 }}
{{ if and $hasPromo (eq $price \"0\") }}
{{ $gamePage := .String \"productSlug\" }}
{{ if gt (len (.Array \"offerMappings\")) 0 }}
{{ $gamePage = .String \"offerMappings.0.pageSlug\" }}
{{end }}
<a href=\"https://store.epicgames.com/en-US/p/{{ $gamePage }}\" target=\"_blank\" class=\"card\">
{{ $title := .String \"title\" }}
{{ range .Array \"keyImages\" }}
{{ if eq (.String \"type\") \"OfferImageWide\" }}
<img src=\"{{ .String \"url\" }}\" alt=\"{{ $title }}\" style=\"width: 100%; max-width: 300px; height: 150px; object-fit: cover; border-radius: var(--border-radius);\">
{{ end }}
{{ end }}
<div class=\"card-content\">
<span class=\"size-base color-primary\">{{ $title }}</span><br>
<span class=\"size-h5 color-subdue\">
{{ if $hasPromo }}
{{ $promotions := .Array \"promotions.promotionalOffers\" }}
{{ if gt (len $promotions) 0 }}
{{ $firstPromo := index $promotions 0 }}
{{ $offers := $firstPromo.Array \"promotionalOffers\" }}
{{ if gt (len $offers) 0 }}
{{ $firstOffer := index $offers 0 }}
Free until {{ slice ($firstOffer.String \"endDate\") 0 10 }}
{{ else }}
Free this week!
{{ end }}
{{ else }}
Free this week!
{{ end }}
{{ end }}
</span>
</div>
</a>
{{ end }}
{{ end }}
</div>
{{ else }}
<p class=\"color-negative\">Error fetching Epic Games data.</p>
{{ end }}
</div>
";
}
];
}
];
}
];
}

View file

@ -1,16 +1,15 @@
{ pkgs, inputs, ... }:
{ pkgs, ... }:
{
home.username = "wekuz";
home.homeDirectory = "/home/wekuz";
home.packages = with pkgs; [
fastfetch
neofetch
# Utilities
neovim
tmux
gnupg
zip
xz
unzip
@ -35,16 +34,6 @@
ethtool
];
programs.gpg = {
enable = true;
publicKeys = [
{
source = "${inputs.gpgKey}";
trust = 5;
}
];
};
programs.git = {
enable = true;
settings = {
@ -60,5 +49,5 @@
enable = true;
};
home.stateVersion = "26.05";
home.stateVersion = "25.11";
}

View file

@ -1,5 +1,4 @@
vaultwarden.env: ENC[AES256_GCM,data:PLNb5cFoJaOyUo5U/gpXH0RGVwwQxL3kCE/OLw7MaLWazuBGHIPUEhH+kDMP2cfF916D63w8Ddgb/oH+lHGCw235YwgoqZ9yjWYPG4VB2Jdw+17LeCbirdh3zv8nNzFnSZDJmhz5Pj82+iswju6sYO/H7UfZ3aeASWonKW9LGHVltKSY1rAVhZRu4SvhqPiAxNpEzN0/EDTl46RIxrgEoUraN+Ie8fh1wU9SKevExkPU3NXGO1gG2tWRxclMPgc=,iv:es40/5HpBAhOy2xmU/yCUD43fVVsAXk6qJ3OasKjK3U=,tag:bneErU9pPZkQ2VzbQ1NrJA==,type:str]
rsyncd.secrets: ENC[AES256_GCM,data:VjHRoZa6/lDtCheKSJyM,iv:kxygNaKOuK0iH/bFY/uKxcbMcYSR8nWDUx+4aDNqTMk=,tag:uYYPWAPCWtvLLGwmOWyH+A==,type:str]
sops:
age:
- recipient: age1sqssntfzzlhcgp0wuf9wmeavg0hmwmq349npsq8vaxj9sxey5s9ssc82sw
@ -11,7 +10,7 @@ sops:
eDUxR1V0ZEFSYnZTYnYzakFydEliaFUKoa/gHecAy01vTk7I02KMGGPHZBql5K48
hkLDjoWK9dkGRX8kqRd028cuMCQRenLpULEECWp6oV+evUdMf7wRtg==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-04-03T15:46:07Z"
mac: ENC[AES256_GCM,data:gh9Voy28Lo4G1qwQ7/WVAt/L3SAXIY5bs5fdCE0Oywi0ZV4YTJI5VCAcfxuE0Fc3DXdHF0/LORGjKa2l0yp5msMdBVpzI46FBiwYvU+w7EfEoEmhu0yMLf0SZmXkVLEq8u2uVFHGVbR1dOKcdFef+qTIGTlz4cE9VQ01xg/nh7g=,iv:fasXOAm2hjB4ULKT4vPOFnFpAcEgSeNvNM7Zrnk6MvM=,tag:1hWtpuTVdC3H8flA1m0Z0A==,type:str]
lastmodified: "2025-12-30T19:34:00Z"
mac: ENC[AES256_GCM,data:kFttJIQM/Zyd9xeiJpdEVoJ2AhlbuvtUxGBkQTQeEvrJsClAUM7s288XhkG02IxpH9bt2U6AgF60K/3E3qEdFHpM9WKTso/n6mXeD5WUyo9XD1XiWMPZ07arRLH0ajRWHOlIadKm7hvPhEWiWxVg9+sc0BHtjTGMLgvRhUyMPgM=,iv:gsMvU3lxVFSF8bJK5u9cNFSYDT5OZPmWqehv0ozB5Uo=,tag:XsfLCSTlqSgeKy609lMmLQ==,type:str]
unencrypted_suffix: _unencrypted
version: 3.12.2
version: 3.11.0

View file

@ -0,0 +1,77 @@
#!/bin/bash
# This script requires root privileges and the existance of LOG_DIR and
set -Eeuo pipefail
shopt -s failglob
GPG_RECIP="wekuz@duck.com"
DATA_DIR="/var/lib/vaultwarden/data"
BACKUP_DIR="$(mktemp -d /tmp/vw-backup.XXXXXX)"
DB_FILE="$BACKUP_DIR/db.sqlite3"
TAR_FILE="$BACKUP_DIR/vw-backup.tar.zst"
GPG_FILE="$TAR_FILE.gpg"
LOG_DIR="/var/log/vaultwarden"
LOG_FILE="$LOG_DIR/backup.log"
STATUS_FILE="/var/lib/glance/assets/vw-backup.json"
exec >>"$LOG_FILE" 2>&1
write_status() {
local status="$1"
local error_msg="${2:-null}"
local last_success="${3:-null}"
local size_bytes="${4:-null}"
cat >"$STATUS_FILE" <<EOF
{
"status": "$status",
"last_run": "$(date -u +%d/%m/%y-%H:%M)",
"last_success": "$last_success",
"size_bytes": $size_bytes,
"error": $error_msg
}
EOF
}
echo "===== Backup started at $(date -u +%d/%m/%y-%H:%M) ====="
cleanup() {
rm -rf "$BACKUP_DIR"
}
trap cleanup EXIT
failure() {
local err_msg="Command \"$BASH_COMMAND\" failed at line $LINENO"
echo "!!!!! BACKUP FAILED at $(date -u +%d/%m/%y-%H:%M) with error: $err_msg !!!!!"
write_status "failed" "\"$err_msg\"" "$(cat "$STATUS_FILE" | jq -r '.last_success')" null
}
trap failure ERR
log() {
echo "[$(date -u +%d/%m/%y-%H:%M)] $*"
}
log "Starting SQLite DB backup..."
sqlite3 "$DATA_DIR/db.sqlite3" ".backup '$DB_FILE'"
log "SQLite DB backup completed."
log "Creating archive..."
tar -I "zstd -12" -cf "$TAR_FILE" "$DB_FILE" "$DATA_DIR/attachments" "$DATA_DIR"/rsa_key*
log "Archive created."
log "Encrypting backup with GPG..."
gpg --batch --yes -e -r "$GPG_RECIP" "$TAR_FILE"
log "Encryption completed."
log "Uploading backup..."
rclone sync "$GPG_FILE" backblaze:backups-wekuz
log "Upload completed."
write_status "success" null "$(date -u --rfc-3339=seconds)" "$(stat -c%s '$GPG_FILE')"
echo "===== Backup completed successfully at $(date -u +%d/%m/%y-%H:%M) ====="