Delete Old Vercel Production Deployments
Deleting old vercel production deployments seems to not be recommended by them, but there are usecases where this can be the only right thing to do.
Having tons of vercel deployments doesn’t matter in itself. They don’t generate cost or count toward any limits.
The problem comes if they still have access to your production database. You don’t want old code interfering with your production database.
Here is a script to delete vercel production deployments:
/*
This script deletes production deployments with no aliases. This means that a current live production build should not be deleted.
As an extra precaution, it only gets deployments that are more than 7 days old.
Use at your own risk
*/
import axios, { AxiosError } from "axios"
const token = process.env.VERCEL_ACCESS_TOKEN
if (!token) {
throw new Error("No token")
}
const base = "https://api.vercel.com"
let dryRun = true
if (process.env.NO_DRY_RUN === "true") {
dryRun = false
}
const team_id = "team_XXX"
const project_id = "prj_XXX"
const vercelRestRequest = async <T>(
method: "GET" | "DELETE",
endpoint: string,
otherParams?: Record<string, string | number>,
) => {
const config = {
headers: {
Authorization: "Bearer " + token,
},
params: {
teamId: team_id,
projectId: project_id,
...otherParams,
},
}
const axiosFn = method === "DELETE" ? axios.delete : axios.get
return axiosFn<T>(base + endpoint, config)
.catch((error: Error | AxiosError) => {
if (axios.isAxiosError(error)) {
console.log(" error.response?.data: ", error.response?.data)
} else {
throw error
}
})
.then((e) => {
if (e) {
return e.data
} else {
throw new Error("no data")
}
})
}
const getAliases = async (deploymentUid: string) => {
const res = await vercelRestRequest<{ aliases: Alias[] }>(
"GET",
`/v2/deployments/${deploymentUid}/aliases`,
)
return res.aliases
}
const deleteDeployment = async (deploymentUid: string) => {
const res = await vercelRestRequest<{ uid: string; state: "DELETED" }>(
"DELETE",
`/v13/deployments/${deploymentUid}`,
)
return res
}
const go = async () => {
const aWeekAgo = new Date(new Date().setDate(new Date().getDate() - 7))
const { deployments } = await vercelRestRequest<{
deployments: Deployment[]
}>("GET", "/v6/deployments", {
target: "production",
until: aWeekAgo.getTime(),
})
for (const deployment of deployments) {
const aliases = await getAliases(deployment.uid)
if (aliases.length === 0) {
console.log(
"❌ deleting deployment: ",
deployment.url,
"aliases: ",
aliases.length,
dryRun ? "(dry run)" : "",
)
if (!dryRun) {
await deleteDeployment(deployment.uid)
}
} else {
console.log(
"🌳 not deleting deployment: ",
deployment.url,
"aliases: ",
aliases.length,
)
}
}
}
void go()
// types stolen from cli package (https://github.dev/vercel/vercel/tree/main/packages/cli)
export type Alias = {
uid: string
alias: string
createdAt: number
deployment: {
id: string
url: string
}
creator: {
uid: string
username: string
email: string
}
deploymentId?: string
}
export type Deployment = {
uid: string
url: string
name: string
type: "LAMBDAS"
state: "BUILDING" | "ERROR" | "INITIALIZING" | "QUEUED" | "READY" | "CANCELED"
version?: number
created: number
createdAt: number
creator: { uid: string; username: string }
target: string | null
ownerId: string
projectId: string
inspectorUrl: string
meta: {
[key: string]: unknown
}
}
Find project_id
and team_id
on Vercel’s team settings and project settings. Create an access token in your account settings.
To run the script, execute this command:
NO_DRY_RUN=true ACCESS_TOKEN=xxx ts-node deleteOldVercelDeployments.ts
Hope this was helpful!