Building an AI Agent That Cleans Up Unused Resources on Deployxa
Over time, your Deployxa account accumulates unused resources: apps that were created for testing and never deleted, environment variables that are no longer used, stale deployments that are taking up storage, and unused custom domains. These resources waste money, clutter the dashboard, and make it harder to find the resources that matter. Manual cleanup is tedious and error-prone, because you have to check each resource to determine if it is still used. What you need is an AI agent that identifies unused resources and cleans them up automatically. With the Deployxa MCP server and an LLM, this is straightforward to build. Here is how.
The direct answer is that a cleanup agent is a script that runs on a schedule (e.g., weekly), lists all resources (apps, environment variables, deployments, domains), identifies unused ones (e.g., apps with no traffic in the past 30 days, environment variables that are not referenced in the code, deployments that are older than the rollback window), and either deletes them (for clearly unused resources) or flags them for review (for potentially unused resources). The agent uses an LLM to interpret the data and make cleanup decisions, which means it can handle ambiguous cases (e.g., "this app has no traffic, but it was deployed recently, which suggests it might be a staging app that is not yet in use"). For more on resource management, see our article on the cost optimization engine.
Why Manual Cleanup Does Not Scale
Three problems make manual cleanup unsustainable. First, it is tedious. Checking each app, each environment variable, and each deployment to determine if it is still used is repetitive work that takes time. Second, it is error-prone. You might delete a resource that is still used (causing an outage) or keep a resource that is not used (wasting money). Third, it does not happen. Cleanup is a low-priority task, which means it gets postponed indefinitely, and the resources accumulate. An AI agent solves all three problems: it is automatic (no manual work), reliable (it verifies before deleting), and scheduled (it runs regularly). For more on agentic patterns, see our article on building an AI agent that monitors your app 24/7.
What the Cleanup Agent Does
The cleanup agent performs the following tasks on a schedule (e.g., weekly):
- List all apps. The agent calls deployxa_list_apps to get all apps.
- Identify unused apps. The agent checks each app's traffic (via deployxa_get_metrics) and deployment history. Apps with no traffic in the past 30 days and no recent deployments are flagged as potentially unused.
- List all environment variables. The agent calls deployxa_get_env_vars for each app to get all environment variables.
- Identify unused environment variables. The agent checks if each environment variable is referenced in the app's code (by scanning the repository). Variables that are not referenced are flagged as potentially unused.
- List all deployments. The agent calls deployxa_get_deployment_history for each app to get all deployments.
- Identify stale deployments. The agent checks each deployment's age. Deployments that are older than the rollback window (e.g., 7 days) are flagged as stale.
- Clean up or flag. The agent either deletes clearly unused resources (e.g., an app with no traffic in 90 days) or flags potentially unused resources for review (e.g., an app with no traffic in 30 days).
- Report. The agent sends a Slack notification with the cleanup summary (what was deleted, what was flagged, estimated savings).
Step-by-Step: Building the Cleanup Agent
Here is how to build the cleanup agent in Python, using the Deployxa MCP server and an LLM.
Step 1: Install dependencies
pip install openai requests schedule
npm install -g @deployxa/mcp-server
deployxa-mcp loginStep 2: Create the cleanup script
# cleanup.py
import openai
import requests
import schedule
import time
import json
from datetime import datetime, timedelta
# Configuration
SLACK_WEBHOOK_URL = "https://hooks.slack.com/services/..."
DEPLOYXA_MCP_URL = "http://localhost:3000/mcp"
CHECK_INTERVAL_DAYS = 7 # Run weekly
# Thresholds
UNUSED_APP_DAYS = 30 # Apps with no traffic for 30 days are flagged
STALE_DEPLOYMENT_DAYS = 7 # Deployments older than 7 days are stale
def call_deployxa(tool, params):
"""Call a Deployxa MCP tool."""
response = requests.post(
DEPLOYXA_MCP_URL,
json={"tool": tool, "params": params},
)
return response.json()
def send_slack_notification(message):
"""Send a Slack notification."""
requests.post(SLACK_WEBHOOK_URL, json={"text": message})
def get_unused_apps():
"""Identify apps that are potentially unused."""
apps = call_deployxa("deployxa_list_apps", {})
unused_apps = []
for app in apps.get("apps", []):
# Get metrics for the past 30 days
metrics = call_deployxa("deployxa_get_metrics", {
"app_id": app["id"],
"period": "30d",
})
traffic = metrics.get("total_requests", 0)
if traffic == 0:
unused_apps.append({
"id": app["id"],
"name": app["name"],
"last_deployed": app.get("last_deployed", "unknown"),
"traffic_30d": traffic,
})
return unused_apps
def get_unused_env_vars(app_id, app_name):
"""Identify environment variables that are not referenced in the code."""
env_vars = call_deployxa("deployxa_get_env_vars", {"app_id": app_id})
# In a real implementation, you would scan the app's repository
# to check if each env var is referenced. For this example, we'll
# use a simplified approach.
unused_vars = []
for var in env_vars.get("vars", []):
# Check if the var name appears in the repository
# (This would require cloning the repo and grepping)
# For this example, we'll flag vars that look like they might be unused
if var["key"].endswith("_OLD") or var["key"].endswith("_BACKUP") or var["key"].endswith("_TEST"):
unused_vars.append(var["key"])
return unused_vars
def get_stale_deployments(app_id):
"""Identify deployments that are older than the rollback window."""
deployments = call_deployxa("deployxa_get_deployment_history", {"app_id": app_id})
stale_deployments = []
cutoff = datetime.utcnow() - timedelta(days=STALE_DEPLOYMENT_DAYS)
for deployment in deployments.get("deployments", []):
deployed_at = datetime.fromisoformat(deployment["deployed_at"].replace("Z", ""))
if deployed_at < cutoff:
stale_deployments.append({
"id": deployment["id"],
"deployed_at": deployment["deployed_at"],
"age_days": (datetime.utcnow() - deployed_at).days,
})
return stale_deployments
def analyze_with_llm(unused_apps, unused_env_vars, stale_deployments):
"""Use an LLM to analyze the unused resources and recommend actions."""
client = openai.OpenAI()
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{
"role": "system",
"content": """You are a resource cleanup agent. Analyze the unused resources and recommend actions.
Return a JSON object with:
- "delete": a list of resources to delete (clearly unused, safe to delete)
- "flag": a list of resources to flag for review (potentially unused, needs human review)
- "keep": a list of resources to keep (might be unused but should not be deleted)
- "estimated_savings": estimated monthly savings from deleting the flagged resources
Guidelines:
- Only recommend deleting apps with no traffic for 90+ days
- Flag apps with no traffic for 30-89 days for review
- Recommend deleting env vars that are clearly unused (e.g., _OLD, _BACKUP suffixes)
- Recommend cleaning up stale deployments older than 7 days
- Be conservative: when in doubt, flag for review rather than delete
"""
},
{
"role": "user",
"content": f"""Unused apps (no traffic in 30 days):
{json.dumps(unused_apps, indent=2)}
Unused environment variables:
{json.dumps(unused_env_vars, indent=2)}
Stale deployments (older than {STALE_DEPLOYMENT_DAYS} days):
{json.dumps(stale_deployments, indent=2)}
"""
}
]
)
return json.loads(response.choices[0].message.content)
def delete_resource(resource_type, resource_id):
"""Delete a resource."""
if resource_type == "app":
return call_deployxa("deployxa_delete_app", {"app_id": resource_id, "confirmed": True})
elif resource_type == "env_var":
return call_deployxa("deployxa_delete_env_var", {"env_var_id": resource_id})
elif resource_type == "deployment":
return call_deployxa("deployxa_delete_deployment", {"deployment_id": resource_id})
def run_cleanup():
"""Run the full cleanup pipeline."""
print(f"[{datetime.utcnow().isoformat()}] Running resource cleanup...")
# Step 1: Get unused apps
unused_apps = get_unused_apps()
print(f" Found {len(unused_apps)} potentially unused apps")
# Step 2: Get unused env vars (for each app)
all_unused_env_vars = []
for app in unused_apps:
unused_vars = get_unused_env_vars(app["id"], app["name"])
if unused_vars:
all_unused_env_vars.append({
"app": app["name"],
"vars": unused_vars,
})
print(f" Found {len(all_unused_env_vars)} apps with unused env vars")
# Step 3: Get stale deployments (for each app)
all_stale_deployments = []
for app in unused_apps:
stale = get_stale_deployments(app["id"])
if stale:
all_stale_deployments.append({
"app": app["name"],
"deployments": stale,
})
print(f" Found {len(all_stale_deployments)} apps with stale deployments")
# Step 4: Analyze with LLM
analysis = analyze_with_llm(unused_apps, all_unused_env_vars, all_stale_deployments)
# Step 5: Delete resources (for clearly unused)
deleted = []
for resource in analysis.get("delete", []):
result = delete_resource(resource["type"], resource["id"])
deleted.append(resource)
# Step 6: Report
message = f"🧹 Resource cleanup complete!\n\n"
message += f"Deleted: {len(deleted)} resources\n"
message += f"Flagged for review: {len(analysis.get('flag', []))} resources\n"
message += f"Estimated savings: ${analysis.get('estimated_savings', 0)}/month\n\n"
if analysis.get("flag"):
message += "Flagged for review:\n"
for item in analysis["flag"][:10]: # Show first 10
message += f" - {item}\n"
send_slack_notification(message)
print(f"\n{message}")
# Schedule the cleanup
schedule.every(CHECK_INTERVAL_DAYS).days.do(run_cleanup)
# Run immediately, then on schedule
run_cleanup()
while True:
schedule.run_pending()
time.sleep(1)Step 3: Run the cleanup agent
python cleanup.pyThe agent runs immediately, then every 7 days. It identifies unused resources, deletes clearly unused ones, flags potentially unused ones, and reports via Slack.
Step 4: Deploy the cleanup agent
The agent should run on a server (not your laptop), so it runs 24/7. You can deploy it as a Deployxa app (a Python script that runs in a persistent container).
Common Pitfalls and Troubleshooting
The first pitfall is deleting resources that are still used. If the agent incorrectly identifies a resource as unused and deletes it, it can cause an outage. The fix is to be conservative: only delete resources that are clearly unused (e.g., apps with no traffic for 90+ days), and flag everything else for review. The second pitfall is not verifying before deleting. The agent should verify that a resource is unused (e.g., check traffic, check code references) before deleting it. The fix is to implement thorough verification logic. The third pitfall is not having a recovery mechanism. If the agent deletes a resource that was actually needed, you need a way to recover it. The fix is to back up resources before deleting them (e.g., export environment variables, save deployment artifacts) and to keep the backups for a period (e.g., 30 days). The fourth pitfall is alert fatigue. If the agent flags too many resources for review, the team will start ignoring the alerts. The fix is to only flag resources that are genuinely ambiguous, and to batch the flags into a weekly summary. The fifth pitfall is not testing the agent. The agent's deletion logic is critical, which means it needs to be tested. The fix is to test the agent in a staging environment before using it in production. For more on testing, see our article on the testing void.
Advanced Cleanup Patterns
Beyond the basics, the cleanup agent can be extended with several advanced patterns. The first is dependency analysis. The agent can analyze dependencies between resources (e.g., "this app uses this database, so do not delete the database while the app exists") and avoid deleting resources that are still needed. The second is cost analysis. The agent can calculate the cost of each resource and prioritize cleanup by cost (e.g., delete the most expensive unused resources first). The third is compliance. The agent can check if resources are subject to compliance requirements (e.g., "this app handles user data, so it cannot be deleted without approval") and flag them for manual review. The fourth is scheduled cleanup. The agent can schedule cleanup for off-peak hours (e.g., 3 AM on Sunday) to minimize impact. The fifth is integration with ticket systems. The agent can create tickets for flagged resources, so the team can review and approve deletions via their normal workflow. For more on advanced patterns, see our articles on the agentic cost optimization pipeline and building a custom AI deployment assistant.
Conclusion: Let the Agent Clean Up for You
Manual cleanup is tedious, error-prone, and does not happen. An AI agent that identifies unused resources and cleans them up is the sustainable solution. With the Deployxa MCP server and an LLM, you can build a cleanup agent that runs regularly, identifies unused resources, deletes clearly unused ones, and flags potentially unused ones for review. Stop accumulating unused resources and start letting the agent clean them up.
Ready to build your cleanup agent? Install the Deployxa MCP server with npm i -g @deployxa/mcp-server, run deployxa-mcp login, and start building. For more on agentic workflows, see our articles on the agentic blue/green deployment pipeline and the agentic cost optimization pipeline. Learn about building a custom AI deployment assistant and the agentic incident response pipeline in our companion articles. Explore our free developer tools to speed up your workflow.