DEV Community

giveitatry
giveitatry

Posted on

Renewing Harbor Internal TLS Certificates

Overview

Harbor supports internal TLS to encrypt communication between its internal services (Core, Registry, Job Service, Database, Trivy, Portal, etc.).

When internal certificates expire, Harbor components may fail to communicate and errors such as the following may appear in the logs:

x509: certificate has expired or is not yet valid
tls: failed to verify certificate
certificate has expired
Enter fullscreen mode Exit fullscreen mode

If internal_tls is enabled in harbor.yml, Harbor stores the source certificates in the directory specified by internal_tls.dir. During execution of ./prepare, these certificates are copied into /data/secret/tls, which is mounted into the Harbor containers.

Example configuration:

internal_tls:
  enabled: true
  dir: /opt/harbor/internal-certs
Enter fullscreen mode Exit fullscreen mode

Prerequisites

  • Harbor installation directory (for example ~/harbor)
  • Docker Compose
  • Harbor prepare image matching your Harbor version
  • Root or sudo access

Step 1 – Stop Harbor

cd ~/harbor
docker compose down
Enter fullscreen mode Exit fullscreen mode

Step 2 – Backup Existing Certificates

Always create a backup before replacing certificates.

cp -a /opt/harbor/internal-certs \
      /opt/harbor/internal-certs.bak.$(date +%Y%m%d)
Enter fullscreen mode Exit fullscreen mode

Step 3 – Remove Existing Certificates

rm -f /opt/harbor/internal-certs/*
Enter fullscreen mode Exit fullscreen mode

Step 4 – Generate New Internal Certificates

Replace the image version with your Harbor version if necessary.

docker run --rm \
    -v /opt/harbor:/opt/harbor \
    goharbor/prepare:v2.13.1 \
    gencert \
    -p /opt/harbor/internal-certs \
    --days 3650
Enter fullscreen mode Exit fullscreen mode

Note: Adjust the certificate lifetime (--days) according to your organization's security policy.


Step 5 – Set Permissions

chown -R 10000:10000 /opt/harbor/internal-certs

chmod 644 \
    /opt/harbor/internal-certs/*.crt \
    /opt/harbor/internal-certs/*.csr \
    /opt/harbor/internal-certs/*.srl 2>/dev/null

chmod 600 /opt/harbor/internal-certs/*.key
Enter fullscreen mode Exit fullscreen mode

Step 6 – Remove Previously Copied Certificates

rm -f /data/secret/tls/*
Enter fullscreen mode Exit fullscreen mode

Step 7 – Recreate Harbor Configuration

./prepare --with-trivy
Enter fullscreen mode Exit fullscreen mode

The prepare script copies the newly generated certificates into:

/data/secret/tls
Enter fullscreen mode Exit fullscreen mode

and regenerates the Docker Compose configuration.


Step 8 – Verify the New Certificates

Verify that the certificates have been copied:

ls -la /data/secret/tls/
Enter fullscreen mode Exit fullscreen mode

Verify the CA expiration date:

openssl x509 \
    -in /data/secret/tls/harbor_internal_ca.crt \
    -noout \
    -dates
Enter fullscreen mode Exit fullscreen mode

Example output:

notBefore=Aug  6 09:29:22 2026 GMT
notAfter=Aug  5 09:29:22 2036 GMT
Enter fullscreen mode Exit fullscreen mode

It is also recommended to verify one of the service certificates:

openssl x509 \
    -in /data/secret/tls/core.crt \
    -noout \
    -dates
Enter fullscreen mode Exit fullscreen mode

To verify every certificate:

for cert in /data/secret/tls/*.crt; do
    echo "===== $cert ====="
    openssl x509 -noout -dates -in "$cert"
done
Enter fullscreen mode Exit fullscreen mode

Step 9 – Verify File Ownership

After running ./prepare, Harbor assigns ownership based on the user ID expected by each container.

Display ownership information:

ls -ln /data/secret/tls
Enter fullscreen mode Exit fullscreen mode

or

stat -c "%n %u:%g" /data/secret/tls/*
Enter fullscreen mode Exit fullscreen mode

Example output:

core.crt               10000:10000
core.key               10000:10000
registry.crt           10000:10000
registry.key           10000:10000
harbor_db.crt            999:999
harbor_db.key            999:999
Enter fullscreen mode Exit fullscreen mode

This is expected behavior.

Harbor uses different Linux users inside different containers:

Service Expected UID
Core 10000
Registry 10000
Job Service 10000
Portal 10000
Trivy 10000
Internal CA 10000
Database 999

Do not manually change ownership inside /data/secret/tls after running ./prepare, as Harbor intentionally sets the ownership required by each container.


Step 10 – Start Harbor

docker compose up -d
Enter fullscreen mode Exit fullscreen mode

Step 11 – Verify Harbor Health

Verify that all containers are running:

docker compose ps
Enter fullscreen mode Exit fullscreen mode

All Harbor services should eventually report:

healthy
Enter fullscreen mode Exit fullscreen mode

Step 12 – Check Container Logs

Inspect the logs for TLS-related errors:

docker compose logs --tail=100 core
docker compose logs --tail=100 registry
docker compose logs --tail=100 jobservice
docker compose logs --tail=100 nginx
Enter fullscreen mode Exit fullscreen mode

There should be no errors such as:

x509: certificate has expired
certificate has expired
tls: failed to verify certificate
Enter fullscreen mode Exit fullscreen mode

Troubleshooting

If Harbor still reports expired certificates:

  1. Verify that internal_tls.enabled is set to true.
  2. Confirm that internal_tls.dir points to the directory where the new certificates were generated.
  3. Ensure ./prepare completed successfully without errors.
  4. Verify that /data/secret/tls contains newly generated certificates with updated expiration dates.
  5. Restart Harbor using docker compose down followed by docker compose up -d.
  6. Check the logs of the affected container for certificate validation errors.
  7. Verify that the system time on the Harbor host is correct (timedatectl status), as an incorrect clock can also cause certificate validation failures.

Top comments (0)