The idea is for the system to automatically update every day at 3 AM while logging the progress, and by 3:30 AM (as to avoid any overlap), recycle off the older logs (older than 7 days old), and once a week on a Sunday at 4:00 AM, clean up some of the older images.
# ========================
# 🔄 About Automatic Upgrades
# ========================
# Automatically checks for and applies NixOS updates daily at 3:00 AM.
# It runs: `nixos-rebuild switch --upgrade`
#
# âś… What it does:
# - Downloads the latest NixOS packages
# - Rebuilds your system with the new updates
# - Applies changes immediately (new software, fixes, etc.)
# - Saves a log to: /var/log/nixos-upgrade.log
#
# ⚠️ What it *doesn't* do:
# - ❌ Doesn't reboot your system — you still need to do that manually
# - ❌ Doesn't update Flatpak or home-manager apps
#
#
# Ensure logrotate state directory exists
system.activationScripts.ensureLogrotateStateDir.text = ''
mkdir -p /var/lib/logrotate
chown root:root /var/lib/logrotate
chmod 755 /var/lib/logrotate
'';
systemd.services.nixos-upgrade = {
description = "Automatic NixOS upgrade";
serviceConfig = {
Type = "oneshot";
ExecStart = ''
set -euo pipefail
touch /var/log/nixos-upgrade.log
chmod 644 /var/log/nixos-upgrade.log
${pkgs.nixos-rebuild}/bin/nixos-rebuild switch --upgrade 2>&1 | tee -a /var/log/nixos-upgrade.log
'';
StandardOutput = "journal+console";
StandardError = "journal+console";
};
};
systemd.timers.nixos-upgrade = {
description = "Daily NixOS upgrade at 3 AM";
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = "*-*-* 03:00:00"; # Change this if 3 AM isn't a convenient time for you.
Persistent = true;
WakeSystem = true;
};
enable = true; # Ensures the timer is enabled
};
# ========================
# đź“‘ Logrotate Setup for NixOS Upgrade Logs
# Declarative, 7-Day Rotation
# ========================
# Configure logrotate declaratively
# This keeps 7 days of logs. Change "rotate 7" below to keep more or fewer days.
environment.etc."logrotate.d/nixos-upgrade".text = ''
/var/log/nixos-upgrade.log {
daily
missingok
rotate 7
compress
delaycompress
notifempty
create 644 root root
}
'';
# Systemd timer to rotate logs daily at 3:30 AM
systemd.services.logrotate-nixos-upgrade = {
description = "Logrotate for NixOS upgrade logs";
serviceConfig.Type = "oneshot";
serviceConfig.ExecStart = "${pkgs.logrotate}/bin/logrotate /etc/logrotate.d/nixos-upgrade --state /var/lib/logrotate/status";
};
systemd.timers.logrotate-nixos-upgrade = {
description = "Daily Logrotate for NixOS upgrade logs";
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = "*-*-* 03:30:00"; # Log rotation happens 30 minutes after the upgrade to avoid overlap.
Persistent = true;
WakeSystem = true;
};
enable = true; # Ensures the timer is enabled
};
# ========================
# 🗑️ Automatic System Cleanup (Garbage Collection)
# ========================
#
# This system runs a safe, automatic cleanup every Sunday at 4:00 AM.
# It removes unused system generations and outdated packages to free up space.
#
# âś… What it does:
# - Runs: `nix-collect-garbage -d`
# - Deletes old, unused system versions (generations)
# - Frees disk space without affecting the active system
#
# ⚠️ What it doesn't do:
# - ❌ Doesn’t touch the current or booted configuration
# - ❌ Doesn’t reboot your machine
#
# đź’ˇ Pro Tip:
# You can run it manually anytime:
#
# sudo nix-collect-garbage -d
#
# Safe to run regularly — it only deletes what's no longer in use.
systemd.services.nix-garbage-collect = {
description = "Weekly Nix Garbage Collection";
serviceConfig = {
Type = "oneshot";
ExecStart = "${pkgs.nix}/bin/nix-collect-garbage -d";
StandardOutput = "journal";
StandardError = "journal";
};
};
systemd.timers.nix-garbage-collect = {
description = "Weekly Nix Garbage Collection Timer";
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = "Sun *-*-* 04:00:00"; # Every Sunday at 4 AM
Persistent = true;
WakeSystem = true;
};
enable = true; # Ensures timer is enabled declaratively
};
Hard to review this without knowing your intent. If you’re intentionally trying to avoid modern tooling, I suppose that’s mostly fine. I do find it amusing seeing logrotate in systemd services that explicitly log to the journal
Well, I feel silly. I thought I had done a great job documented my work, so it would be obvious my intentions. lol
Intention is to automatically update daily at 3:00 AM, log the process /var/log/nixos-upgrade.log, the /var/log/nixos-upgrade.log file is rotated daily, with up to 7 days of logs kept. Older logs are compressed and discarded after 7 days. Every Sunday at 4:00 AM, the system runs nix-collect-garbage -d, which removes old system generations and outdated packages that are no longer in use.
Right, yes, those intentions are pretty clear, and the comments are in fact quite nice.
The question is whether you’re trying to be hipster about it, trying to learn how you would do this yourself, or if you simply overlooked the existing module. Depending on your answer, you’ll get very different suggestions.
You clearly put in a lot of work, so I’d like to give appropriate review for your use case, not simply tell you to use what someone else wrote.
you can use StateDirectory et al to have systemd make your /var/lib directory. I guess you haven’t really engaged with anyone on why you are using log rotate for this, so I guess I’ll ask one more time: you do realise that you can just skip all the log rotate stuff, write to stdout and just do journalctl -u nixos-upgrade to see what your command got up to? It also makes it trivial to see what else the system was doing at the same time. What is logrotate achieving for you? It’s kind of weird that you have asked for a code review but you don’t seem to want to engage with anyone trying to review
Type 1 - Oh, I see you’re trying to do “random” let me help you with that, but also did you know you can do “other random too”
Type 2 - “Oh, god, you’re doing it that way. Look, let focus on this other thing you didn’t ask for”
I’m more of a type 1 person and tend to ignore type 2, just to avoid debates or hijacked comments or whatever else gets this thread onto page 2+ without going where I need to go.
That said, to generate independent logs. I wanted the update to take place at my desired time. I wanted those logs to last “random” many days. Furthermore, I wanted old logs to go away after those many days, while still adding new ones. And I wanted the system to clean up the junk on my day of choice and my time of choice.
I also wanted to show the work - Kind of how your teacher told you to show your work when doing math. So that, whoever looked at the code, could see more than 1 line that supposedly did half of the work.
I am also working on another script, which will be based on this one, but use the newer syntax that NixOS is using going forward, with flakes in mind. But mine will bypass the version lock and keep it rolling as it is now. – Why? I supposed because I can, and because that is what I want it to do.
Thank you kindly for your time and consideration. I hope you have a wonderful day. A bit off-topic, but I don’t know how the weather is where you are, but here it’s finally feel like Spring -not too cool and not too warm. I hope the weather where you are is just as nice.
If you want to extend the functionality of an existing NixOS module, you don’t need to rewrite it from scratch. This would be equivalent to your module, for example:
There is more to explain and learn, too, e.g. your setting of the PATH env variable isn’t exactly idiomatic. But I’ll hold until we get your full answer.
With that said, I do think it’s worth learning how journalctl and journald in general work. Writing logs to a file on the system is a pretty outdated way of handling logs, though I appreciate it’s very intuitive if you haven’t had much experience with modern Linux systems. journalctl can achieve exactly the same separation and storage requirements, while not needing any of this manual file managenent.
I thank you for your feedback, and you remind me, what I forgot to mention. Which is my fault, as it is likely confusing some people. My code is part of a guide I am writing for absolute newbies, and I don’t expect them to be filtering through the terminal commands, but rather open up KATE the way they would Notepad (Windows) or TextEdit (Apple).
My general goal is to have them not rely on the terminal, as point of fact. Mostly, to get them started with Flatpak and an easy-to-understand Config File, which they can compare with the one included when they 1st install the KDE Plasma ISO. So the whole point, was to have logs, they can locate using Dolphin, and open with Kate, and know that everything is related to that 1 thing (in this case, updates).
Do you know, how many places instruct newbies to disable secure boot, and boot from the USB, but don’t actually tell them how to do that? Well, that is what I am working on. For example: Making sure you're not a bot!
I worked in Consumer IT, long ago. Completely different world, Consumer IT is compared to the workshop in a datacenter. Very humbling to attempt to get people to use things and tell them this is better. Consumers (end-users) will compartmentalize just about everything. They will tell you they want an “all in one” tool, but that thing will overwhelm them faster than editing a file. They don’t think like us. lol
I should add, QJournalctl to the config. They should have one good log viewer. But the whole things of searching and finding more than what they’re looking for, when half the time they don’t know what they’re looking for, is the issue. That is why I wanted them to have 1 dedicated file just for update logs.
I totally get that, I am not here to tell you what to do (and I doubt anyone else is either), I just want to offer the best possible feedback. I see that you have a specific goal with logging to a dedicated file, so fine by me.
I definitely would read up that man page I linked though around State/Log/Runtime directories, it is really nice to just have systemd handle them
offtopic: the weather is nice enough that the car is charging entirely off solar all this month \o/