- Knownframe
- CookDex
- Docs
- Tasks and scheduling
Tasks and scheduling
This is the reference for CookDex task execution, scheduling, automations, safety policies and runtime settings. Every task is also documented in place on CookDex’s Help page.
The Web UI stores schedules through its API (POST /schedules); routes are relative to /cookdex/api/v1 by default, or your WEB_BASE_PATH.
Scheduling
Section titled “Scheduling”CookDex supports two schedule kinds:
| Kind | Required fields | Notes |
|---|---|---|
interval |
seconds |
Runs repeatedly. Optional start_at, end_at, and run_if_missed values are stored in UTC. |
once |
run_at |
Runs one time in the future. Optional run_if_missed controls restored missed runs. |
calendar |
every (day or week), time (HH:MM), weekday (0 = Sunday), timezone |
A wall-clock time in a time zone, so it follows daylight saving. Used by automations; not accepted by POST /schedules. |
POST /schedules accepts interval and once schedules; calendar schedules are made by automations.
Automations
Section titled “Automations”An automation is an ordered list of steps (a task and its options) with settings for the whole run and a trigger. It’s stored by the Web UI and run by the hidden workflow task (python -m cookdex.workflow_runner), which builds every step up front, then runs them one after another in a single run with one log and one set of results.
| Field | Values | Notes |
|---|---|---|
steps |
1 to 12 {task_id, options} |
Options are the task’s own, without dry_run, backup_first or apply_cleanups; those come from the automation. |
mode |
preview, apply |
Preview runs every step as a dry run. |
backup_first |
boolean | With apply, one pre-change backup is made before the first step instead of one per step. |
stop_on_error |
boolean | Stop at the first step that fails, or carry on with the rest. |
trigger |
manual, calendar (every: day/week, time, weekday 0 = Sunday, timezone), interval (seconds, start_at), once (run_at) |
Enabled automations with a time-based trigger own one schedule for the workflow task. Calendar triggers keep their wall-clock time across daylight-saving changes. |
Approval to apply changes unattended is kept per automation (policy key workflow:<id>), not per task. Only an owner can give it; an editor who changes an approved automation’s steps or settings withdraws it. Four ready-made automations ship switched off. Plain task schedules, including older ones and ones made through POST /schedules, are turned into one-step automations with the same timing, options and approval.
Safety Policies
Section titled “Safety Policies”Most write-capable tasks default to dry_run=true. Live runs (dry_run=false) and other dangerous options are blocked unless an owner enables the task policy through PUT /policies or the Web UI unlock flow.
Cleanup commands and reimport return a nonzero exit status when their reports contain failed operations, including partial failures. The maintenance pipeline stops on a failed cleanup stage unless continue_on_error=true.
The Backup First option is hidden while a task is in dry-run mode. When enabled for a live run, CookDex creates a Mealie backup before the main task starts.
Task IDs
Section titled “Task IDs”Data Pipeline
| Task ID | Title | Purpose |
|---|---|---|
data-maintenance |
Data Maintenance Pipeline | Run staged cleanup and audit steps in order, or select a subset of stages. |
recipe-dredger |
Recipe Dredger | Crawl configured recipe sites, verify recipe pages, filter by language, and import verified URLs into Mealie. |
mealie-backup |
Mealie Backup | Create a Mealie backup through the admin API and optionally delete older backups this task made. |
Actions
| Task ID | Title | Purpose |
|---|---|---|
clean-recipes |
Clean Recipe Library | Remove duplicate source URLs, filter junk content, and normalize messy import names. |
slug-repair |
Repair Recipe Slugs | Find recipes whose slug no longer matches their name and fix them. |
ingredient-parse |
Ingredient Parser | Parse raw ingredient text into structured food, unit, and quantity fields. |
yield-normalize |
Yield Normalizer | Fill missing yield text or parse yield text into numeric servings. |
cleanup-duplicates |
Clean Up Duplicates | Merge duplicate food, unit, tag, and category entries. |
reimport-recipes |
Re-import Recipes | Re-scrape source URLs while preserving recipe identity, favorites, and organization. |
Organizers
| Task ID | Title | Purpose |
|---|---|---|
tag-categorize |
Tag & Categorize Recipes | Assign categories, tags, and tools with rule matching and optional AI. |
Audits
| Task ID | Title | Purpose |
|---|---|---|
health-check |
Health Check | Run read-only recipe quality and taxonomy audits. |
Options
Section titled “Options”data-maintenance
Section titled “data-maintenance”| Option | Type | Default | Description |
|---|---|---|---|
dry_run |
boolean | true |
Preview changes without writing anything. |
backup_first |
boolean | true |
Create a Mealie backup before a live run. Hidden while dry_run=true. |
stages |
string list | all stages | Select stages: dedup, junk, names, parse, foods, units, categorize, yield, quality, audit. The retired labels, tools, taxonomy and cookbooks stages are skipped. |
confidence_threshold |
integer | 70 |
Ingredient parser NLP confidence percentage. Lower accepts more NLP results and reduces AI fallback. |
max_recipes |
integer | unset | Limit ingredient parsing when the parse stage runs. |
no_cache |
boolean | false |
Ignore the ingredient parser scan cache. |
reason |
string | all categories | Limit junk filtering to one category. |
force_all |
boolean | false |
Normalize all names, not only unformatted names. |
provider |
string | configured default | Override AI provider for categorization: chatgpt, anthropic, or ollama. |
nutrition_sample |
integer | 200 |
Nutrition sample size for the quality stage. Not used when the database is connected (coverage is exact then). |
continue_on_error |
boolean | false |
Keep running later stages if one stage fails. |
apply_cleanups |
boolean | false |
Dangerous. Allows cleanup stages to write changes. Hidden while dry_run=true. |
Default stage order:
dedup -> junk -> names -> parse -> foods -> units -> categorize -> yield -> quality -> audit
recipe-dredger
Section titled “recipe-dredger”| Option | Type | Default | Description |
|---|---|---|---|
dry_run |
boolean | true |
Preview imports without writing to Mealie. |
limit |
integer | 50 |
Maximum recipes to import per configured site. |
depth |
integer | 1000 |
Maximum URLs to scan per site sitemap. |
no_cache |
boolean | false |
Ignore cached sitemaps and crawl fresh. |
import_workers |
integer | 2 |
Concurrent import workers, capped at 4. |
precheck_duplicates |
boolean | true |
Fetch existing Mealie recipes first and skip duplicates before import. |
language_filter |
boolean | true |
Skip recipes that do not match DREDGER_TARGET_LANGUAGE. |
max_retry_attempts |
integer | 3 |
Retry transient failures before marking a URL rejected. |
mealie-backup
Section titled “mealie-backup”| Option | Type | Default | Description |
|---|---|---|---|
keep |
integer | unset | After creating a backup, delete older backups this task made, keeping this many. |
prune_only |
boolean | false |
Skip backup creation and only delete older backups this task made. Requires keep. |
CookDex records every backup it creates in backup_ledger.json in the checkpoint directory (cache/maintenance by default), and pruning only ever deletes backups listed there. Backups made in Mealie, uploaded to it, or made before CookDex kept the ledger are never deleted. The backup taken before a change (Backup First) is recorded separately; CookDex keeps the newest 10 of those, and they don’t count against keep.
clean-recipes
Section titled “clean-recipes”| Option | Type | Default | Description |
|---|---|---|---|
dry_run |
boolean | true |
Preview changes without writing anything. |
backup_first |
boolean | true |
Create a Mealie backup before a live run. Hidden while dry_run=true. |
run_dedup |
boolean | true |
Remove imported duplicates with the same source URL. |
run_junk |
boolean | true |
Remove records missing both meaningful ingredients and usable instructions; report ambiguous title matches for review. |
run_names |
boolean | true |
Normalize names derived from URL slugs. |
reason |
string | all categories | Limit junk filtering to one category. |
force_all |
boolean | false |
Normalize all recipe names, not only unformatted names. |
slug-repair
Section titled “slug-repair”| Option | Type | Default | Description |
|---|---|---|---|
dry_run |
boolean | true |
Only list the recipes that would change. |
backup_first |
boolean | true |
Create a backup before repairing addresses. |
Live runs fix each recipe through Mealie’s API (saving its name with a trailing space and back, which makes Mealie regenerate the slug), or in one database transaction when the database is connected. Recipes whose name would take a slug another recipe already has are skipped and listed.
ingredient-parse
Section titled “ingredient-parse”| Option | Type | Default | Description |
|---|---|---|---|
dry_run |
boolean | true |
Preview changes without writing anything. |
backup_first |
boolean | true |
Create a Mealie backup before a live run. Hidden while dry_run=true. |
max_recipes |
integer | unset | Limit parsing to at most N recipes. |
no_cache |
boolean | false |
Ignore the scan cache and reprocess all unparsed recipes. |
confidence_threshold |
integer | 70 |
Minimum NLP confidence percentage before accepting a parse. Low-confidence lines fall back to AI parsing. |
yield-normalize
Section titled “yield-normalize”| Option | Type | Default | Description |
|---|---|---|---|
dry_run |
boolean | true |
Preview changes without writing anything. |
backup_first |
boolean | true |
Create a backup before filling servings. |
cleanup-duplicates
Section titled “cleanup-duplicates”| Option | Type | Default | Description |
|---|---|---|---|
dry_run |
boolean | true |
Preview merges without writing anything. |
backup_first |
boolean | true |
Create a Mealie backup before a live run. Hidden while dry_run=true. |
target |
string | both |
Deduplicate both (foods and units), foods, units, taxonomy (tags and categories), tags, or categories. |
Tags and categories are merged through Mealie’s POST /organizers/tags/merge and POST /organizers/categories/merge routes (Mealie v3.25+). Mealie moves every recipe from the duplicate to the kept entry and deletes the duplicate. Mealie does not update cookbook filters that name the duplicate, so CookDex repoints those cookbooks at the kept entry. The kept entry is the one used by the most recipes. Matching is conservative: names must differ only by case, spacing, punctuation, accents, & vs and, or a plural ending on the last word whose singular also exists (for example Gluten-Free / gluten free, or Cookie / Cookies). On an older Mealie the merge routes are missing; the run reports this and skips those merges without failing.
reimport-recipes
Section titled “reimport-recipes”| Option | Type | Default | Description |
|---|---|---|---|
dry_run |
boolean | true |
Preview eligible recipes. |
backup_first |
boolean | true |
Create a Mealie backup before a live run. Hidden while dry_run=true. |
max_recipes |
integer | unset | Limit reimport to at most N recipes. |
workers |
integer | 2 |
Concurrent scrape workers, capped at 4. |
delay |
number | 0.5 |
Seconds between requests per worker. |
resume |
boolean | false |
Skip recipes in the durable completion checkpoint; previews do not overwrite it. |
slugs |
string | unset | Comma-separated recipe slugs to reimport. Leave blank for all eligible recipes. |
When invoking python -m cookdex.recipe_reimporter directly, writes require --apply; DRY_RUN=true still overrides that flag.
Reimport uses the Mealie API. When the database is connected and an older Mealie rejects an update with a 403 because of a slug mismatch, it repairs the slug and retries.
tag-categorize
Section titled “tag-categorize”| Option | Type | Default | Description |
|---|---|---|---|
dry_run |
boolean | true |
Preview changes without writing anything. |
backup_first |
boolean | true |
Create a Mealie backup before a live run. Hidden while dry_run=true. |
method |
string | both |
both runs rules first then AI, rules uses rules only, ai skips rules. |
recat |
boolean | false |
Re-process every recipe, including recipes that already have organization data. Hidden for rules. |
provider |
string | configured default | Override AI provider for this run. Hidden for rules. |
missing_targets |
string | skip |
skip missing taxonomy targets or create them automatically. Hidden for ai. |
Retired: taxonomy-refresh and cookbook-sync
Section titled “Retired: taxonomy-refresh and cookbook-sync”Both pushed CookDex’s own copy of the taxonomy to Mealie. Organize now edits Mealie directly, so they were removed along with the Taxonomy Editor. On upgrade, CookDex saves whatever the old copy held (and any unpublished editor draft) to reports/retired-managed-taxonomy-<time>.json, which you can import in Organize, and turns off schedules that used either task with a note saying why.
Cookbook filters
Section titled “Cookbook filters”Each cookbook’s queryFilterString is a list of clauses joined with AND. CookDex accepts these clauses:
| Filter | Fields | Operators | Example |
|---|---|---|---|
| Categories | recipeCategory.name, recipe_category.id |
IN, NOT IN, CONTAINS ALL |
recipeCategory.name IN ["Dinner"] |
| Tags | tags.name, tags.id |
IN, NOT IN, CONTAINS ALL |
tags.name NOT IN ["Dessert"] |
| Tools | tools.name, tools.id |
IN, NOT IN, CONTAINS ALL |
tools.name IN ["Air Fryer"] |
| Foods | recipeIngredient.food.name, recipeIngredient.food.id |
IN, NOT IN, CONTAINS ALL |
recipeIngredient.food.name IN ["Chicken"] |
| Food labels | recipeIngredient.food.label.name, recipe_ingredient.food.label_id |
IN, NOT IN, CONTAINS ALL |
recipeIngredient.food.label.name IN ["Seafood"] |
| Rating | rating |
=, <>, >, >=, <, <= |
rating >= 4 |
- Filters in an imported taxonomy file can use names. Organize resolves category, tag, tool and food-label names to Mealie ids when the change is applied, after any tags or categories created in the same batch. A name that can’t be found fails that cookbook with a message.
ratingis the recipe’s average rating across all users, from 0 to 5. Unrated recipes never match a rating clause, includingrating < 3.- Rating filters need Mealie v3.25 or later, and food-label filters need v3.28 or later. When an older Mealie rejects one of these filters, Mealie’s error is shown for that cookbook.
Example: rating >= 4 AND recipeIngredient.food.label.name IN ["Seafood"].
health-check
Section titled “health-check”| Option | Type | Default | Description |
|---|---|---|---|
scope_quality |
boolean | true |
Score recipe completeness for categories, tags, tools, ingredients, cook time, yield, and nutrition. |
scope_taxonomy |
boolean | true |
Scan taxonomy for unused entries, duplicate names, and recipes missing categories or tags. |
nutrition_sample |
integer | 200 |
Nutrition sample size. Not used when the database is connected (coverage is exact then). |
health-check is read-only and does not expose a dry_run option.
Database Connection
Section titled “Database Connection”Every task works through Mealie’s API. When a database connection string is saved in Settings -> Faster database access (MEALIE_DB_URL), tasks that can use it do so on their own, and fall back to the API with a warning in the log if it can’t be reached. There is no per-task switch. The --use-db flag on the command-line modules still forces it.
The Docker image includes the PostgreSQL and SSH drivers. Source installs are covered in Local Dev.
See Database connection for setup, what it speeds up, and what CookDex reads and writes.
Runtime Settings
Section titled “Runtime Settings”Runtime settings are managed through GET /settings and PUT /settings.
- Non-secret values are stored in the app state database.
- Secret values are encrypted at rest.
- Task runs receive the effective runtime environment built from the settings catalog.
.envis only for container settings (port, base path, HTTPS, headless bootstrap). On first start, older.envvalues for catalog settings are copied into Settings once, and the Settings page is the source of truth from then on.
Main setting groups:
| Group | Examples |
|---|---|
| Connection | MEALIE_URL, MEALIE_API_KEY |
| AI | CATEGORIZER_PROVIDER, OPENAI_API_KEY, ANTHROPIC_API_KEY, OLLAMA_URL, model settings |
| Dredger | DREDGER_TARGET_LANGUAGE, DREDGER_CRAWL_DELAY, DREDGER_CACHE_EXPIRY_DAYS |
| Faster database access | MEALIE_DB_URL, optional SSH settings (MEALIE_DB_SSH_HOST, _USER, _KEY) |
| Runner | MAX_RUN_DURATION_SECONDS |
Every setting is editable in Settings, which is also the complete reference — each field shows its group, default, and description. A few that are easy to miss:
| Setting | Default | Purpose |
|---|---|---|
UPDATE_CHECK_ENABLED |
true |
Check GitHub for releases on startup and about daily. Disabled means no update request. Only the app version is sent; GitHub sees the server network address. Failures show no banner. |
AI_BATCH_HEARTBEAT_SECONDS |
30 |
Seconds between progress messages while an AI batch is in flight. Set 0 to disable. |
OLLAMA_REQUEST_TIMEOUT |
300 |
Seconds to wait for each Ollama request before retrying. |
OLLAMA_NUM_THREAD |
4 |
CPU threads used by Ollama generation. Raise it if the host has cores to spare. |
Run History
Section titled “Run History”The Web UI keeps the most recent 500 runs. Older run records are pruned by a periodic housekeeping job, along with their log entries; task log files are rotated separately by the runner. Export anything you need to retain long-term before it ages out.