# Cron and workers

> How FeatherPanel runs scheduled jobs and background work, the cron runner, the async runner and which jobs run every minute, for Docker and source installs.

Source: https://featherpanel.com/docs/panel/cron-and-workers/

FeatherPanel has two kinds of background work: a cron runner that executes PHP and bash jobs every minute, and the async
runner, a Rust service for longer jobs.

## Docker installs

Nothing to set up. Inside the `backend` container supervisord runs two programs: `frankenphp` (the web server) and
`cron-runner`. `cron-runner.sh` loops forever and, once a minute, runs:

- `php storage/cron/runner.php` as `www-data`, under `flock` so only one run happens at a time, and killed after
  `CRON_TIMEOUT` seconds (default 900).
- `bash storage/cron/runner.bash`.

Output goes to `/var/log/cron.log` inside the container:

```bash
docker exec featherpanel_backend tail -f /var/log/cron.log
```

The async runner is its own container, `featherpanel_async_runner`. It shares the logs volume with the backend.

## Source installs

The source installer writes `/etc/cron.d/featherpanel`:

```cron
* * * * * www-data bash /var/www/featherpanel/backend/storage/cron/runner.bash >/dev/null 2>&1
* * * * * www-data php /var/www/featherpanel/backend/storage/cron/runner.php >/dev/null 2>&1
```

It builds the async runner with Cargo into `runner/target/release/async-runner` and installs it as the
`featherpanel-async-runner` systemd service.

## Jobs the runner executes

The PHP runner loads every file in `backend/storage/cron/php/` (the `glob` order is alphabetical, which is why some jobs
have an `A`, `B` or `Z` prefix), then the `Cron/` folder of each installed plugin. Each job decides on its own whether it
is due.

| Job | Purpose |
|-----|---------|
| `AAdminBackupPolicyProcessor` | Runs due [admin backup policies](https://featherpanel.com/docs/admin/backups) |
| `ANodeBackupAgentProcessor` | Runs due Wings [node backup](https://featherpanel.com/docs/wings/node-backups) policies |
| `AServerAutoStartProcessor` | Starts servers queued for a staggered start after a node reconnects |
| `AServerScheduleProcessor` | Runs due [server schedules](https://featherpanel.com/docs/user/servers/schedules) |
| `BPlayerStatusPoller` | Polls player counts |
| `BServerResourceSampler` | Stores a CPU, memory and disk sample per running server every five minutes and keeps one day |
| `ProcessDueAccountDeletions` | Deletes accounts whose scheduled deletion is due |
| `UmamiTelemetryReporter` | Sends usage telemetry when it is enabled |
| `UserDataExportProcessor` | Builds queued user data exports |
| `ZAllocationCleanup` | Once a day, unassigns allocations that point to servers that no longer exist |
| `ZCheckExpiredServers` | Every five minutes, suspends game servers and VMs past their expiry date |
| `ZDeleteOldData` | Deletes used or expired SSO tokens and soft-deleted mail templates, and prunes notifications (read ones after 30 days, all after 90) |
| `ZFeatherZeroTrustScanner` | Runs the FeatherZeroTrust scanner |
| `ZProxmoxConsoleUserCleanup` | Removes temporary Proxmox console users |

Plugins can add their own jobs. See [Plugins](https://featherpanel.com/docs/plugins).

## Checking that cron runs

If schedules do not fire, backups do not start or the dashboard sparklines stay empty, the cron runner is the first thing
to check:

```bash
docker exec featherpanel_backend ps aux | grep cron-runner
docker exec featherpanel_backend tail -n 50 /var/log/cron.log
```

## Frequently asked questions

### Do I need to add a crontab entry for FeatherPanel?

Not with Docker. The backend container runs a cron loop under supervisord. Source installs get /etc/cron.d/featherpanel from the installer.
