Skip to content

Scheduled Tasks

Demi can create, manage, and monitor scheduled tasks using NixOS systemd timers. These are declarative, survive reboots, and can be rolled back like any other NixOS change.

Creating a Timer

You: back up my postgres database every night at 2am

Demi: Here's the plan:
      - Timer: nurvus-backup-postgres
      - Schedule: *-*-* 02:00:00 (daily at 2am)
      - Command: podman exec postgres pg_dump -U postgres mydb > /srv/backups/postgres/$(date +%Y%m%d).sql
      - Creates /srv/backups/postgres/ directory

      Ready to proceed?

You: yes

Demi: Timer added. Rebuilding to activate.
      ★ nixos_rebuild (action=switch)
      Backup scheduled. First run at 2am tonight.

Schedule Formats

Demi uses systemd calendar notation:

Schedule Meaning
*-*-* 02:00:00 Daily at 2am
hourly Every hour
daily Every day at midnight
weekly Every Monday at midnight
*-*-* *:00/15:00 Every 15 minutes
Mon *-*-* 09:00:00 Every Monday at 9am
*-*-01 00:00:00 First of every month

Managing Timers

You: what timers are running?

Demi: ★ timer (action=list)
      Scheduled tasks (2):
        backup-postgres [enabled]
          Schedule: *-*-* 02:00:00
        update-blocklists [enabled]
          Schedule: weekly
You: disable the postgres backup

Demi: Disabling timer nurvus-backup-postgres. Ready to proceed?

You: yes

Demi: Timer disabled. Rebuild complete.
You: remove the blocklist updater

Demi: Removing timer nurvus-update-blocklists. This will delete the scheduled task.
      Ready to proceed?

You: yes

Demi: Timer removed. Rebuild complete.

Running Tasks Immediately

You can run any scheduled task right now without waiting for its schedule:

You: run the postgres backup now
Demi: ★ timer (action=run, name=backup-postgres)
      Started nurvus-backup-postgres.service.

One-Off Scheduled Tasks

Need to run something once at a specific time? Use run_at for a transient timer that doesn't require a rebuild:

You: restart the minecraft server at midnight tonight
Demi: ★ timer (action=run_at, name=restart-minecraft,
        schedule=*-*-* 00:00:00, command=systemctl restart podman-minecraft)
      Scheduled one-off task for midnight. This won't survive a reboot.

You: back up postgres in an hour
Demi: ★ timer (action=run_at, name=backup-postgres-now,
        schedule=+1h, command=podman exec postgres pg_dump ...)
      Scheduled. Will run in 1 hour.

One-off tasks are transient and won't survive a reboot. Use add for recurring tasks that need to persist.

Common Use Cases

Database backups: - "back up postgres every night" - "dump the nextcloud database weekly"

Container maintenance: - "restart the minecraft server every Sunday at 4am" - "update pi-hole's gravity list daily"

Monitoring: - "check if nginx is responding every 5 minutes and restart it if not"

Cleanup: - "delete postgres backups older than 30 days every week" - "clean up old container images monthly"

How It Works

Under the hood, Demi adds two blocks to configuration.nix:

  1. A systemd service (oneshot) that runs the command
  2. A systemd timer that triggers the service on schedule

All managed timers are prefixed with nurvus- so they don't conflict with system timers. Disabling a timer sets its wantedBy to an empty list so it stays in the config but doesn't activate.