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
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
Prerequisites
- Harbor installation directory (for example
~/harbor) - Docker Compose
- Harbor
prepareimage matching your Harbor version - Root or sudo access
Step 1 – Stop Harbor
cd ~/harbor
docker compose down
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)
Step 3 – Remove Existing Certificates
rm -f /opt/harbor/internal-certs/*
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
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
Step 6 – Remove Previously Copied Certificates
rm -f /data/secret/tls/*
Step 7 – Recreate Harbor Configuration
./prepare --with-trivy
The prepare script copies the newly generated certificates into:
/data/secret/tls
and regenerates the Docker Compose configuration.
Step 8 – Verify the New Certificates
Verify that the certificates have been copied:
ls -la /data/secret/tls/
Verify the CA expiration date:
openssl x509 \
-in /data/secret/tls/harbor_internal_ca.crt \
-noout \
-dates
Example output:
notBefore=Aug 6 09:29:22 2026 GMT
notAfter=Aug 5 09:29:22 2036 GMT
It is also recommended to verify one of the service certificates:
openssl x509 \
-in /data/secret/tls/core.crt \
-noout \
-dates
To verify every certificate:
for cert in /data/secret/tls/*.crt; do
echo "===== $cert ====="
openssl x509 -noout -dates -in "$cert"
done
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
or
stat -c "%n %u:%g" /data/secret/tls/*
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
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
Step 11 – Verify Harbor Health
Verify that all containers are running:
docker compose ps
All Harbor services should eventually report:
healthy
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
There should be no errors such as:
x509: certificate has expired
certificate has expired
tls: failed to verify certificate
Troubleshooting
If Harbor still reports expired certificates:
- Verify that
internal_tls.enabledis set totrue. - Confirm that
internal_tls.dirpoints to the directory where the new certificates were generated. - Ensure
./preparecompleted successfully without errors. - Verify that
/data/secret/tlscontains newly generated certificates with updated expiration dates. - Restart Harbor using
docker compose downfollowed bydocker compose up -d. - Check the logs of the affected container for certificate validation errors.
- 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)