Set up automated cleanup policies for GitLab container registry to manage storage costs and remove unused container images. This tutorial covers UI configuration, API automation, and monitoring for production environments.
Prerequisites
- GitLab instance with container registry enabled
- GitLab admin or maintainer access
- Personal access token with API scope
- Basic knowledge of container registries and GitLab CI/CD
What this solves
GitLab container registries can quickly consume significant storage space as Docker images accumulate from CI/CD pipelines and development workflows. Without proper cleanup policies, old and unused images pile up, leading to increased storage costs and slower registry operations. This tutorial shows you how to configure automated cleanup policies that remove unused images based on age, tags, and retention rules while preserving production images.
Understanding GitLab container registry cleanup policies
GitLab cleanup policies work at the project level and can target specific image repositories within your container registry. The cleanup system evaluates images based on several criteria including tag patterns, image age, and the number of images to retain.
Cleanup policies support different rule types that determine which images get removed. Tag-based rules match specific tag patterns like feature- or dev-. Age-based rules remove images older than a specified number of days. Count-based rules keep only the most recent N images that match the criteria.
Step-by-step configuration
Access project cleanup policy settings
Navigate to your GitLab project and access the container registry cleanup configuration through the project settings menu.
In your GitLab project, go to Settings > Packages and registries. Scroll down to the Cleanup policy for tags section. This is where you'll configure all cleanup rules for your project's container registry.
Configure basic cleanup policy
Set up a basic cleanup policy that removes old development and feature branch images while preserving production tags.
Enable the cleanup policy by toggling the Cleanup policy switch. Set the following basic configuration:
- Expiration interval: 7 days (removes images older than 7 days that match other criteria)
- Expiration schedule: Every day at 06:00 UTC
- Number of tags to retain: 10 (keeps the 10 most recent tags that match patterns)
- Tags with names matching this regex will expire:
.*(matches all tags) - Tags with names matching this regex will be preserved:
^(main|master|production|release-.*|v\d+\.\d+\.\d+)$
This configuration removes images older than 7 days while always preserving main, master, production, release, and semantic version tags.
Add advanced cleanup rules
Create more specific rules for different types of images in your registry to optimize storage usage.
Click Add another rule to create additional cleanup policies. Configure separate rules for different image categories:
Rule for feature branches:
- Tags matching:
^feature-.* - Expiration interval: 3 days
- Keep N tags: 5
Rule for development images:
- Tags matching:
^(dev|develop|test)-.* - Expiration interval: 5 days
- Keep N tags: 3
Rule for pull request images:
- Tags matching:
^pr-\d+ - Expiration interval: 1 day
- Keep N tags: 2
Configure cleanup via GitLab API
Use the GitLab API to programmatically manage cleanup policies across multiple projects for consistent configuration.
First, create a personal access token with api scope in GitLab under User Settings > Access Tokens. Then use the API to configure cleanup policies:
#!/bin/bash
GITLAB_TOKEN="your_personal_access_token"
GITLAB_URL="https://gitlab.example.com"
PROJECT_ID="123"
Get current cleanup policy
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
"$GITLAB_URL/api/v4/projects/$PROJECT_ID/registry/repositories"
Update cleanup policy
curl --request PUT \
--header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
--header "Content-Type: application/json" \
--data '{
"container_expiration_policy_attributes": {
"enabled": true,
"cadence": "1d",
"older_than": "7d",
"keep_n": 10,
"name_regex": ".*",
"name_regex_keep": "^(main|master|production|release-.*|v[0-9]+\.[0-9]+\.[0-9]+)$"
}
}' \
"$GITLAB_URL/api/v4/projects/$PROJECT_ID"
Set up CLI-based cleanup automation
Create a script that manages cleanup policies across multiple projects using GitLab's API for bulk operations.
Create a cleanup management script for consistent policy deployment:
#!/bin/bash
set -euo pipefail
Configuration
GITLAB_TOKEN="${GITLAB_TOKEN:-}"
GITLAB_URL="${GITLAB_URL:-https://gitlab.example.com}"
CONFIG_FILE="${CONFIG_FILE:-/opt/cleanup-config.json}"
if [[ -z "$GITLAB_TOKEN" ]]; then
echo "Error: GITLAB_TOKEN environment variable is required"
exit 1
fi
Function to apply cleanup policy to a project
apply_cleanup_policy() {
local project_id="$1"
local policy_config="$2"
echo "Applying cleanup policy to project $project_id..."
response=$(curl --silent --request PUT \
--header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
--header "Content-Type: application/json" \
--data "$policy_config" \
"$GITLAB_URL/api/v4/projects/$project_id" || echo "")
if [[ -n "$response" ]]; then
echo "✓ Policy applied to project $project_id"
else
echo "✗ Failed to apply policy to project $project_id"
fi
}
Function to get registry storage usage
get_registry_usage() {
local project_id="$1"
curl --silent --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
"$GITLAB_URL/api/v4/projects/$project_id/registry/repositories" | \
jq -r '.[] | "Repository: \(.path) | Size: \(.size_bytes // "unknown") bytes"'
}
Read projects and apply policies
if [[ -f "$CONFIG_FILE" ]]; then
jq -r '.projects[] | "\(.id) \(.cleanup_policy | tostring)"' "$CONFIG_FILE" | \
while read -r project_id policy_json; do
apply_cleanup_policy "$project_id" "$policy_json"
echo "Current registry usage for project $project_id:"
get_registry_usage "$project_id"
echo "---"
done
else
echo "Config file $CONFIG_FILE not found"
exit 1
fi
Create the configuration file for your cleanup policies:
{
"projects": [
{
"id": 123,
"name": "web-application",
"cleanup_policy": {
"container_expiration_policy_attributes": {
"enabled": true,
"cadence": "1d",
"older_than": "7d",
"keep_n": 10,
"name_regex": ".*",
"name_regex_keep": "^(main|master|production|release-.*|v[0-9]+\\.[0-9]+\\.[0-9]+)$"
}
}
},
{
"id": 456,
"name": "api-service",
"cleanup_policy": {
"container_expiration_policy_attributes": {
"enabled": true,
"cadence": "1d",
"older_than": "3d",
"keep_n": 5,
"name_regex": "^(feature-.|dev-.|test-.*)$",
"name_regex_keep": "^(main|production)$"
}
}
}
]
}
Make the script executable and run it:
sudo chmod +x /opt/gitlab-registry-cleanup.sh
export GITLAB_TOKEN="your_token_here"
export GITLAB_URL="https://gitlab.example.com"
/opt/gitlab-registry-cleanup.sh
Monitor registry storage usage
Set up monitoring to track storage usage and cleanup effectiveness across your GitLab container registries.
Create a monitoring script that tracks registry metrics:
#!/bin/bash
set -euo pipefail
GITLAB_TOKEN="${GITLAB_TOKEN:-}"
GITLAB_URL="${GITLAB_URL:-https://gitlab.example.com}"
METRICS_FILE="${METRICS_FILE:-/var/log/registry-metrics.log}"
Function to get project registry statistics
get_project_registry_stats() {
local project_id="$1"
local project_name="$2"
# Get project details
project_info=$(curl --silent --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
"$GITLAB_URL/api/v4/projects/$project_id")
# Get registry repositories
repositories=$(curl --silent --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
"$GITLAB_URL/api/v4/projects/$project_id/registry/repositories")
echo "=== Project: $project_name (ID: $project_id) ==="
echo "Last activity: $(echo "$project_info" | jq -r '.last_activity_at')"
total_size=0
repo_count=0
echo "$repositories" | jq -c '.[]' | while read -r repo; do
repo_id=$(echo "$repo" | jq -r '.id')
repo_path=$(echo "$repo" | jq -r '.path')
repo_size=$(echo "$repo" | jq -r '.size_bytes // 0')
# Get tags for this repository
tags=$(curl --silent --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
"$GITLAB_URL/api/v4/projects/$project_id/registry/repositories/$repo_id/tags" || echo '[]')
tag_count=$(echo "$tags" | jq 'length')
echo "Repository: $repo_path"
echo " Size: $repo_size bytes ($(numfmt --to=iec "$repo_size"))"
echo " Tags: $tag_count"
if [[ "$tag_count" -gt 0 ]]; then
echo " Recent tags:"
echo "$tags" | jq -r '.[:5][] | " - \(.name) (\(.created_at))"'
fi
echo ""
total_size=$((total_size + repo_size))
repo_count=$((repo_count + 1))
done
echo "Total repositories: $repo_count"
echo "Total size: $total_size bytes ($(numfmt --to=iec "$total_size"))"
# Log metrics in structured format
timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
echo "$timestamp,$project_id,$project_name,$repo_count,$total_size" >> "$METRICS_FILE"
}
Function to generate summary report
generate_summary() {
if [[ -f "$METRICS_FILE" ]]; then
echo "=== Storage Usage Summary ==="
echo "Date,Project_ID,Project_Name,Repositories,Total_Size_Bytes" | head -1
tail -10 "$METRICS_FILE"
echo ""
echo "Top 5 projects by storage usage:"
tail -50 "$METRICS_FILE" | sort -t',' -k5 -nr | head -5 | \
while IFS=',' read -r date proj_id proj_name repo_count size_bytes; do
size_human=$(numfmt --to=iec "$size_bytes")
echo " $proj_name (ID: $proj_id): $size_human ($repo_count repos)"
done
fi
}
Main execution
if [[ $# -gt 0 && "$1" == "--summary" ]]; then
generate_summary
exit 0
fi
Create metrics file with header if it doesn't exist
if [[ ! -f "$METRICS_FILE" ]]; then
echo "timestamp,project_id,project_name,repository_count,total_size_bytes" > "$METRICS_FILE"
fi
Monitor specific projects (add your project IDs here)
projects=(
"123:web-application"
"456:api-service"
"789:mobile-app"
)
for project in "${projects[@]}"; do
IFS=':' read -r project_id project_name <<< "$project"
get_project_registry_stats "$project_id" "$project_name"
echo ""
done
generate_summary
Set up automated cleanup scheduling
Configure systemd timers to run cleanup monitoring and policy enforcement on a regular schedule.
Create a systemd service for registry monitoring:
[Unit]
Description=GitLab Container Registry Monitoring
After=network.target
[Service]
Type=oneshot
User=gitlab-registry
Group=gitlab-registry
Environment=GITLAB_TOKEN=your_token_here
Environment=GITLAB_URL=https://gitlab.example.com
Environment=METRICS_FILE=/var/log/registry-metrics.log
ExecStart=/opt/registry-monitor.sh
ExecStartPost=/opt/registry-monitor.sh --summary
StandardOutput=journal
StandardError=journal
Create the corresponding timer:
[Unit]
Description=Run GitLab Registry Monitoring Daily
Requires=gitlab-registry-monitor.service
[Timer]
OnCalendar=daily
RandomizedDelaySec=1800
Persistent=true
[Install]
WantedBy=timers.target
Create the service user and enable the timer:
sudo useradd --system --shell /bin/false --home-dir /var/lib/gitlab-registry --create-home gitlab-registry
sudo mkdir -p /var/log /var/lib/gitlab-registry
sudo chown gitlab-registry:gitlab-registry /var/log/registry-metrics.log /var/lib/gitlab-registry
sudo chmod 755 /opt/registry-monitor.sh /opt/gitlab-registry-cleanup.sh
Enable and start the timer
sudo systemctl enable --now gitlab-registry-monitor.timer
sudo systemctl status gitlab-registry-monitor.timer
Configure cleanup policy alerts
Set up alerting to notify you when cleanup policies fail or when storage usage exceeds thresholds.
Create an alerting script that checks cleanup policy effectiveness:
#!/bin/bash
set -euo pipefail
GITLAB_TOKEN="${GITLAB_TOKEN:-}"
GITLAB_URL="${GITLAB_URL:-https://gitlab.example.com}"
ALERT_EMAIL="${ALERT_EMAIL:-admin@example.com}"
STORAGE_THRESHOLD_GB="${STORAGE_THRESHOLD_GB:-50}"
METRICS_FILE="/var/log/registry-metrics.log"
Function to send alert email
send_alert() {
local subject="$1"
local message="$2"
echo "$message" | mail -s "GitLab Registry Alert: $subject" "$ALERT_EMAIL"
echo "Alert sent: $subject"
}
Function to check storage thresholds
check_storage_thresholds() {
if [[ ! -f "$METRICS_FILE" ]]; then
send_alert "Metrics Missing" "Registry metrics file not found at $METRICS_FILE"
return 1
fi
# Get latest metrics (last 24 hours)
yesterday=$(date -d '1 day ago' -u +"%Y-%m-%d")
while IFS=',' read -r timestamp project_id project_name repo_count size_bytes; do
# Skip header line
[[ "$timestamp" == "timestamp" ]] && continue
# Check if entry is from last 24 hours
entry_date=$(echo "$timestamp" | cut -d'T' -f1)
[[ "$entry_date" < "$yesterday" ]] && continue
size_gb=$(echo "scale=2; $size_bytes / 1024 / 1024 / 1024" | bc)
if (( $(echo "$size_gb > $STORAGE_THRESHOLD_GB" | bc -l) )); then
alert_msg="Project '$project_name' (ID: $project_id) is using ${size_gb}GB of registry storage, which exceeds the threshold of ${STORAGE_THRESHOLD_GB}GB.
Current statistics:
- Repositories: $repo_count
- Total size: ${size_gb}GB
Consider reviewing cleanup policies or manually removing unused images."
send_alert "High Storage Usage: $project_name" "$alert_msg"
fi
done < "$METRICS_FILE"
}
Function to check cleanup policy status
check_cleanup_policies() {
local config_file="/opt/cleanup-config.json"
if [[ ! -f "$config_file" ]]; then
send_alert "Configuration Missing" "Cleanup configuration file not found at $config_file"
return 1
fi
jq -r '.projects[].id' "$config_file" | while read -r project_id; do
# Get current policy status
policy_response=$(curl --silent --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
"$GITLAB_URL/api/v4/projects/$project_id" || echo "")
if [[ -z "$policy_response" ]]; then
send_alert "API Error" "Failed to retrieve policy status for project $project_id"
continue
fi
# Check if cleanup policy is enabled
policy_enabled=$(echo "$policy_response" | jq -r '.container_expiration_policy.enabled // false')
if [[ "$policy_enabled" != "true" ]]; then
project_name=$(echo "$policy_response" | jq -r '.name')
send_alert "Policy Disabled" "Cleanup policy is disabled for project '$project_name' (ID: $project_id)"
fi
done
}
Main execution
echo "Running registry cleanup alerts..."
check_storage_thresholds
check_cleanup_policies
echo "Cleanup alerts completed"
Add the alerting script to your monitoring timer or create a separate one:
[Unit]
Description=GitLab Container Registry Alerts
After=network.target
[Service]
Type=oneshot
User=gitlab-registry
Group=gitlab-registry
Environment=GITLAB_TOKEN=your_token_here
Environment=GITLAB_URL=https://gitlab.example.com
Environment=ALERT_EMAIL=admin@example.com
Environment=STORAGE_THRESHOLD_GB=50
ExecStart=/opt/cleanup-alerts.sh
StandardOutput=journal
StandardError=journal
Install mail utilities and enable the alert service:
sudo apt install -y mailutils bc
sudo systemctl enable --now gitlab-registry-alerts.service
Verify your setup
Test your cleanup policy configuration and monitoring setup to ensure everything works correctly.
# Check cleanup policy status via API
curl --header "PRIVATE-TOKEN: your_token" \
"https://gitlab.example.com/api/v4/projects/123" | \
jq '.container_expiration_policy'
Test monitoring script
/opt/registry-monitor.sh --summary
Check systemd timer status
sudo systemctl status gitlab-registry-monitor.timer
sudo systemctl list-timers gitlab-registry-monitor.timer
View recent monitoring logs
sudo journalctl -u gitlab-registry-monitor.service --since="1 day ago"
Test alert script
GITLAB_TOKEN="your_token" STORAGE_THRESHOLD_GB=1 /opt/cleanup-alerts.sh
Check registry storage usage
tail -10 /var/log/registry-metrics.log
You can also verify the cleanup is working by checking your GitLab project's container registry through the web interface. Navigate to your project's Packages and registries > Container Registry to see current images and confirm old images are being removed according to your policies.
Monitor storage usage and cleanup effectiveness
Regular monitoring helps you understand storage patterns and optimize cleanup policies. Set up dashboards and reports to track cleanup effectiveness over time.
#!/bin/bash
METRICS_FILE="/var/log/registry-metrics.log"
REPORT_FILE="/var/log/cleanup-report-$(date +%Y%m%d).txt"
echo "GitLab Container Registry Cleanup Report" > "$REPORT_FILE"
echo "Generated: $(date)" >> "$REPORT_FILE"
echo "==========================================" >> "$REPORT_FILE"
echo "" >> "$REPORT_FILE"
Storage trends over last 30 days
echo "Storage Trends (Last 30 Days):" >> "$REPORT_FILE"
echo "--------------------------------" >> "$REPORT_FILE"
awk -F',' 'NR>1 {projects[$3] += $5; dates[$1]++} END {
print "Total storage by project:"
for (p in projects) printf " %s: %.2f GB\n", p, projects[p]/1024/1024/1024
print "\nDays with metrics:", length(dates)
}' "$METRICS_FILE" >> "$REPORT_FILE"
Recent cleanup activity
echo "" >> "$REPORT_FILE"
echo "Recent Activity (Last 7 Days):" >> "$REPORT_FILE"
echo "-------------------------------" >> "$REPORT_FILE"
tail -50 "$METRICS_FILE" | awk -F',' '
BEGIN {print "Date,Project,Repositories,Size_GB"}
NR>1 {printf "%s,%s,%s,%.2f\n", $1, $3, $4, $5/1024/1024/1024}' >> "$REPORT_FILE"
echo "Report saved to: $REPORT_FILE"
The GitLab container registry with SSL certificates and security hardening tutorial provides additional security configuration for your registry setup. For comprehensive infrastructure monitoring, consider setting up Prometheus and Grafana monitoring stack to visualize registry metrics alongside other system metrics.
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Cleanup policy not running | Policy disabled or invalid regex | Check policy settings in GitLab UI, verify regex patterns work with grep |
| Images not being deleted | Preserved by regex pattern | Review name_regex_keep pattern, test with sample tag names |
| API calls failing | Invalid token or insufficient permissions | Verify token has api scope and user has maintainer role on projects |
| Too many images deleted | Overly broad cleanup rules | Use more specific regex patterns, increase keep_n value |
| Storage not reducing | Large base layers shared between images | Allow more time for garbage collection, check for registry maintenance windows |
| Monitoring script errors | Missing dependencies or permissions | Install jq, curl, verify script has executable permissions |
Next steps
- Set up GitLab backup and disaster recovery with automated restoration to protect your registry data
- Configure Prometheus monitoring for ArgoCD to integrate registry cleanup with GitOps workflows
- Configure Grafana dashboards for TimescaleDB analytics to visualize registry usage metrics over time
- Implement Kubernetes network policies with Calico CNI and OPA Gatekeeper for secure container deployments
Running this in production?
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Variables
GITLAB_URL=""
GITLAB_TOKEN=""
PROJECT_IDS=()
CONFIG_DIR="/opt/gitlab-cleanup"
SCRIPT_NAME="gitlab-registry-cleanup.sh"
# Usage function
usage() {
echo "Usage: $0 -u <gitlab_url> -t <token> [-p <project_id>] [-p <project_id>] ..."
echo ""
echo "Options:"
echo " -u <url> GitLab URL (e.g., https://gitlab.example.com)"
echo " -t <token> GitLab personal access token with api scope"
echo " -p <id> Project ID (can be specified multiple times)"
echo " -h Show this help message"
echo ""
echo "Example:"
echo " $0 -u https://gitlab.example.com -t glpat-xxxxxxxxxxxx -p 123 -p 456"
exit 1
}
# Error handling and cleanup
cleanup_on_error() {
echo -e "${RED}[ERROR] Script failed. Cleaning up...${NC}"
if [[ -d "$CONFIG_DIR" ]]; then
rm -rf "$CONFIG_DIR"
fi
exit 1
}
trap cleanup_on_error ERR
# Parse command line arguments
while getopts "u:t:p:h" opt; do
case $opt in
u) GITLAB_URL="$OPTARG" ;;
t) GITLAB_TOKEN="$OPTARG" ;;
p) PROJECT_IDS+=("$OPTARG") ;;
h) usage ;;
*) usage ;;
esac
done
# Validate required arguments
if [[ -z "$GITLAB_URL" || -z "$GITLAB_TOKEN" ]]; then
echo -e "${RED}[ERROR] GitLab URL and token are required${NC}"
usage
fi
if [[ ${#PROJECT_IDS[@]} -eq 0 ]]; then
echo -e "${YELLOW}[WARNING] No project IDs specified. Will create configuration template only.${NC}"
fi
# Check if running as root or with sudo
echo "[1/8] Checking prerequisites..."
if [[ $EUID -ne 0 ]]; then
echo -e "${RED}[ERROR] This script must be run as root or with sudo${NC}"
exit 1
fi
# Auto-detect distribution
echo "[2/8] Detecting operating system..."
if [[ -f /etc/os-release ]]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_INSTALL="apt install -y"
PKG_UPDATE="apt update"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
PKG_UPDATE="dnf check-update || true"
;;
amzn)
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
PKG_UPDATE="yum check-update || true"
;;
*)
echo -e "${RED}[ERROR] Unsupported distribution: $ID${NC}"
exit 1
;;
esac
echo -e "${GREEN}Detected: $PRETTY_NAME${NC}"
else
echo -e "${RED}[ERROR] Cannot detect operating system${NC}"
exit 1
fi
# Update package lists and install dependencies
echo "[3/8] Installing dependencies..."
$PKG_UPDATE
$PKG_INSTALL curl jq cron
# Create configuration directory
echo "[4/8] Creating configuration directory..."
mkdir -p "$CONFIG_DIR"
chmod 755 "$CONFIG_DIR"
# Create cleanup script
echo "[5/8] Creating GitLab registry cleanup script..."
cat > "$CONFIG_DIR/$SCRIPT_NAME" << 'EOF'
#!/usr/bin/env bash
set -euo pipefail
# Configuration
GITLAB_URL="${GITLAB_URL:-}"
GITLAB_TOKEN="${GITLAB_TOKEN:-}"
LOG_FILE="/var/log/gitlab-registry-cleanup.log"
# Logging function
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}
# Function to configure cleanup policy for a project
configure_cleanup_policy() {
local project_id="$1"
local policy_data='{
"container_expiration_policy_attributes": {
"enabled": true,
"cadence": "1d",
"older_than": "7d",
"keep_n": 10,
"name_regex": ".*",
"name_regex_keep": "^(main|master|production|release-.*|v\\d+\\.\\d+\\.\\d+)$"
}
}'
log "Configuring cleanup policy for project $project_id"
response=$(curl -s -w "%{http_code}" \
--request PUT \
--header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
--header "Content-Type: application/json" \
--data "$policy_data" \
--output /tmp/gitlab_response.json \
"$GITLAB_URL/api/v4/projects/$project_id")
if [[ "$response" -eq 200 ]]; then
log "Successfully configured cleanup policy for project $project_id"
else
log "ERROR: Failed to configure cleanup policy for project $project_id (HTTP $response)"
if [[ -f /tmp/gitlab_response.json ]]; then
log "Response: $(cat /tmp/gitlab_response.json)"
fi
return 1
fi
}
# Function to add advanced cleanup rules
configure_advanced_rules() {
local project_id="$1"
log "Configuring advanced cleanup rules for project $project_id"
# Feature branch rule
local feature_rule='{
"container_expiration_policy_attributes": {
"enabled": true,
"cadence": "1d",
"older_than": "3d",
"keep_n": 5,
"name_regex": "^feature-.*",
"name_regex_keep": ""
}
}'
# Development rule
local dev_rule='{
"container_expiration_policy_attributes": {
"enabled": true,
"cadence": "1d",
"older_than": "5d",
"keep_n": 3,
"name_regex": "^(dev|develop|test)-.*",
"name_regex_keep": ""
}
}'
# PR rule
local pr_rule='{
"container_expiration_policy_attributes": {
"enabled": true,
"cadence": "1d",
"older_than": "1d",
"keep_n": 2,
"name_regex": "^pr-\\d+",
"name_regex_keep": ""
}
}'
for rule_name in "feature" "dev" "pr"; do
case $rule_name in
"feature") rule_data="$feature_rule" ;;
"dev") rule_data="$dev_rule" ;;
"pr") rule_data="$pr_rule" ;;
esac
response=$(curl -s -w "%{http_code}" \
--request PUT \
--header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
--header "Content-Type: application/json" \
--data "$rule_data" \
--output /tmp/gitlab_${rule_name}_response.json \
"$GITLAB_URL/api/v4/projects/$project_id")
if [[ "$response" -eq 200 ]]; then
log "Successfully configured $rule_name cleanup rule for project $project_id"
else
log "WARNING: Failed to configure $rule_name cleanup rule for project $project_id (HTTP $response)"
fi
done
}
# Main execution
main() {
if [[ -z "$GITLAB_URL" || -z "$GITLAB_TOKEN" ]]; then
log "ERROR: GITLAB_URL and GITLAB_TOKEN must be set"
exit 1
fi
if [[ -f /opt/gitlab-cleanup/project_ids.txt ]]; then
while IFS= read -r project_id; do
[[ -n "$project_id" && ! "$project_id" =~ ^[[:space:]]*# ]] && {
configure_cleanup_policy "$project_id"
configure_advanced_rules "$project_id"
}
done < /opt/gitlab-cleanup/project_ids.txt
else
log "No project IDs file found at /opt/gitlab-cleanup/project_ids.txt"
exit 1
fi
log "GitLab registry cleanup configuration completed"
}
main "$@"
EOF
chmod 755 "$CONFIG_DIR/$SCRIPT_NAME"
# Create project IDs file
echo "[6/8] Creating project configuration..."
if [[ ${#PROJECT_IDS[@]} -gt 0 ]]; then
printf '%s\n' "${PROJECT_IDS[@]}" > "$CONFIG_DIR/project_ids.txt"
else
cat > "$CONFIG_DIR/project_ids.txt" << EOF
# Add your GitLab project IDs here, one per line
# Lines starting with # are ignored
# Example:
# 123
# 456
EOF
fi
chmod 644 "$CONFIG_DIR/project_ids.txt"
# Create environment configuration
echo "[7/8] Creating environment configuration..."
cat > "$CONFIG_DIR/config.env" << EOF
# GitLab Configuration
GITLAB_URL="$GITLAB_URL"
GITLAB_TOKEN="$GITLAB_TOKEN"
EOF
chmod 600 "$CONFIG_DIR/config.env"
# Create systemd service and timer for scheduled execution
if systemctl --version >/dev/null 2>&1; then
cat > /etc/systemd/system/gitlab-registry-cleanup.service << EOF
[Unit]
Description=GitLab Container Registry Cleanup
After=network.target
[Service]
Type=oneshot
EnvironmentFile=$CONFIG_DIR/config.env
ExecStart=$CONFIG_DIR/$SCRIPT_NAME
User=root
StandardOutput=journal
StandardError=journal
EOF
cat > /etc/systemd/system/gitlab-registry-cleanup.timer << EOF
[Unit]
Description=Run GitLab Container Registry Cleanup daily
Requires=gitlab-registry-cleanup.service
[Timer]
OnCalendar=daily
RandomizedDelaySec=1h
Persistent=true
[Install]
WantedBy=timers.target
EOF
systemctl daemon-reload
systemctl enable gitlab-registry-cleanup.timer
systemctl start gitlab-registry-cleanup.timer
echo -e "${GREEN}Created systemd timer for daily execution${NC}"
else
# Fallback to cron
echo "0 6 * * * root source $CONFIG_DIR/config.env && $CONFIG_DIR/$SCRIPT_NAME" > /etc/cron.d/gitlab-registry-cleanup
chmod 644 /etc/cron.d/gitlab-registry-cleanup
echo -e "${GREEN}Created cron job for daily execution at 6:00 AM${NC}"
fi
# Create log file
touch /var/log/gitlab-registry-cleanup.log
chmod 644 /var/log/gitlab-registry-cleanup.log
echo "[8/8] Verifying installation..."
# Verify dependencies
for cmd in curl jq; do
if ! command -v "$cmd" >/dev/null 2>&1; then
echo -e "${RED}[ERROR] $cmd is not installed${NC}"
exit 1
fi
done
# Verify files
required_files=(
"$CONFIG_DIR/$SCRIPT_NAME"
"$CONFIG_DIR/project_ids.txt"
"$CONFIG_DIR/config.env"
"/var/log/gitlab-registry-cleanup.log"
)
for file in "${required_files[@]}"; do
if [[ ! -f "$file" ]]; then
echo -e "${RED}[ERROR] Required file $file is missing${NC}"
exit 1
fi
done
echo -e "${GREEN}[SUCCESS] GitLab Container Registry cleanup script installed successfully!${NC}"
echo ""
echo "Configuration files:"
echo " Script: $CONFIG_DIR/$SCRIPT_NAME"
echo " Project IDs: $CONFIG_DIR/project_ids.txt"
echo " Environment: $CONFIG_DIR/config.env"
echo " Log file: /var/log/gitlab-registry-cleanup.log"
echo ""
echo "To manually run the cleanup:"
echo " source $CONFIG_DIR/config.env && $CONFIG_DIR/$SCRIPT_NAME"
echo ""
if [[ ${#PROJECT_IDS[@]} -eq 0 ]]; then
echo -e "${YELLOW}[NOTICE] Please add your project IDs to $CONFIG_DIR/project_ids.txt${NC}"
fi
Review the script before running. Execute with: bash install.sh