Important considerations
-
You must specify --clean-before-timestamp to control how far back the cleanup reaches. Use an ISO 8601 formatted timestamp (for example, 2025-01-01T00:00:00+00:00).
-
We recommend that you specify --tables to limit the cleanup to specific tables. If omitted, the command cleans all supported tables.
-
Start with a small scope — use an older --clean-before-timestamp value (closer to your environment creation date) and a single table first. This limits the cleanup to only the oldest records. Because the command deletes everything before the specified timestamp, using a more recent timestamp results in a larger deletion scope. Gradually move the timestamp forward as you gain confidence in the process.
-
Large-scale cleanup can impact database performance. Deleting a high volume of records puts pressure on the Aurora PostgreSQL database and might affect the responsiveness of your environment. Use the --batch-size parameter to control transaction size, and consider running cleanup during low-traffic periods. Exercise caution when running on production environments.
Dependent table behavior
When you specify a table with --tables, the command automatically includes any dependent (child) tables that have foreign key relationships with the specified table. Child table records are deleted first, then the parent table records, to satisfy foreign key constraints. For example, specifying --tables dag_run also cleans task_instance, task_instance_history, xcom, task_state_store, and deadline because these tables reference dag_run through foreign keys.
The following table summarizes the dependency chains.
| Table specified |
Additional tables cleaned (dependents) |
dag_run |
task_instance, task_instance_history, xcom, task_state_store, deadline |
dag |
dag_version, deadline |
task_instance |
task_instance_history, xcom |
trigger |
task_instance, task_instance_history, xcom |
dag_version |
task_instance, task_instance_history, xcom, dag_run |
Tables without dependents (such as log, job, import_error, sla_miss) are cleaned in isolation when specified.
Use --dry-run to see exactly which tables and how many rows would be affected before committing to a cleanup.
Available parameters for airflow db clean
The following table describes the available parameters for airflow db clean.
| Parameter |
Description |
Default |
--clean-before-timestamp |
(Required) The date or timestamp before which data is purged. If no timezone is supplied, the Apache Airflow default timezone is assumed. Example: 2025-01-01T00:00:00+00:00 |
None |
--tables or -t |
Table names to perform maintenance on (comma-separated). Options include: dag_run, task_instance, task_instance_history, log, job, xcom, import_error, task_reschedule, trigger, dag, dag_version, sla_miss, callback_request, celery_taskmeta, celery_tasksetmeta, asset_event, deadline, revoked_token, task_state_store, connection_test_request, _xcom_archive |
None |
--batch-size |
Maximum number of rows to delete or archive in a single transaction. Lower values reduce long-running locks but increase the number of batches. |
None |
--dry-run |
Perform a dry run without actually deleting data. Recommended for initial testing. |
False |
--skip-archive |
Don't preserve purged records in an archive table. By default, db clean moves purged records into archive tables (named with a _<table>_archive convention, for example _dag_run_archive) instead of permanently deleting them. This provides a safety net — you can inspect archived data, export it with airflow db export-archived, or drop it later with airflow db drop-archived. When --skip-archive is set, records are permanently deleted without this intermediate step. |
False |
--dag-ids |
Only cleanup data related to the given DAG IDs. |
None |
--exclude-dag-ids |
Avoid cleaning up data related to the given DAG IDs. |
None |
-y, --yes |
Skip the confirmation prompt. Required for non-interactive CLI execution through Amazon MWAA. |
False |
-v, --verbose |
Make logging output more verbose. |
False |
For more information about the available parameters, see CLI and env variables reference on the Apache Airflow website.
Code samples
The following examples show how to invoke airflow db clean through the Amazon MWAA CLI endpoint. For more information about creating CLI tokens, see Creating an Apache Airflow CLI token.
Using a Python script:
import boto3
import base64
import requests
# Replace with your environment name and AWS Region
mwaa_env_name = "YOUR_ENVIRONMENT_NAME"
region = "YOUR_REGION"
# Configure cleanup scope
clean_before_timestamp = "2025-06-01T00:00:00+00:00"
tables = "dag_run,task_instance,log,job,xcom"
# Build the Airflow CLI command
# -y flag is required to skip interactive confirmation prompt
airflow_cmd = f"db clean --clean-before-timestamp {clean_before_timestamp} --tables {tables} -y"
# Create a CLI token
client = boto3.client("mwaa", region_name=region)
cli_token_response = client.create_cli_token(Name=mwaa_env_name)
cli_token = cli_token_response["CliToken"]
web_server_hostname = cli_token_response["WebServerHostname"]
# Invoke the Airflow CLI through the MWAA endpoint
url = f"https://{web_server_hostname}/aws_mwaa/cli"
response = requests.post(
url,
headers={
"Authorization": f"Bearer {cli_token}",
"Content-Type": "text/plain",
},
data=airflow_cmd,
)
# Parse and display the results
stdout_message = base64.b64decode(response.json()["stdout"]).decode("utf-8")
stderr_message = base64.b64decode(response.json()["stderr"]).decode("utf-8")
print(f"Status code: {response.status_code}")
print(f"stdout:\n{stdout_message}")
print(f"stderr:\n{stderr_message}")
Dry run example (recommended first step)
Before performing an actual cleanup, run with --dry-run to see what would be deleted:
AIRFLOW_CMD="db clean --clean-before-timestamp 2025-06-01T00:00:00+00:00 --tables dag_run,task_instance --dry-run -y"
from airflow import DAG
from airflow.models.param import Param
from airflow.operators.bash_operator import BashOperator
from airflow.utils.dates import days_ago
from datetime import datetime, timedelta
# Note: Database commands might time out if running longer than 5 minutes. If this occurs, please increase the MAX_AGE_IN_DAYS (or change
# timestamp parameter to an earlier date) for initial runs, then reduce on subsequent runs until the desired retention is met.
MAX_AGE_IN_DAYS = 30
# To clean specific tables, please provide a comma-separated list per
# https://airflow.apache.org/docs/apache-airflow/stable/cli-and-env-variables-ref.html#clean
# A value of None will clean all tables
TABLES_TO_CLEAN = None
with DAG(
dag_id="clean_db_dag",
schedule_interval=None,
catchup=False,
start_date=days_ago(1),
params={
"timestamp": Param(
default=(datetime.now()-timedelta(days=MAX_AGE_IN_DAYS)).strftime("%Y-%m-%d %H:%M:%S"),
type="string",
minLength=1,
maxLength=255,
),
}
) as dag:
if TABLES_TO_CLEAN:
bash_command="airflow db clean --clean-before-timestamp '{{ params.timestamp }}' --tables '"+TABLES_TO_CLEAN+"' --skip-archive --yes"
else:
bash_command="airflow db clean --clean-before-timestamp '{{ params.timestamp }}' --skip-archive --yes"
cli_command = BashOperator(
task_id="bash_command",
bash_command=bash_command
)