Cron Jobs: Schedule Server Tasks Without Editing Crontab
The Cron Jobs tab schedules work on your server without cron syntax. Pick a frequency from a dropdown, point the job at a shell command, a saved script, or a URL, and get a Slack, Telegram, or webhook alert the moment a run fails. Every run is logged with its full output.
Cron is how work gets scheduled on a server. Nightly backups, log cleanups, certificate renewals, the small jobs nobody thinks about until one stops. Setting one up means crontab -e, five numbers you decode from memory, and then hoping. And when a job stops running, nothing tells you. You find out days later, when the backup you needed is not there. The real problem with cron was never the syntax. It is the silence.
The Cron Jobs tab replaces both halves of that. Connect to a server and every scheduled job is listed for you, including the ones CtrlOps did not create. Pick a frequency from a dropdown instead of writing an expression, point the job at a shell command, a script you already saved, or a URL, and choose who hears about it when a run fails. Every run is kept with its full output. Scripts you save in the Script Directory can be scheduled here directly, and backup jobs keep their own schedule in the Backup tab.
What you can do in the Cron Jobs tab
- See every scheduled job on the server, with its schedule, next run, last run status, and alerts.
- Find cron jobs that were already on the server and adopt them into the dashboard in one click.
- Build a schedule from a dropdown, and read the next few run times before you save.
- Run a shell command, a saved script from your Script Directory, or an HTTP request.
- Set a timeout, decide how many run logs to keep, and choose what happens when runs overlap.
- Get alerted when a job fails, times out, succeeds, or prints something matching a pattern.
- Send those alerts to Slack, Telegram, or a webhook, using a channel you set up once.
- Run any job right now, off-schedule, without touching its timing.
- Pause, edit, duplicate, or delete a job from the card.
- Select several jobs and pause, resume, or delete them together.
- Open the full history of any job and search, tail, or download the output of a single run.
Why a Cron Jobs tab instead of editing crontab
| Doing it by hand | Doing it in CtrlOps |
|---|---|
ssh user@host, then crontab -e in an editor you may not have picked | Click the server, click the Cron Jobs tab |
Decode 0 2 * * * and hope you counted the fields right | Pick Daily, set the hour and minute |
| Work out when it will actually fire next | Read the next run times, previewed as you choose |
| Jobs somebody else added are invisible until you go looking | Native jobs are listed for you with an Adopt button |
| Redirect output yourself, then remember where you sent it | Every run is logged with its full output |
| Find out it failed when something downstream breaks | A Slack message the moment the run fails |
Wrap the command in flock so two runs cannot collide | Set Overlapping runs |
Prefix the command with timeout 300 | Set Timeout (seconds) |
| Comment the line out to pause it, and remember to put it back | Click Pause, then Resume |
| Copy a similar line and hand-edit the copy | Click Duplicate |
Before you start
You need very little, and CtrlOps tells you when something is missing.
| Requirement | Why you need it | What to do if it is missing |
|---|---|---|
| An active server connection | Jobs are scheduled on the server you are connected to | Open the server from the Home page first |
| Cron on the server | It is what actually fires the jobs | Nearly every Linux distribution ships it. If it is not running, start the cron service |
| An alert channel | Only if you want to be told when a run fails | Set one up once on the Alert Channels tab of the Home screen |
You do not need an alert channel to schedule a job. Without one the job still runs and still records its history, it just runs silently. You can add a channel later and save the job again.
Open the Cron Jobs tab
Connect to your server
Open CtrlOps and click the server you want to schedule work on. Wait for the header to show Connected.
Click the Cron Jobs tab
In the left sidebar, click Cron Jobs. It sits at the bottom of the list, below Audit Report.
See what is already scheduled
If you have no jobs yet you get an empty screen inviting you to create your first one. If the server already has crontab entries, they are listed here with a NATIVE badge, ready to adopt.
Two controls sit in the panel header. The timezone chip next to the title, usually Etc/UTC, tells you which clock every schedule on this page is measured against. Sync crontab re-reads the server's crontab and writes CtrlOps entries back out, which is what you click if the crontab was edited outside the app.
Adopt cron jobs that are already on the server
Most servers already have cron jobs on them, written months ago by somebody who has since moved on. CtrlOps reads the crontab and shows you those jobs instead of pretending they do not exist.
Spot the NATIVE badge
Jobs CtrlOps found rather than created carry a NATIVE badge. They read NOT TRACKED under LAST RUN, because CtrlOps has no history for them yet.
Click Adopt
Click Adopt on the job card
CtrlOps takes the entry over and starts recording every run.
Confirm the job is tracked
The NATIVE badge and the NOT TRACKED label disappear. The card now shows next run, last run status, and alerts like any other job.
Adopting does not change when a job runs. The schedule stays exactly as it was. What changes is that the job now has run history, full output logs, and somewhere to send an alert.
Create a cron job
Click New cron job
Click New cron job (top right of the Cron Jobs tab)
A panel opens on the right with a four-step flow. The left rail tracks where you are and shows a live RESULTING CRONTAB preview as you go.
Name the job and set its schedule
| Field | What to enter |
|---|---|
| Job name | Something you will recognise in an alert at 3am, like nightly-cleanup. |
| Execute user | The Linux user the job runs as. Defaults to the user you connected with. |
| How often | The frequency. See the schedule options below. |
Pick what the job runs
Choose one of three task types: a shell command, a saved script, or an HTTP request. Each one is covered in the three task types below.
Choose your alerts
Tick the conditions that should reach you and pick a channel to send them to. The job fails and The job times out are already ticked. See adding alerts.
Review and create
Click Create cron job
The review screen restates everything in one place: the name, what it runs, the schedule, the guard rails, which conditions alert, and where they go. Leave Enable immediately on and the entry goes into the crontab as soon as you save.
Pick a schedule without writing cron syntax
Pick how often the job should run and CtrlOps writes the expression for you.
| How often | What it means |
|---|---|
| Every N minutes | Runs on a fixed minute interval, for example every 15 minutes. |
| Hourly | Runs once an hour, on the minute you choose. |
| Every N hours | Runs on a fixed hour interval, for example every 6 hours. |
| Daily | Runs once a day, at the hour and minute you choose. |
| Every N days | Runs on a fixed day interval. |
| Weekly | Runs on the weekday and time you choose. |
| Monthly | Runs on the day of the month and time you choose. |
| Custom expression | Write the five cron fields yourself, if you already know them. |
Whatever you pick, two things update live. The EXPRESSION box shows the cron expression with a plain-English reading underneath, and NEXT RUNS lists the next few times the job will fire so you can sanity-check the schedule before saving it.
Down the left rail, RESULTING CRONTAB shows the actual line CtrlOps will write:
0 2 * * * ~/.local/share/ctrlops/cron/jobs/<new>.shSchedules use the server's timezone, shown in the panel header, not your laptop's. If a job needs to run at 2am your time, check the server clock first.
If you would rather understand the syntax itself, our free cron expression generator builds and explains an expression field by field.
Set guard rails on a job
Three settings sit below the schedule. The defaults are sensible, so change them only when you have a reason.
| Setting | What it does | When to change it |
|---|---|---|
| Timeout (seconds) | Stops a run that has gone on too long | Lower it for a job that should be quick, so a hung run is caught rather than left running |
| Logs retention (last runs) | How many runs are kept in the history | Raise it for a job you audit, lower it for a noisy job that runs every few minutes |
| Overlapping runs | What happens when a run is still going and the next one is due | Set it for any job that can occasionally take longer than the gap between runs |
A job scheduled every five minutes that occasionally takes six is the classic way to pile up processes until a server falls over. Overlapping runs is the setting that prevents it, and it costs nothing to set now.
The three task types
| Task type | Use it when |
|---|---|
| Shell command | The job is a one-off specific to this server |
| Script directory | You want the same job on several servers |
| HTTP request | The job is really just pinging a URL |
Shell command
A POSIX shell script, written once for this job and stored with it. This is the right choice for something specific to one server, like clearing a cache directory that only exists there.
Script directory
Runs a script you already saved in the Script Directory, with its runtime variables. Because the script library travels with you rather than living on one box, this is what you want when the same job belongs on several servers: write it once, schedule it everywhere.
HTTP request
Pings a URL on a schedule, with no shell involved at all.
| Field | What to enter |
|---|---|
| Method | The HTTP method, for example GET or POST. |
| URL | The full URL to call, for example a health endpoint. |
Add alerts to a cron job
This is the part that changes how cron feels. Pick what should reach you:
| Alert me when | What triggers it |
|---|---|
| The job fails | The run exits with a non-zero exit code |
| The job times out | The run exceeds the timeout you set |
| The job succeeds | Every clean run, useful for a job you must prove ran |
| Output matches a pattern | The run's output contains a pattern you give |
Open the Alerts step
Either create a new job and continue to step 3, or open an existing job's More actions menu, click Edit, and go to the same step.
Pick a channel under Send to
Click Add channel and pick a saved channel
Channels are set up once on the Alert Channels tab of the Home screen and reused on every job and every server, so most of the time this is picking one off a list.
Tick the triggers you want
The job fails and The job times out are ticked for you, so a new job already alerts on failure unless you turn it off.
Then save the job. Saving is what installs the delivery details on the server.
Because the alert is written into the job on the server rather than handled by the dashboard, it fires whether or not CtrlOps is open. Close the app, close your laptop, and a failed run still reaches you.
Pick an alert channel
Alerts go to a channel: Slack, Telegram, or a webhook. You set a channel up once and reuse it on every job, on every server.
Channels live on the Alert Channels tab of the Home screen, not inside a single job. That tab lists what you have saved, lets you edit or delete a channel, and has a Send test button so you can confirm delivery before you rely on it.
Custom webhook bodies and message templates accept tokens such as {{jobName}}, {{status}}, {{exitCode}}, {{outputTail}}, and {{duration}}. The full list is on the Alert Channels tab itself, under What CtrlOps sends.
Channel changes reach a cron job only when that job is saved. Each job bakes its channel's delivery details into its own wrapper script on the server, which is exactly why alerts work with CtrlOps closed. If you edit or replace a channel, open each job that uses it and save it again. See where CtrlOps puts your cron jobs.
Read the job list
Each job is a card, and the card is meant to answer "is this fine?" without opening anything.
| On the card | What it tells you |
|---|---|
| Schedule | The frequency in plain English, with the cron expression underneath |
| Next run | A countdown and the exact date and time |
| Last run | Whether the previous run succeeded or failed, and how long it took |
| Alerts | Which channel this job notifies, or a prompt to add one |
Above the list, filter chips narrow things down:
| Chip | Shows |
|---|---|
| All | Every job on the server |
| Active | Jobs currently scheduled to run |
| Paused | Jobs that exist but are not firing |
| Failing | Jobs whose last run failed, the one to check first |
| Native | Jobs found on the server that you have not adopted yet |
The search box filters by name, script, or command, which is how you find a job on a server that has thirty of them. The toggle at the top right switches between card and table views.
Act on a single job
| Action | Where it is | What it does |
|---|---|---|
| Run now | Play icon on the card | Runs the job immediately, without changing its schedule |
| History | Clock icon on the card | Opens every recorded run and its output |
| Pause | More actions menu | Stops the job firing, keeps its definition and history |
| Edit | More actions menu | Reopens the four-step panel on an existing job |
| Duplicate | More actions menu | Copies the job as a starting point for a similar one |
| Delete | More actions menu | Removes the job |
Delete removes the entry from the server's crontab, so the job stops running. If you only want it to stop for now, use Pause instead, which keeps the job and its history intact.
Act on several jobs at once
Select jobs in the list and the toolbar turns into bulk actions: Pause, Resume, Delete, and Clear to drop the selection. The honest use for this is a maintenance window: pause everything before you start, resume it when you are done, and you never have to remember which individual jobs you commented out.
Open the run history and logs
Click the history icon on the job card
The clock icon, next to run now, opens that job's run history. How many runs are kept is set by the Logs retention guard rail.
Pick a run
Runs are listed newest first with their status, size, and exit code. Click one to load its output.
Search, tail, or download the output
The full output is here, stdout and stderr together rather than in two separate files you have to correlate.
| Control | What it does |
|---|---|
| Search output | Filters the loaded lines |
| Line count | How many lines to load, 500 by default |
| Follow | Tails a run that is still going |
| Refresh | Re-pulls the output |
| Download | Saves the run's output to your machine |
| Delete | Removes that run from the history |
This is one job's output. For whole log files on disk, like an Nginx access log, use the Logs tab instead.
Where CtrlOps puts your cron jobs
Worth understanding, because it explains most of the behaviour on this page.
CtrlOps does not put your command directly in the crontab. It writes a small wrapper script under ~/.local/share/ctrlops/cron/jobs/ and points the crontab entry at that:
0 2 * * * ~/.local/share/ctrlops/cron/jobs/<new>.shThe wrapper is what records the run, enforces the timeout, applies the overlapping-runs rule, and sends the alert. Three things follow from that. Alerts arrive even with CtrlOps closed, because the server sends them. Editing an alert channel does not reach jobs you already saved, because their delivery details were written into their own wrapper at save time. And if the crontab is edited outside CtrlOps, Sync crontab is what brings the two back into agreement.
Tips
Set the alert when you create the job, not after the first failure. The whole point is to find out from a message rather than from a customer.
On a server you inherited, adopt the native jobs before you change anything. Once they are tracked you can see what actually runs and what has been quietly failing for months.
If the same job belongs on more than one server, save it in the Script Directory first and schedule it as a Script directory task. Then you fix the script once instead of in five separate crontabs.
Troubleshooting
Frequently Asked Questions
How do I create a cron job without editing crontab?
Open CtrlOps, click your server, go to the Cron Jobs tab, and click New cron job. A four-step panel asks for a name and how often the job should run, what it should run, and which failures should alert you. CtrlOps writes the crontab entry for you and shows the resulting line in a preview as you build it.
Do I need to know cron syntax to use CtrlOps?
No. The frequency picker covers every N minutes, hourly, every N hours, daily, every N days, weekly, and monthly, and CtrlOps turns your choice into the cron expression. It also lists the next few run times, so you can confirm the schedule means what you think it means before you save. A custom expression option is there if you already know the syntax.
Can CtrlOps find cron jobs that are already on my server?
Yes. CtrlOps reads the server crontab when you open the tab and lists anything it did not create with a NATIVE badge and an Adopt button. Until you adopt one it reads NOT TRACKED under LAST RUN. Click Adopt and the job joins the dashboard with run history, logs, and alerts, without changing its schedule.
How do I get a Slack alert when a cron job fails?
In the Alerts step of the job, tick The job fails, pick a Slack channel under Send to, and save. The job fails and The job times out are ticked by default, so a new job already alerts on failure unless you turn it off. You can also alert on every successful run, or when the output matches a pattern.
Do cron job alerts still fire when CtrlOps is closed?
Yes. The channel's delivery details are written into the job's wrapper script on the server, so the server sends the alert itself. Nothing depends on the desktop app being open, which is the whole point of putting the alert in the job rather than in the dashboard.
Where can I see the output of a cron job run?
Click the history icon on the job card. Every run is listed with its status, and opening one shows the full output, stdout and stderr together. You can search it, change how many lines to load, download it, delete it, or click Follow to tail a run that is still going.
What can a CtrlOps cron job actually run?
Three things. A shell command is a POSIX shell script you write once for this job. A script directory job runs a saved script from your Script Directory with runtime variables, so the same script works on every server. An HTTP request job takes a method and a URL and pings it on a schedule.
Can I stop a cron job from running twice at the same time?
Yes. Each job carries an Overlapping runs setting alongside its timeout and log retention. Set it when a run can take longer than the gap between runs, for example a job scheduled every five minutes that occasionally takes six.
How do I pause a cron job without deleting it?
Open the More actions menu on the job card and click Pause. The job stops firing but its definition, alerts, and run history are kept, and the same menu resumes it later. You can also select several jobs in the list and pause or resume them together.
What timezone do CtrlOps cron jobs run in?
The server's timezone, which is shown in the panel header next to the Cron Jobs title, for example Etc/UTC. The next run times CtrlOps previews use that same timezone, so a job set for 2:00 runs at 2:00 on the server, not on your laptop.
Backups
Schedule automatic Linux server backups to AWS S3 or Dropbox with CtrlOps. No scripts needed, configure, schedule, and restore in minutes.
Port Forwarding
The Port Forwarding tab tunnels a service on your server to a port on your own machine, over the SSH connection you already have. Build local, remote, or dynamic SOCKS forwards from a form, test one before you save it, and start or stop it in a click. Nothing is ever exposed to the internet.