Moving a Date-Gated Publisher to an Authenticated Ubuntu Timer
π Topic
My future-dated blog posts originally depended on a scheduler running on my Mac. That worked while the Mac was awake, but it was the wrong availability assumption for a publishing job that should continue when the main computer is off.
I prepared a Linux systemd timer on an always-on Ubuntu server and then activated it after owner authentication was available. The interesting security lesson was that moving a timer to another machine also moves authority, credentials, code, state, and failure modes.
π― Goal
Release due posts on both websites every half hour, including after downtime, without copying credentials into the server or enabling an unauthenticated publisher.
π What I Did
The installer requires the Linux owner to authenticate the GitHub CLI first. It refuses to install or enable the timer when gh auth status fails. It also refuses to pin a dirty release script, copies the script from a committed revision, and performs a read-only check before activating the service.
The generated systemd service runs as the owner rather than root. Its restrictions include NoNewPrivileges=true, PrivateTmp=true, ProtectSystem=strict, a narrow writable state directory, and a restrictive umask. The timer runs every half hour and uses Persistent=true so a missed interval can be caught up after the server returns.
The important verification was not just reading the unit file. The actual systemd-started service ran successfully, dispatched both owner workflows, and the resulting deployments completed. The installed source hash matched the committed release script. The old Mac scheduler was then removed to prevent two machines racing to dispatch the same build.
π§ What I Learned
An unattended job is a security boundary. The timer needs an identity, a source revision, a state directory, a network dependency, a failure record, and a recovery rule. βIt runs from cronβ is not a security design.
I also learned to separate preparation from activation. The first Ubuntu handoff correctly said the timer was prepared but not active. Only after the owner completed the authenticated login and the real systemd service succeeded could I call the migration activated.
This is the same evidence habit I use in incident response: configuration is an intention; runtime evidence tells me what actually happened.
β οΈ Limitations
The work proved the systemd-started service and deployment path. It did not include a physical reboot of the Ubuntu server, so I am not claiming boot-after-restart proof. GitHub cron remains a backup, and the scheduler records failures rather than silently claiming success.
β Takeaway
Moving automation to an always-on host improves availability only when the new host has an explicit authentication boundary, least-privilege service settings, pinned code, and a real execution check. A timer is not trustworthy because it is enabled; it is trustworthy when its identity, code, state, and observed run are all accounted for.