# How to Configure Tag Retention Policies in Harbor: A Complete Guide

> Master Harbor tag retention policies. Learn to automatically clean up old image tags with this complete guide. Configure retention rules easily via UI or API.

- Repository: [Harbor/harbor](https://github.com/goharbor/harbor)
- Tags: how-to-guide
- Published: 2026-04-09

---

**Harbor's tag retention policies automatically clean up old image tags using configurable rules defined through the v2.0 REST API or web UI, storing policy definitions in the `retention_policy` table and executing them via the `controller/retention` package.**

Configuring tag retention policies in Harbor is essential for maintaining a clean container registry and controlling storage costs. The open-source Harbor project (goharbor/harbor) provides a flexible retention engine that evaluates image tags against criteria such as push count and last pull date, automatically removing artifacts that no longer meet your organization's retention criteria.

## Architecture of the Retention Policy Engine

Harbor implements tag retention through a layered architecture that separates API handling, business logic, and persistence. Understanding these components helps when troubleshooting or extending retention functionality.

The **API layer** in [`src/server/v2.0/handler/retention.go`](https://github.com/goharbor/harbor/blob/main/src/server/v2.0/handler/retention.go) handles HTTP requests for CRUD operations, validates rule syntax, performs permission checks, and triggers executions. The **model conversion** layer at [`src/server/v2.0/handler/model/retention.go`](https://github.com/goharbor/harbor/blob/main/src/server/v2.0/handler/model/retention.go) translates between internal `policy.Metadata` structs and Swagger API models.

The **business logic** resides in `src/controller/retention`, implementing the policy life-cycle including creation, updates, deletions, and asynchronous execution handling. For **persistence**, the DAO layer in [`src/pkg/retention/dao/retention.go`](https://github.com/goharbor/harbor/blob/main/src/pkg/retention/dao/retention.go) stores policy definitions in the `retention_policy` database table.

The **frontend UI**, built in Vue/TypeScript at [`src/portal/src/app/base/project/tag-feature-integration/tag-retention/retention.ts`](https://github.com/goharbor/harbor/blob/main/src/portal/src/app/base/project/tag-feature-integration/tag-retention/retention.ts), provides a visual wizard for composing rules and monitoring execution status using the same underlying API endpoints.

## Policy Structure and Configuration Schema

A retention policy in Harbor consists of several key components that define what to retain and when to evaluate rules.

The **algorithm** field specifies how rules combine logically—`or` is the default and most common choice. The **scope** currently supports only project-level targeting with `level: "project"` and a `ref` containing the numeric project ID.

Each policy contains up to 15 **rules**, where each rule specifies:

- **template**: Built-in logic such as `latestPushedK`, `nDaysSinceLastPush`, or `nDaysSinceLastPull`
- **action**: Either `retain` (keep matching tags) or `immutable` (prevent deletion)
- **params**: Template-specific values like `count` or `days`
- **tag_selectors**: Glob patterns using `doublestar` syntax to match tag names
- **scope_selectors.repository**: Repository-level glob patterns to limit which repositories the rule evaluates

The **trigger** determines execution timing, supporting `kind: "Schedule"` with a cron expression or `kind: "Manual"` for on-demand runs.

## Configuring Policies via the Harbor API

While the Harbor web UI provides a visual interface, automating retention policy configuration requires direct API interaction.

### Prerequisites and Permissions

Before creating policies, identify your target **project ID** and ensure you have RBAC permissions on the `TagRetention` resource. The handler function `requireAccess` (lines 108-115 in [`retention.go`](https://github.com/goharbor/harbor/blob/main/retention.go)) validates these rights on every API call.

### Creating a Retention Policy

Construct a JSON payload following the schema defined in the internal models. This example retains the 10 most recently pushed tags matching `library/**` and removes release tags older than 30 days:

```json
{
  "algorithm": "or",
  "scope": { "level": "project", "ref": 42 },
  "rules": [
    {
      "template": "latestPushedK",
      "action": "retain",
      "params": { "count": 10 },
      "tag_selectors": [
        {
          "kind": "doublestar",
          "decoration": "matches",
          "pattern": "**"
        }
      ],
      "scope_selectors": {
        "repository": [
          {
            "kind": "doublestar",
            "decoration": "repoMatches",
            "pattern": "library/**"
          }
        ]
      }
    },
    {
      "template": "nDaysSinceLastPull",
      "action": "retain",
      "params": { "days": 30 },
      "tag_selectors": [
        {
          "kind": "doublestar",
          "decoration": "matches",
          "pattern": "release-*"
        }
      ],
      "scope_selectors": {
        "repository": [
          {
            "kind": "doublestar",
            "decoration": "repoMatches",
            "pattern": "**"
          }
        ]
      }
    }
  ],
  "trigger": {
    "kind": "Schedule",
    "settings": { "cron": "0 0 * * *" }
  }
}

```

Submit the policy using the v2.0 endpoint. The `CreateRetention` handler (lines 61-90 in [`src/server/v2.0/handler/retention.go`](https://github.com/goharbor/harbor/blob/main/src/server/v2.0/handler/retention.go)) validates the rule count, checks for conflicts, and stores the definition:

```bash
curl -k -X POST "https://harbor.example.com/api/v2.0/retentions" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <TOKEN>" \
  -d @policy.json

```

The API returns a policy ID and updates the project's metadata with a `retention_id` reference.

### Executing and Monitoring Policies

Trigger a manual execution using the policy ID returned during creation. The `TriggerRetentionExecution` method processes this request (lines 70-84 in the handler):

```bash
curl -k -X POST "https://harbor.example.com/api/v2.0/retentions/123/executions" \
  -H "Authorization: Bearer <TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"dry_run":false}'

```

Monitor execution progress by listing executions and their tasks:

```bash

# List all executions for a policy

curl -k "https://harbor.example.com/api/v2.0/retentions/123/executions" \
  -H "Authorization: Bearer <TOKEN>"

# List tasks for a specific execution

curl -k "https://harbor.example.com/api/v2.0/executions/456/tasks" \
  -H "Authorization: Bearer <TOKEN>"

```

## Understanding Rule Templates and Selectors

Harbor provides several **built-in rule templates** that determine which tags to evaluate:

- **latestPushedK**: Retains the most recent *K* tags pushed to the repository
- **nDaysSinceLastPush**: Retains tags pushed within the last *N* days
- **nDaysSinceLastPull**: Retains tags pulled within the last *N* days

The **tag_selectors** and **scope_selectors** use `doublestar` glob patterns supporting wildcards. The `decoration` field specifies matching behavior—`matches` for exact matches or `repoMatches` for repository patterns. You can include `extras` parameters to handle untagged images or specific manifest types.

## Permission Model and Security Considerations

All retention API operations enforce RBAC through the `requireAccess` middleware. Users need appropriate permissions on the project resource `TagRetention`. System administrators can manage policies across all projects, while project members require specific role assignments.

Retention executions run asynchronously with their own task queues, ensuring that large-scale cleanup operations do not block the main API. The execution records maintain audit trails of which tags were evaluated and what actions were taken.

## Summary

- **Harbor's retention engine** spans multiple layers: API handlers in [`src/server/v2.0/handler/retention.go`](https://github.com/goharbor/harbor/blob/main/src/server/v2.0/handler/retention.go), business logic in `src/controller/retention`, and persistence via [`src/pkg/retention/dao/retention.go`](https://github.com/goharbor/harbor/blob/main/src/pkg/retention/dao/retention.go).
- **Policy definitions** use JSON structures supporting up to 15 rules with templates like `latestPushedK` and `nDaysSinceLastPull`, combined via `or` logic.
- **Configuration** requires a project ID, properly formatted selectors using `doublestar` glob patterns, and RBAC permissions on the `TagRetention` resource.
- **Execution** can be scheduled via cron expressions or triggered manually through `POST /api/v2.0/retentions/{id}/executions`, with status monitoring available through the executions and tasks endpoints.

## Frequently Asked Questions

### What rule templates are available for Harbor tag retention?

Harbor supports several built-in templates including `latestPushedK` (retain the most recent K tags), `nDaysSinceLastPush` (retain tags pushed within N days), and `nDaysSinceLastPull` (retain tags pulled within N days). These templates are defined in the policy metadata definitions within `src/pkg/retention/policy` and populated in the UI dropdowns defined in [`retention.ts`](https://github.com/goharbor/harbor/blob/main/retention.ts).

### How do I manually trigger a tag retention policy execution?

Send a `POST` request to `/api/v2.0/retentions/{id}/executions` with a JSON body containing `"dry_run": false` (or `true` for testing). The `TriggerRetentionExecution` handler creates an execution record and returns a location header pointing to the execution ID, allowing you to monitor progress through the tasks endpoint.

### What permissions are required to configure retention policies?

The API enforces RBAC checks through the `requireAccess` function in [`src/server/v2.0/handler/retention.go`](https://github.com/goharbor/harbor/blob/main/src/server/v2.0/handler/retention.go). Users need permissions on the `TagRetention` resource within the target project. System administrators have global access, while project members need specific roles granted through Harbor's permission system.

### Where does Harbor store retention policy definitions?

Policy definitions persist in the `retention_policy` database table, accessed through the DAO layer in [`src/pkg/retention/dao/retention.go`](https://github.com/goharbor/harbor/blob/main/src/pkg/retention/dao/retention.go). When created, policies also store a `retention_id` reference in the project's metadata, linking the project to its associated retention configuration.