A cron/at-like interface for systemd user timers and services. Create, manage, and monitor scheduled jobs and persistent services without writing unit files by hand.
- Add a line with `new` as the ID to create a job: `new | every 5 minutes | echo hello`
- Add a service with `new:s` and `service` as the schedule: `new:s,n=monitor | service | /usr/bin/my-monitor.sh`
- Comment out a line (`#`) to disable, uncomment to enable
- Append flags after the ID with `:` — `s` for service, `n=name` for naming, `i` for desktop notification, `e=addr` for email, `o` for output (default 10 lines), `o=N` for custom count, comma-separated (e.g., `a1b2c3:n=backup,i,o,e=user@host`)
- the formatting is just for us humans and whitespace will be stripped on save
Use `-s` instead of `-t` to create a service that starts on login and auto-restarts on failure (`Restart=on-failure`). No timer is involved — the service runs continuously.
```bash
# Persistent service (starts immediately, restarts on failure)
Disable/enable work the same as for timer jobs — disable stops the service and prevents it from starting on login; enable starts it immediately and re-enables it.
Each job gets a 6-character hex ID (e.g., `a1b2c3`) displayed on creation and in status output. You can also assign a human-readable name with `-n` at creation time. Names can be used interchangeably with hex IDs in `-D`, `-E`, `-X`, `-R`, `-S`, and `-L`. Names must be unique and cannot contain whitespace, pipes, or colons.
**Timer jobs** (`-t`): systab creates a `.service` + `.timer` unit file pair in `~/.config/systemd/user/`. One-time jobs auto-unload after firing. Notifications use `ExecStopPost` so they fire after the service completes regardless of success or failure, with `dialog-information` or `dialog-error` icons based on `$SERVICE_RESULT`. When a job has a name, notifications display it as `ID (name)` — looked up dynamically so the label stays current even if the name is changed after creation.
**Service jobs** (`-s`): systab creates a single `.service` unit file with `Type=simple`, `Restart=on-failure`, and `WantedBy=default.target`. No timer is created. The service starts immediately on creation and restarts on login. Disable stops the service; enable starts it again.
All managed units are tagged with a `# SYSTAB_MANAGED` marker comment and a 6-char hex ID. Job output (stdout/stderr) is captured in the systemd journal and viewable via `systab -L`. Flags (names, notification settings, service type) are persisted as `# SYSTAB_FLAGS=` comments in service files so they survive edit sessions.
The primary repository is hosted on [Forgejo](https://code.opennomad.com/opennomad/systab) with a public mirrors on [Codeberg](https://codeberg.org/opennomad/systab) and [GitHub](https://github.com/opennomad/systab).
I was missing the simplicity of `at` and `crontab` commands. `systemd` has many features and benefits that those tools do not have, but convenience for the user to set a quick timer is not one of them.
`-f` validates that the file exists and is executable at creation time, catching typos and permission issues early. With `-c`, errors only surface when systemd runs the job later (visible via `systab -L`). Under the hood, both produce the same `ExecStart` line.
Originally I meant to keep it the same, but there isn't a one-to-one mapping of times supported by cron and systemd. systemd has more options. I also want the human readble strings like "in 5 minutes". Trying to force that into the crontab format with it's space delimited format meant quotes, etc. in the end I used the `|` and limit the number of fields so that the commands can also be piped.