Short answer: for a nightly payment reconciliation cleanup, choose the least complex scheduler that can prove a run happened, retry it safely, and leave an audit trail. Use a database-native job for a small SQL-only deletion in one Postgres database. Use an app cron webhook when the cleanup policy belongs in Python or touches more than the database. Add a queue when recovery from a long or fanned-out run is the real problem.
The word “cheapest” is a trap here. A timer that costs little to start can cost much more to operate after a missed run, a duplicate request, or a partial deletion. The useful question is where recovery state lives.
This is the shape I use for an edtech payment system: a nightly trigger starts reconciliation, the application compares provider records with local records, and cleanup removes only artifacts that are both expired and safe to remove. The scheduler owns the clock. The application owns the policy.…