Enabling and Using Immutable Tags in Harbor to Prevent Overwrites
Learn how to enable Harbor’s immutable‑tag feature to block overwrites, see a worked push example, and understand its limits and common pitfalls.
08 May 2026, 10:24 UTC

Useful answer
To stop any push that would overwrite an existing tag in Harbor, enable the immutable‑tag feature. Once enabled, a second push of the same tag returns HTTP 409 Conflict and the operation is aborted, protecting release artifacts from accidental overwrites.
How to enable immutable tags
Via the configuration file
Edit the Harbor harbor.yml file, set the flag under the core section, then restart the services.
# harbor.yml (excerpt)
core:
# … other settings
immutable_tag: true
After saving the file, run the preparation script and restart Harbor (commands assume the offline/docker‑compose installer; adjust for Helm if you use that method).
# From the Harbor installation directory
./prepare
# Restart containers
docker compose down && docker compose up -d
Via the UI (per‑project)
- Log in to Harbor as an administrator.
- Navigate to Project → <your‑project> → Settings → Policies.
- Toggle Immutable tag to Enabled and save.
Both methods achieve the same effect; the UI toggle overrides the global setting for that specific project.
What happens when you try to overwrite a tag
When immutable tags are enabled, Harbor treats a push that would reuse an existing tag as a conflict.
- The Docker client receives HTTP 409 Conflict.
- The response body contains a JSON error object:
{
"errors": [
{
"code": "immutable_tag_violation",
"message": "tag already exists"
}
]
}
The push is aborted; no new manifest or layers are stored.
Worked example
Assume a project named library with repository nginx and tag 1.21 already present.
- Push the image the first time (this succeeds).
# Build or pull nginx:1.21 locally
docker pull nginx:1.21
# Tag it for Harbor
docker tag nginx:1.21 harbor.example.com/library/nginx:1.21
# Push
docker push harbor.example.com/library/nginx:1.21
- Attempt to push the same tag again (this will fail).
docker push harbor.example.com/library/nginx:1.21
# Expected output (client side):
# received unexpected HTTP status: 409 Conflict
# server response: {"errors":[{"code":"immutable_tag_violation","message":"tag already exists"}]}
Verification steps
After enabling the feature, you can confirm it is working without relying on memorized output.
Check the logs
Harbor core logs record each violation.
# On the host running Harbor core
sudo tail -f /var/log/harbor/core.log
# Look for a line similar to:
# 2026-10-08T15:30:12Z error immutable tag violation: tag already exists for project/library/repository/nginx tag 1.21
Use the Harbor API
First verify the tag exists, then try to create it again via the API.
# 1. Confirm tag exists (replace placeholders)
curl -k -u "admin:Harbor12345" \
"https://harbor.example.com/api/v2.0/projects/library/repositories/nginx/tags/1.21"
# 2. Attempt to create the same tag (should fail)
curl -k -X POST -u "admin:Harbor12345" \
-H "Content-Type: application/json" \
-d '{"name":"1.21"}' \
"https://harbor.example.com/api/v2.0/projects/library/repositories/nginx/tags"
# Expected response: HTTP 409 with the JSON error shown above.
Limits and what the feature does NOT cover
- Only new pushes are protected. Existing mutable tags stay mutable unless you change the project setting and then never push to them again.
- The setting applies to tag pushes. Manifest lists, foreign layers, or pulling existing images are unaffected.
- Helm chart replication or CI pipelines that rely on retagging the same version (e.g.,
helm push . --version 1.0.0repeatedly) will start failing after immutability is enabled.
Common mistakes to avoid
- Forgetting to restart Harbor. Changing
harbor.ymlalone does not activate the flag; the core service must be reloaded. - Assuming the change is retroactive. Turning on immutable tags does not make already‑pushed tags immutable; only subsequent pushes are blocked.
- Using the wrong scope. The UI toggle is per‑project; if you enable it globally but forget to enable it for a specific project, pushes there remain mutable.
- Overlooking CI/CD pipelines. Automation that pushes a new image with the same tag (e.g.,
docker push myrepo/app:lateston every build) will start receiving 409 errors after immutability is turned on, causing build failures.
Practical way to check the result
After you have made the configuration change and restarted Harbor:
- Push a fresh image with a new tag (e.g.,
test:immutable-check). - Immediately push the same tag again.
- Confirm the second push fails with HTTP 409 and the error message
tag already exists. - Optionally, inspect the core log for the
immutable tag violationline.
If the second push succeeds, either the feature is not enabled for that project or the Harbor instance was not restarted after the change.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.