If you’re running a Fedora CoreOS server, you might want to schedule a nightly reboot—maybe to keep things fresh or apply updates automatically. Since CoreOS is a minimal, immutable system, we’ll lean on systemd timers instead of the classic cron approach. Let’s set up a reboot every night at 11 PM. Here’s the step-by-step guide, with copy-paste-ready snippets.

I use this on homelab appliances (Pi-hole, AdGuard, a quiet NUC) where a reboot is cheaper than arguing with a leaked container. Pair it with Quadlets so services come back without SSH: Pi-hole Quadlet, AdGuard Quadlet, or the full CoreOS + Bitwarden + AdGuard Ignition guide.

Should you even reboot every night?

Good reasons: you want zincati / automatic updates to finish with a predictable bounce; a GPU or NIC on old hardware gets weird after days of uptime; you like a known-good “DNS was down for 90 seconds at 23:00” window instead of a random 14:07 surprise.

Bad reasons: “reboots make it more secure” by themselves. They do not. Patching does. CoreOS already reboots when Zincati applies an update, depending on your config. A second nightly reboot on top of that is a policy choice. If Zincati already reboots when updates land, you may only want this timer on machines that rarely get updates or that you have disabled automatic reboots on.

DNS / DHCP appliances: a reboot drops resolution for every client until Podman is back. Schedule 23:00 only if nobody is streaming or working. 03:30 is kinder. I keep 23:00 below because that is what the original snippet used; change OnCalendar without changing the rest.

Prerequisites

  • Fedora CoreOS with sudo (core user).
  • The machine’s clock and timezone actually correct (timedatectl). Timers fire in local time unless you use UTC in the calendar spec.
  • Services you care about must start on boot (Quadlets, WantedBy=multi-user.target, etc.). A nightly reboot of a box you start containers on by hand is a practical joke.
  • Physical or IPMI/console access the first night you test, in case the box does not come back (wrong disk, network not online, etc.).

Check time before you edit anything:

timedatectl
date
systemctl list-timers --all | head

If NTP synchronized: no, fix that first (sudo timedatectl set-ntp true or whatever your network allows). A timer at 23:00 on a clock that thinks it is 1970 is chaos.

Step 1: Create a reboot service

First, we need a systemd service to handle the reboot itself. Fedora CoreOS keeps things clean, so we’ll put this in /etc/systemd/system/. Let’s call it reboot-nightly.service.

Create the file with this command (you’ll need sudo):

sudo nano /etc/systemd/system/reboot-nightly.service

Paste this into the file:

[Unit]
Description=Nightly reboot at 11 PM

[Service]
Type=oneshot
ExecStart=/usr/sbin/reboot

Save and exit (Ctrl+O, Enter, Ctrl+X in nano). This is a simple oneshot service—perfect for a one-and-done action like rebooting.

Why a separate service instead of ExecStart on the timer? Timers activate services. Keeping reboot in a oneshot unit means you can systemctl start reboot-nightly.service at 10:59 as a test without waiting for the calendar. You can also add ExecStartPre= later (for example, a script that skips reboot if a backup is running) without touching the timer.

Why /usr/sbin/reboot? On CoreOS that binary exists and talks to systemd. shutdown -r now also works; I stay with reboot because it is explicit. Do not systemctl isolate yourself into a corner in a oneshot unless you know you want that.

There is no [Install] on the service. The timer is what we enable.

Step 2: Set up a timer

Now, let’s schedule it with a systemd timer. Create a file called reboot-nightly.timer in the same directory:

sudo nano /etc/systemd/system/reboot-nightly.timer

Paste this:

[Unit]
Description=Run nightly reboot at 11 PM

[Timer]
OnCalendar=*-*-* 23:00:00
Persistent=true
Unit=reboot-nightly.service

[Install]
WantedBy=timers.target

Save and exit again. Here’s what’s happening:

  • OnCalendar=*-*-* 23:00:00 means “every day at 11 PM” (local time).
  • Persistent=true ensures the reboot triggers even if the system was off at 11 PM—it’ll catch up when it’s back online.
  • The Unit line ties this timer to our service.

About Persistent=true: if the NUC was unplugged from 22:00 to 23:30, systemd may fire the missed job shortly after boot. That is usually what you want for “apply the bounce we skipped.” It is not what you want if you powered the box on at 08:00 to demo something and it immediately reboots. For a laptop-as-appliance that you only sometimes leave on, consider Persistent=false.

Calendar variants:

# 03:30 local, every day
OnCalendar=*-*-* 03:30:00

# 23:00 on weekdays only
OnCalendar=Mon..Fri 23:00:00

# 11 PM UTC regardless of timezone
OnCalendar=*-*-* 23:00:00 UTC

man systemd.time is the reference. Test a calendar string without rebooting:

systemd-analyze calendar '*-*-* 23:00:00'

It should print the next elapse timestamp. If that timestamp is wrong, your timezone is wrong — not the timer file.

Step 3: Activate the timer

Time to make it official! Run these commands:

sudo systemctl daemon-reload
sudo systemctl enable reboot-nightly.timer
sudo systemctl start reboot-nightly.timer
  • daemon-reload refreshes systemd with our new files.
  • enable keeps the timer active across reboots.
  • start kicks it off right away (starts the timer, not an immediate reboot).

Expected output: no errors. enable may print Created symlink … reboot-nightly.timer. If daemon-reload complains about a parse error, nano introduced a bad character — systemctl cat reboot-nightly.timer and look.

To prove the service reboots without waiting until night, only do this when you are ready to bounce:

# optional live test — this WILL reboot now
# sudo systemctl start reboot-nightly.service

I comment that on purpose. Use it once from console.

Step 4: Double-check it’s working

Let’s confirm the timer is set:

systemctl list-timers
systemctl status reboot-nightly.timer
systemctl list-timers reboot-nightly.timer

You’ll see reboot-nightly.timer in the list, with its next run time. If it’s there, you’re golden.

Expected list-timers columns: NEXT, LEFT, LAST, PASSED, UNIT, ACTIVATES. NEXT should be this evening (or tomorrow if you are already past 23:00). LAST may be - until the first firing.

These unit files live in /etc, which survives OSTree updates. You do not re-create them after Zincati. If they vanish, you were editing the wrong machine or a writable overlay you threw away.

A few extra tips

  • Time zone check: The timer uses your server’s local time. Run timedatectl to see it, and adjust with sudo timedatectl set-timezone YOUR_TIMEZONE if needed. Example: sudo timedatectl set-timezone America/Denver.
  • Root access: You’ll need sudo for all this since we’re modifying system files.
  • CoreOS quirk: These changes live in /etc, which survives updates. Don’t mess with /usr—it’s read-only by design.
  • Zincati: if automatic updates also reboot, you can get two reboots in one night. Read sudo systemctl status zincati and the FCOS auto-updates docs. Harmonize policies instead of stacking surprises.
  • Lid on a laptop appliance: a nightly reboot will not help if the laptop is asleep in a backpack. For lid-ignore on CoreOS laptops, the beginner CoreOS post has the HandleLidSwitch=ignore drop-in.
  • Disable later: sudo systemctl disable --now reboot-nightly.timer. Leave the files or rm them; disable is enough to stop the habit.

Troubleshooting

Timer never appears

Wrong path (/usr/lib vs /etc), or forgot daemon-reload. Units must be named something.timer and something.service matching Unit=.

It reboots at the wrong hour

Timezone. timedatectl vs the wall clock in your office. Also check whether you accidentally added UTC on the calendar line.

It reboots on boot at 8 AM

Persistent=true catching up. Set it to false or use a time you always keep the machine powered for.

Quadlet containers do not return after the reboot

That is not the timer’s fault. Fix [Install] WantedBy=multi-user.target on the .container file and test sudo reboot once during the day. See the Pi-hole Quadlet post.

I want a reboot only if an update is waiting

That is Zincati’s job, not this oneshot. Do not glue rpm-ostree status into a homemade script unless you are ready to maintain it. Prefer configuring the update strategy.

Wrap-up

And that’s it! Your Fedora CoreOS server will now reboot every night at 11 PM like clockwork. If you run into snags or want to tweak the timing, feel free to experiment—just adjust the OnCalendar line. Happy automating.

Best regards,

The Linux IT Guy