Compare commits

..

1 commit

Author SHA1 Message Date
00cc68b7cc init 2026-05-07 22:37:08 +07:00
11 changed files with 277 additions and 202 deletions

View file

@ -7,12 +7,5 @@
- `thinkpad` - Thinkpad T480, i5 8350U, 16GB RAM, 256GB NVME - `thinkpad` - Thinkpad T480, i5 8350U, 16GB RAM, 256GB NVME
- `homelab` - i7 8700T, 32GB RAM, 512GB NVME, 1TB 2.5" SATA - `homelab` - i7 8700T, 32GB RAM, 512GB NVME, 1TB 2.5" SATA
## Todo
- Automatic backups to external drives.
- Better documentation and code structure.
- Use NixOS modules system.
## Credits ## Credits
- [orangc's flake](https://git.orangc.net/c/dots) - [orangc's flake](https://git.orangc.net/c/dots)
- [vimjoyer's tutorials](https://www.youtube.com/@vimjoyer)
- [wallpaper source](https://github.com/er2de2/catppuccin_walls/blob/master/wallpapers_png/autumn_2.0.png)

View file

@ -17,10 +17,8 @@
BAT_PCT=`${pkgs.acpi}/bin/acpi -b | ${pkgs.gnugrep}/bin/grep -P -o '[0-9]+(?=%)'` BAT_PCT=`${pkgs.acpi}/bin/acpi -b | ${pkgs.gnugrep}/bin/grep -P -o '[0-9]+(?=%)'`
BAT_STA=`${pkgs.acpi}/bin/acpi -b | ${pkgs.gnugrep}/bin/grep -P -o '\w+(?=,)'` BAT_STA=`${pkgs.acpi}/bin/acpi -b | ${pkgs.gnugrep}/bin/grep -P -o '\w+(?=,)'`
echo "`date` battery status:$BAT_STA percentage:$BAT_PCT" echo "`date` battery status:$BAT_STA percentage:$BAT_PCT"
export DISPLAY=:0 test $BAT_PCT -le 30 && test $BAT_PCT -gt 15 && test $BAT_STA = "Discharging" && DISPLAY=:0.0 ${pkgs.libnotify}/bin/notify-send "Low Battery" "Battery remaining: $BAT_PCT%."
export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u)/bus test $BAT_PCT -le 15 && test $BAT_STA = "Discharging" && DISPLAY=:0.0 ${pkgs.libnotify}/bin/notify-send -u critical "Low Battery" "Shutdown at 10%."
test $BAT_PCT -le 30 && test $BAT_PCT -gt 15 && test $BAT_STA = "Discharging" && ${pkgs.libnotify}/bin/notify-send "Low Battery" "Battery remaining: $BAT_PCT%."
test $BAT_PCT -le 15 && test $BAT_STA = "Discharging" && ${pkgs.libnotify}/bin/notify-send -u critical "Low Battery" "Shutdown at 10%."
''} > /tmp/cron.batt.log 2>&1" ''} > /tmp/cron.batt.log 2>&1"
]; ];
}; };

View file

@ -49,20 +49,10 @@
initLua = '' initLua = ''
vim.opt.clipboard = "unnamedplus" vim.opt.clipboard = "unnamedplus"
vim.opt.termguicolors = true vim.opt.termguicolors = true
vim.g.clipboard = {
name = "OSC 52",
copy = {
["+"] = require("vim.ui.clipboard.osc52").copy("+"),
["*"] = require("vim.ui.clipboard.osc52").copy("*"),
},
paste = {
["+"] = require("vim.ui.clipboard.osc52").paste("+"),
["*"] = require("vim.ui.clipboard.osc52").paste("*"),
},
}
require("nvim-tree").setup() require("nvim-tree").setup()
vim.api.nvim_create_autocmd("VimEnter", { vim.api.nvim_create_autocmd("VimEnter", {
callback = function() callback = function()
-- vim.cmd("NvimTreeOpen")
vim.cmd("set nu") vim.cmd("set nu")
vim.cmd.wincmd 'p' vim.cmd.wincmd 'p'
end, end,

View file

@ -37,9 +37,9 @@
"cd" = "z"; "cd" = "z";
"sys" = "sudo systemctl --runtime"; "sys" = "sudo systemctl --runtime";
"sys-log" = "journalctl -o cat -f -b -u"; "sys-log" = "journalctl -f -b -u";
"user" = "systemctl --user --runtime"; "user" = "systemctl --user --runtime";
"user-log" = "journalctl -o cat -f -b --user-unit"; "user-log" = "journalctl -f -b --user-unit";
"ts" = "sudo tailscale"; "ts" = "sudo tailscale";
"tsip" = "tailscale ip -4"; "tsip" = "tailscale ip -4";

View file

@ -89,101 +89,6 @@ in {
}; };
pages = [ pages = [
{
name = "Dashboard";
show-mobile-header = true;
width = "slim";
columns = [
{
size = "small";
widgets = [
{
type = "monitor";
title = "Critical Systems";
cache = "15s";
style = "compact";
show-failing-only = true;
sites = map (e: {
same-tab = true;
allow-insecure = true;
title = builtins.elemAt e 0;
url = builtins.elemAt e 1;
}) monitor;
}
{
type = "dns-stats";
title = "DNS Stats";
service = "adguard";
url = "http://localhost:8088/";
hour-format = "12h";
}
{
type = "bookmarks";
groups = [
{
links = [{
same-tab = true;
title = "NixFlake";
icon = "si:nixos";
url = "https://flake.satr14.my.id";
}];
}
{
links = map (e: {
same-tab = true;
title = builtins.elemAt e 0;
icon = "si:${builtins.elemAt e 1}";
url = builtins.elemAt e 2;
alt-status-codes = [ 401 ];
}) bookmarks;
}
];
}
{
type = "to-do";
id = "tasks";
}
];
}
{
size = "full";
widgets = [
{
type = "server-stats";
servers = [{
type = "local";
mountpoints = {
"/boot".hide = true;
"/nix/store".hide = true;
"/var/lib/vaultwarden".hide = true;
"/var/lib/private/cryptpad".hide = true;
"/var/lib/acme/proxy.satr14.my.id".hide = true;
};
}];
}
{
type = "monitor";
cache = "1m";
title = "Services";
sites = map (e: {
same-tab = true;
allow-insecure = true;
title = builtins.elemAt e 0;
icon = "si:${builtins.elemAt e 1}";
url = builtins.elemAt e 2;
check-url = builtins.elemAt e 3;
}) homelab.dash;
}
{
type = "docker-containers";
title = "Containers";
format-container-names = true;
hide-by-default = true;
}
];
}
];
}
{ {
name = "Home"; name = "Home";
show-mobile-header = true; show-mobile-header = true;
@ -282,6 +187,101 @@ in {
} }
]; ];
} }
{
name = "Dashboard";
show-mobile-header = true;
width = "slim";
columns = [
{
size = "small";
widgets = [
{
type = "monitor";
title = "Critical Systems";
cache = "15s";
style = "compact";
show-failing-only = true;
sites = map (e: {
same-tab = true;
allow-insecure = true;
title = builtins.elemAt e 0;
url = builtins.elemAt e 1;
}) monitor;
}
{
type = "dns-stats";
title = "DNS Stats";
service = "adguard";
url = "http://localhost:8088/";
hour-format = "12h";
}
{
type = "bookmarks";
groups = [
{
links = [{
same-tab = true;
title = "NixFlake";
icon = "si:nixos";
url = "https://flake.satr14.my.id";
}];
}
{
links = map (e: {
same-tab = true;
title = builtins.elemAt e 0;
icon = "si:${builtins.elemAt e 1}";
url = builtins.elemAt e 2;
alt-status-codes = [ 401 ];
}) bookmarks;
}
];
}
{
type = "to-do";
id = "tasks";
}
];
}
{
size = "full";
widgets = [
{
type = "server-stats";
servers = [{
type = "local";
mountpoints = {
"/boot".hide = true;
"/nix/store".hide = true;
"/var/lib/vaultwarden".hide = true;
"/var/lib/private/cryptpad".hide = true;
"/var/lib/acme/proxy.satr14.my.id".hide = true;
};
}];
}
{
type = "monitor";
cache = "1m";
title = "Services";
sites = map (e: {
same-tab = true;
allow-insecure = true;
title = builtins.elemAt e 0;
icon = "si:${builtins.elemAt e 1}";
url = builtins.elemAt e 2;
check-url = builtins.elemAt e 3;
}) homelab.dash;
}
{
type = "docker-containers";
title = "Containers";
format-container-names = true;
hide-by-default = true;
}
];
}
];
}
]; ];
}; };
}; };

View file

@ -45,7 +45,7 @@
url = "http://localhost:5080"; #"https://git.proxy.${homelab.domain}"; url = "http://localhost:5080"; #"https://git.proxy.${homelab.domain}";
tokenFile = "/mnt/data/apps/forgejo/token-runner"; tokenFile = "/mnt/data/apps/forgejo/token-runner";
labels = [ "self-hosted:host" ]; labels = [ "self-hosted:host" ];
hostPackages = with pkgs; [ bash coreutils git nix openssh bun ]; hostPackages = with pkgs; [ bash coreutils git nix openssh nodejs ];
}; };
}; };
systemd.services = { systemd.services = {

View file

@ -0,0 +1,103 @@
{ pkgs, ... }: let
serverName = "mc0-explorers-creativity";
serviceName = "minecraft-server-${serverName}";
backupDir = "/mnt/data/backups/mc";
keepBackups = 7; # number of backups to retain
rconHost = "localhost";
rconPort = "25575";
rconPassword = "howdy";
ntfyUrl = "http://127.0.0.1:8067";
ntfyTopic = "mc-backup";
backupScript = pkgs.writeShellScriptBin "mc-backup" ''
set -euo pipefail
rcon() {
${pkgs.rcon-cli}/bin/rcon-cli --address "${rconHost}:${rconPort}" --password "${rconPassword}" "$@" || true
}
BACKUP_OK=false
on_exit() {
# Always restart the server first
echo "[mc-backup] Restarting server..."
systemctl start ${serviceName}.service
# Notify via ntfy only on failure
if [ "$BACKUP_OK" != "true" ]; then
echo "[mc-backup] Sending failure notification..."
${pkgs.curl}/bin/curl -s -o /dev/null \
-H "Title: Minecraft Backup Failed" \
-H "Priority: high" \
-H "Tags: warning" \
-d "Nightly backup failed at $(date '+%Y-%m-%d %H:%M:%S'). Check logs with: journalctl -u mc-backup -n 50" \
"${ntfyUrl}/${ntfyTopic}"
fi
}
# Always restart the server on exit, even if the script fails mid-backup
trap on_exit EXIT
# --- Countdown warnings via RCON ---
echo "[mc-backup] Sending 5-minute warning..."
rcon "say §c[Backup] §fServer will restart in §e5 minutes §ffor a scheduled backup."
sleep 240
echo "[mc-backup] Sending 1-minute warning..."
rcon "say §c[Backup] §fServer restarting in §e1 minute§f."
sleep 50
echo "[mc-backup] Sending 10-second warning..."
rcon "say §c[Backup] §fServer restarting in §e10 seconds§f."
sleep 10
rcon "say §c[Backup] §fShutting down now. Back shortly!"
# --- Save world & stop ---
echo "[mc-backup] Saving world..."
rcon "save-all"
sleep 5
echo "[mc-backup] Stopping ${serviceName}..."
systemctl stop ${serviceName}.service
# --- Backup ---
echo "[mc-backup] Backing up to ${backupDir}..."
mkdir -p "${backupDir}"
TIMESTAMP=$(date +%Y-%m-%d_%H-%M-%S)
XZ_OPT="-9e" tar -cJf "${backupDir}/mc-backup-$TIMESTAMP.tar.xz" \
-C /srv/minecraft ${serverName}
# Prune old backups, keeping the last ${toString keepBackups}
ls -t "${backupDir}"/mc-backup-*.tar.xz | tail -n +${toString (keepBackups + 1)} | xargs -r rm --
echo "[mc-backup] Backup complete: mc-backup-$TIMESTAMP.tar.xz"
BACKUP_OK=true
# Server restart is handled by the EXIT trap above
'';
in {
environment.systemPackages = [ backupScript ];
systemd.services.mc-backup = {
description = "Nightly Minecraft server backup";
serviceConfig = {
Type = "oneshot";
ExecStart = "${backupScript}/bin/mc-backup";
User = "root";
StandardOutput = "journal";
StandardError = "journal";
};
};
systemd.timers.mc-backup = {
description = "Nightly Minecraft backup timer";
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = "daily"; # fires at 00:00:00 every day
Persistent = true; # catch up if the machine was off at midnight
};
};
}

View file

@ -1,65 +1,75 @@
{ inputs, lib, pkgs, ... }: let { inputs, lib, pkgs, ... }: let
production = true; ram-allocation = "10240M";
ram-allocation-mb = 12288; # auth-server = "https://mc.satr14.my.id"; # TODO: self hosted drasl server
rcon-pass = "howdy";
modpack = let modpack = let
commit = "8523f89493ace13087eb68cd9fe3b5eb4f669440"; commit = "667aadf36aac9b0689289f4988a76b924bbb9cbc";
path = if production then "commit/${commit}" else "branch/main";
in pkgs.fetchPackwizModpack { in pkgs.fetchPackwizModpack {
packHash = "sha256-xB9Oc/aneogSQ9r7L42vyVM6xwq+QkoTaXYNuUzeo6M="; packHash = "sha256-sNWuqTIpqnwxhoof5PkJXrvVE5x/wnhc3LoqomjYBNs=";
url = "https://git.satr14.my.id/satr14/server-modpack/raw/${path}/pack.toml"; url = "https://git.satr14.my.id/satr14/server-modpack/raw/commit/${commit}/pack.toml";
}; };
in { in {
imports = [ inputs.mc.nixosModules.minecraft-servers ]; imports = [ inputs.mc.nixosModules.minecraft-servers ];
nixpkgs.overlays = [ inputs.mc.overlay ]; nixpkgs.overlays = [ inputs.mc.overlay ];
powerManagement.cpuFreqGovernor = "powersave"; # performance governor causes overheating and thermal throttling, works fine with powesave
boot.kernel.sysctl = {
"vm.nr_hugepages" = (ram-allocation-mb / 2) + 512; # (heap_mb / 2MB per page) + 512 pages (1GB) for ZGC off-heap overhead
"vm.swappiness" = 10;
};
services.minecraft-servers = { services.minecraft-servers = {
enable = true; enable = true;
eula = true; eula = true;
managementSystem.systemd-socket.enable = true; managementSystem.systemd-socket.enable = true; # Referenced but unset environment variable evaluates to an empty string: MAINPID
# ^^^ https://github.com/Infinidoge/nix-minecraft/issues/119 # ^^^ https://github.com/Infinidoge/nix-minecraft/issues/119
# TODO: figure out how to set gamerules on start servers.mc0-explorers-creativity = {
# gamerules to disable: locator_bar, mob_explosion_drop_decay, (and possibly) reduced_debug_info, global_sound_events
# gamerules to enable (temporarily): noend:disable_end
servers.da-s3 = {
enable = true; enable = true;
autoStart = true; autoStart = true;
restart = "always"; restart = "always";
enableReload = production; enableReload = false; # NOTE: development phase, disable in production
# extraReload = ''
# function rcon() {
# ${pkgs.rcon-cli}/bin/rcon-cli -p ${rcon-pass} $@
# }
# rcon "gamerule locator_bar false" package = pkgs.fabricServers.fabric-1_21_11.override {
# rcon "gamerule mob_explosion_drop_decay false" jre_headless = pkgs.javaPackages.compiler.temurin-bin.jre-25;
# rcon "gamerule reduced_debug_info false" loaderVersion = "0.19.2";
# rcon "gamerule global_sound_events false" };
# '';
operators = lib.mkIf (!production) { jvmOpts = let
"satr14" = { flags = [
uuid = "54441a30-fe73-46e7-adca-c476bd4fc6d2"; "-Xms${ram-allocation}"
bypassesPlayerLimit = true; "-Xmx${ram-allocation}"
level = 4; "--add-modules=jdk.incubator.vector"
};
}; # Custom auth server
# "-Dminecraft.api.env=custom"
# "-Dminecraft.api.auth.host=${auth-server}/auth"
# "-Dminecraft.api.account.host=${auth-server}/account"
# "-Dminecraft.api.profiles.host=${auth-server}/account"
# "-Dminecraft.api.session.host=${auth-server}/session"
# "-Dminecraft.api.services.host=${auth-server}/services"
# Aikar's GC flags (tuned for 10GB)
"-XX:+UseG1GC"
"-XX:+ParallelRefProcEnabled"
"-XX:MaxGCPauseMillis=200"
"-XX:+UnlockExperimentalVMOptions"
"-XX:+DisableExplicitGC"
"-XX:+AlwaysPreTouch"
"-XX:G1HeapWastePercent=5"
"-XX:G1MixedGCCountTarget=4"
"-XX:InitiatingHeapOccupancyPercent=15"
"-XX:G1MixedGCLiveThresholdPercent=90"
"-XX:G1RSetUpdatingPauseTimePercent=5"
"-XX:SurvivorRatio=32"
"-XX:+PerfDisableSharedMem"
"-XX:MaxTenuringThreshold=1"
"-Dusing.aikars.flags=https://mcflags.emc.gs"
"-Daikars.new.flags=true"
"-XX:G1NewSizePercent=30"
"-XX:G1MaxNewSizePercent=40"
"-XX:G1HeapRegionSize=8M"
"-XX:G1ReservePercent=20"
];
in lib.concatStringsSep " " flags;
serverProperties = { serverProperties = {
# server-ip = "localhost";
server-port = 25565; server-port = 25565;
server-name = "Minecraft Server"; server-name = "Digit Association";
motd = "§lSeason 3§r - §dExplorers Creativity 🔥"; motd = "§lSeason 3 TESTING§r - §dExplorers Creativity 🔥";
log-ips = false; # TODO: figure out how to get ips from cloudflared tunnel
difficulty = "normal"; difficulty = "normal";
gamemode = "survival"; gamemode = "survival";
@ -74,41 +84,26 @@ in {
allow-flight = false; allow-flight = false;
player-idle-timeout = 0; player-idle-timeout = 0;
view-distance = 12; # resource-pack = "https://cdn.satr14.my.id/public/fullslide-1.21.11.zip";
simulation-distance = 4; # resource-pack-sha1 = "e0958dcef5755286f390c22280700c471ec34a65";
# resource-pack-enforce = false;
simulation-distance = 16;
view-distance = 4;
enable-rcon = true; enable-rcon = true;
sync-chunk-writes = false; sync-chunk-writes = false;
"rcon.password" = rcon-pass; "rcon.password" = "howdy";
"rcon.port" = 25575; "rcon.port" = 25575;
}; };
symlinks = lib.mapAttrs' symlinks = {
(name: _: lib.nameValuePair "mods/${name}" "${modpack}/mods/${name}") # "resources/datapack/required" = "${modpack}/datapacks";
(builtins.readDir "${modpack}/mods"); "mods" = "${modpack}/mods";
package = pkgs.fabricServers.fabric-1_21_11.override { # "server-icon.png" = "${modpack}/server-icon.png";
jre_headless = pkgs.javaPackages.compiler.temurin-bin.jdk-25; # "config" = "";
loaderVersion = "0.19.2";
}; };
jvmOpts = let flags = [
"-Xms${toString ram-allocation-mb}M"
"-Xmx${toString ram-allocation-mb}M"
"-XX:+UseZGC" # Use ZGC (requires Java v25+, 8+ CPU cores, 10GB+ RAM)
"-XX:+UseCompactObjectHeaders" # Use compact object headers (requires Java v16+, saves a couple of bits per object)
"--add-modules=jdk.incubator.vector" # Exposes SIMD instructions (requires full JDK, useful with performance mods)
"-XX:+UseLargePages" # Large pages support (requires hugepages configured on the system)
"-XX:+AlwaysPreTouch" # Pre-allocates memory on startup, OS claims it immediately for JVM instead of negotiating it
"-XX:+DisableExplicitGC" # Disables mods from manually invoking the GC
"-XX:+PerfDisableSharedMem" # Disables constant /tmp writes for JVM metrics
"-XX:ZAllocationSpikeTolerance=5" # Helps when server is active with many players
"-XX:SoftMaxHeapSize=${toString (ram-allocation-mb - 2048)}M" # Leave 2GB headroom
"-XX:ZCollectionInterval=1" # Force a GC cycle at minimum every second
"-XX:ConcGCThreads=8" # Threads ZGC uses for concurrent work
]; in lib.concatStringsSep " " flags;
}; };
}; };
} }

View file

@ -8,7 +8,6 @@
ntfs3g ntfs3g
exfatprogs exfatprogs
smartmontools smartmontools
rclone
ncdu ncdu
ventoy-full-qt ventoy-full-qt

View file

@ -26,6 +26,7 @@ in {
./homelab/ai.nix ./homelab/ai.nix
./homelab/db.nix ./homelab/db.nix
./homelab/mc.nix ./homelab/mc.nix
./homelab/mc-backup.nix
./core/swapfile.nix ./core/swapfile.nix
./core/oom.nix ./core/oom.nix
@ -35,16 +36,13 @@ in {
users.users.root.openssh.authorizedKeys.keys = homelab.ssh-keys; users.users.root.openssh.authorizedKeys.keys = homelab.ssh-keys;
services = { services.tailscale = {
netbird.enable = true;
tailscale = {
enable = true; enable = true;
authKeyFile = "/mnt/data/apps/tailscale/authkey"; authKeyFile = "/mnt/data/apps/tailscale/authkey";
useRoutingFeatures = "server"; useRoutingFeatures = "server";
extraUpFlags = ts-flags; extraUpFlags = ts-flags;
extraSetFlags = ts-flags; extraSetFlags = ts-flags;
}; };
};
virtualisation = { virtualisation = {
oci-containers.backend = "docker"; oci-containers.backend = "docker";

View file

@ -7,7 +7,6 @@
shell = pkgs.zsh; shell = pkgs.zsh;
extraGroups = [ extraGroups = [
"networkmanager" "networkmanager"
"minecraft"
"wheel" "wheel"
"dialout" "dialout"
"libvirtd" "libvirtd"