skip to content

In Prefect, what does create_markdown_artifact attach to a flow run?

level: middleimportance: nice to knowfreq 25%

answer

  1. for the human reading the run afterwards
  2. not a log line, a rendered panel
  3. name it and it gains a history
  4. the payload sits with run metadata

basics

~20 s

It publishes a block of rendered markdown to the run in the Prefect UI — a row-count summary, a data-quality report, a diff. Give it a key and Prefect versions it, so you can page through the same artifact across every run.

solid answer

~40 s

`create_markdown_artifact` records a piece of rendered markdown against the current flow or task run, so the Prefect UI shows a formatted report next to the run rather than a line buried in logs. It sits alongside `create_link_artifact`, `create_table_artifact` and `create_progress_artifact` — the same idea for a URL, a tabular preview, and a progress indicator. The optional `key` is the interesting part: keyed artifacts get their own page and a version history, so publishing `"nightly-dq-report"` on every run lets you scroll back through previous runs' reports and see a metric drift. Artifacts live in the Prefect API alongside run metadata, not in your result storage, so keep them small and free of secrets or customer data — they are a human-readable summary of what a run did, not a data channel.

code

python · 24 lines
python
from prefect import flow, task
from prefect.artifacts import create_markdown_artifact, create_link_artifact

@task
def check(rows: int, nulls: int) -> None:
    pct = 100 * nulls / max(rows, 1)
    create_markdown_artifact(
        key="orders-dq",
        markdown=(
            "## Orders load\n\n"
            f"| metric | value |\n|---|---|\n"
            f"| rows | {rows} |\n| null keys | {nulls} ({pct:.2f}%) |"
        ),
        description="Nightly orders data-quality summary",
    )

@flow
def nightly():
    check(1_204_318, 12)
    create_link_artifact(
        key="orders-dashboard",
        link="https://example.internal/dashboards/orders",
        link_text="Orders dashboard",
    )

go deeper

for a junior

Know that artifacts render markdown, links, tables or progress in the Prefect UI against a run, and that they are published by calling a helper inside a flow or task body.

for a middle

Explain what the key buys you — versioned history for one named artifact across runs — and that artifacts are stored with run metadata rather than in the result storage used for persisted task results.

for a senior

Show the operating habit: one keyed summary per meaningful outcome so operators open a page instead of scrolling logs, plus discipline about size and about never publishing secrets or raw records.

for a principal

Own the observability contract — which run outcomes must be legible to non-engineers, what belongs in artifacts versus a real BI surface, and the governance limits on what run metadata may contain.

## What an artifact is An **artifact** in Prefect is a small, structured, human-facing record attached to a flow run or task run and rendered in the UI. Logs answer "what happened, line by line"; artifacts answer "what should a person look at when this run finishes". They are published from inside your code by calling a helper, which associates the payload with whichever run is currently executing. ## The helpers - `create_markdown_artifact(markdown=..., key=..., description=...)` — a formatted block: headings, bullets, tables, bold numbers. - `create_link_artifact(link=..., link_text=..., key=...)` — a clickable URL, ideal for the dashboard, the report file, or the warehouse query this run produced. - `create_table_artifact(table=..., key=...)` — a list of dicts (or a dict of lists) rendered as a table, good for a small preview or a per-partition row count. - `create_progress_artifact(...)` with `update_progress_artifact(...)` — a progress indicator for a long loop, so an operator can see how far a run has got without reading logs. All of them are called from inside a `@flow` or `@task` body; the current run context is what ties the artifact to the run. ```python from prefect import flow from prefect.artifacts import create_markdown_artifact @flow def nightly(): rows, nulls = load() create_markdown_artifact( key="nightly-dq-report", markdown=f"# Nightly load\n\n- rows: **{rows}**\n- null keys: **{nulls}**", description="Row and null counts for the nightly orders load", ) ``` ## Why the key matters Without a `key`, the artifact is simply attached to that one run and you find it by opening the run. With a `key`, Prefect treats successive publications as **versions of the same artifact**: the UI gives the key its own page listing every version with the run that produced it. That turns a one-off summary into a time series a human can scan — last night's row count next to the previous seven, the freshness check that has been drifting, the link to each run's output file. Keys are meant to be stable, lowercase, dash-separated identifiers; changing the key starts a new history. ## Where artifacts live Artifacts are stored with run metadata in the Prefect API — the same place states, logs and run records live — not in the result storage where persisted task results go. Two consequences follow. First, size discipline: a markdown artifact is a summary, not a data dump; publishing thousands of rows per run bloats the metadata store and makes the UI unpleasant. Second, sensitivity: anything you put in an artifact is visible to everyone who can see the workspace, and in Prefect Cloud it leaves your infrastructure in a way that persisted result *data* deliberately does not. Never publish credentials, tokens, or raw customer records; publish counts, ratios, and links to where the real data sits. ## When to reach for an artifact rather than a log Use a log line for the running narrative — "connected", "read 12 files", "retrying". Use an artifact when a person will want the summary *after* the run, or will want to compare it against previous runs: data-quality checks, validation results, a diff of what a job would change, the URL of the report it wrote, the parameters of a model it trained. A well-run Prefect deployment typically publishes one keyed artifact per meaningful run outcome, and operators learn to open that page instead of scrolling logs. ## Artifacts and states Artifacts are independent of state transitions — they are not hooks and do not affect whether a run is Completed or Failed. It is common and useful to publish one on the failure path too (inside an `on_failure` hook or an `except` branch), so the run that failed carries a readable explanation of *what* it found rather than only a traceback. Because publication is a normal call inside your code, an artifact is only written if the code reaches it: a Crashed run publishes nothing, which is another reason durable alerting belongs in server-side automations rather than in the artifact itself.

  • What changes when you give an artifact a key?
    Successive publications under the same key become versions of one artifact with its own page in the UI, listing each version and the run that produced it. That turns a per-run summary into a scannable history, which is how teams spot a row count or freshness metric drifting over a week. Without a key the artifact is reachable only from its own run.
  • Should large query results be published as a table artifact?
    No. Artifacts live with run metadata in the Prefect API and are meant as human-readable summaries, so a big table bloats the metadata store and makes the UI unusable. Publish counts, ratios, or a small preview, plus a link artifact pointing at the file or warehouse table that holds the real output.
  • Can an artifact be published from a failing run?
    Yes — publishing is an ordinary call, so an except branch or an on_failure hook can publish a markdown artifact describing what the run found before it gave up. It will not be written if the process is killed outright, which is why crash-proof alerting belongs in server-side automations rather than only in artifact calls.

saying these in an interview costs you the question

  • Treats artifacts as a place to store result data
  • Publishes customer records or credentials into markdown
  • Thinks an unkeyed artifact still builds a history
  • Confuses artifacts with state-change hooks
  • Assumes a crashed run still publishes its artifact

context