<@ULVA73B9P> tell me about the new db vacuum featu...
# ask-marvin
p
@Marvin tell me about the new db vacuum feature and how I would implement it with .serve to clean weekly
m
âś… thought for 321.8 seconds
n
you shouldn't need to implement, it runs as a background service along with the server. you'd just need to enable it
p
sorry the docs are confusing - is it just an env var?
n
yes! i will get a guide up soon, this was just contributed by the community in the last week
🙂 1
m
Here’s a quick rundown of the new DB vacuum service in Prefect 3.x and how to run it weekly. What it is - A server-level background service that periodically cleans up: - Old terminal top-level flow runs past a retention period - Orphaned logs - Orphaned artifacts - Stale artifact collections (where latest points to a deleted artifact) - It’s disabled by default (because it permanently deletes data). - Runs as part of Prefect Server’s “perpetual services” (no flow code needed). How to enable it and run weekly (recommended) - Enable the service and set it to run every 7 days:
Copy code
prefect config set PREFECT_SERVER_SERVICES_DB_VACUUM_ENABLED=true
prefect config set PREFECT_SERVER_SERVICES_DB_VACUUM_LOOP_SECONDS=604800
# optional: change retention (default is 90 days)
prefect config set PREFECT_SERVER_SERVICES_DB_VACUUM_RETENTION_PERIOD=7776000
# optional: batch size per transaction (default 200)
prefect config set PREFECT_SERVER_SERVICES_DB_VACUUM_BATCH_SIZE=200
- Then run your Prefect Server as usual. The vacuum will run in the background on that cadence. Notes - Runs at the server level; there’s no separate CLI to “manually trigger” it. - Tune LOOP_SECONDS (frequency), RETENTION_PERIOD (in seconds), and BATCH_SIZE to your needs. - Test in non-prod and back up your DB before enabling—this deletes data. If you specifically want to use flow.serve to schedule weekly You don’t need .serve for the built-in vacuum (it’s a server service), but if you want a visible, deployment-style weekly job (e.g., to delete old runs), you can implement a small retention flow and schedule it weekly with .serve:
Copy code
from datetime import datetime, timedelta, timezone
import asyncio

from prefect import flow, task, get_run_logger
from prefect.client.orchestration import get_client
from prefect.client.schemas import StateType
from prefect.client.schemas.filters import (
    FlowRunFilter,
    FlowRunFilterStartTime,
    FlowRunFilterState,
    FlowRunFilterStateType,
    FlowRunFilterParentFlowRunId,
)

@task
async def delete_old_top_level_flow_runs(days_to_keep: int = 90, batch_size: int = 200):
    logger = get_run_logger()
    cutoff = datetime.now(timezone.utc) - timedelta(days=days_to_keep)

    async with get_client() as client:
        flow_run_filter = FlowRunFilter(
            start_time=FlowRunFilterStartTime(before_=cutoff),
            state=FlowRunFilterState(
                type=FlowRunFilterStateType(
                    any_=[StateType.COMPLETED, StateType.FAILED, StateType.CANCELLED]
                )
            ),
            parent_flow_run_id=FlowRunFilterParentFlowRunId(is_null_=True),
        )

        total_deleted = 0
        while True:
            runs = await client.read_flow_runs(flow_run_filter=flow_run_filter, limit=batch_size)
            if not runs:
                break

            for fr in runs:
                try:
                    await client.delete_flow_run(fr.id)
                    total_deleted += 1
                except Exception as e:
                    logger.warning(f"Failed to delete {fr.id}: {e}")

            await asyncio.sleep(0)  # yield

        <http://logger.info|logger.info>(f"Retention complete. Deleted {total_deleted} flow runs")

@flow(name="db-retention")
async def db_retention_flow(days_to_keep: int = 90):
    await delete_old_top_level_flow_runs(days_to_keep=days_to_keep)

if __name__ == "__main__":
    # Weekly on Sundays at 03:00
    db_retention_flow.serve(
        name="db-retention-weekly",
        cron="0 3 * * 0",
        parameters={"days_to_keep": 90},
    )
    # Alternatively, use a 7-day interval:
    # from datetime import timedelta
    # db_retention_flow.serve(name="db-retention-weekly", interval=timedelta(days=7))
When to choose which - Prefer the built-in DB vacuum service if you just want reliable automatic cleanup. It also handles orphaned logs/artifacts and stale artifact collections.
- Use a .serve’d flow if you want deployment-style visibility, custom logic, or different rules per environment. Docs and release notes - Database maintenance overview (includes retention guidance): Prefect Database Maintenance - 3.x release notes: Prefect 3.x Release Notes If you share your target retention window and where you’re running Prefect (local Docker, K8s, Cloud, etc.), I can suggest exact settings and manifests.