Fixing TeamCity Docker SSL Certificate Verification Errors with Internal Git Repositories
The Problem
When running TeamCity in a Docker container on macOS and attempting to connect to an internal Git repository (in my case, a self-hosted Bitbucket server), I encountered this frustrating error:
Test connection failed
git ls-remote origin command failed.
exit code: 128
stderr: fatal: unable to access 'https://git.example.com/scm/project/repo.git/':
server certificate verification failed. CAfile: none CRLfile: noneThis error occurred even though:
— I could access the Bitbucket website in Safari with no issues
— I had imported the site certificate into both my local and system Keychain on macOS
The root cause: The TeamCity Docker container has its own certificate store, completely separate from macOS. Simply adding certificates to your Mac’s keychain doesn’t help the containerized application.
The Solution
The solution involves three key steps:
- Convert your certificate to a format Git can use (PEM)
- Create a custom Java truststore for TeamCity
- Configure both Java and Git in the Docker container to trust your certificate
Prerequisites
- Your self-signed or internal CA certificate file (.cer, .crt, or .pem format)
- A TeamCity Docker container with persistent data volumes
- openssl and keytool available on your host machine (macOS includes both)
Step 1: Convert Certificate to PEM Format
First, navigate to your TeamCity directory and convert your certificate:
cd /Users/yourname/Dev/teamcity
# Try DER format first, fall back to PEM if already in that format
openssl x509 -inform DER -in "your-cert.cer" -out data/git-cert.pem 2>/dev/null || \
openssl x509 -in "your-cert.cer" -out data/git-cert.pemThis creates a PEM-formatted certificate in your TeamCity data directory (which is mounted as a persistent volume).
Step 2: Create a Custom Java Truststore
TeamCity runs on Java, so we need to add the certificate to a Java truststore:
# Create config directory if it doesn't exist
mkdir -p data/config
# Create a custom Java truststore with your certificate
keytool -import -noprompt \
-alias git-internal \
-file data/git-cert.pem \
-keystore data/config/custom-truststore.jks \
-storepass changeit
# Verify the certificate was imported
keytool -list \
-keystore data/config/custom-truststore.jks \
-storepass changeit \
-alias git-internalStep 3: Update Docker Launch Script
This is the critical step. You need to configure both Java (for TeamCity) and Git (for repository operations) to trust your certificate.
Here’s an example start_server.sh script:
docker stop server
docker rm server
docker pull jetbrains/teamcity-server
docker run -it -d --name server -u root \
-v /Users/yourname/Dev/teamcity/data:/data/teamcity_server/datadir \
-v /Users/yourname/Dev/teamcity/logs:/opt/teamcity/logs \
-p 8111:8111 \
-e JAVA_OPTS='-Djavax.net.ssl.trustStore=/data/teamcity_server/datadir/config/custom-truststore.jks -Djavax.net.ssl.trustStorePassword=changeit' \
-e GIT_SSL_CAINFO=/data/teamcity_server/datadir/git-cert.pem \
--restart unless-stopped \
jetbrains/teamcity-serverKey environment variables:
- JAVA_OPTS: Configures Java to use your custom truststore
- -Djavax.net.ssl.trustStore: Path to your truststore file (inside the container)
- -Djavax.net.ssl.trustStorePassword: Truststore password (default is “changeit”)
- GIT_SSL_CAINFO: Tells Git where to find the CA certificate for SSL verification
Step 4: Restart and Test
./start_server.shOnce TeamCity restarts, go to your VCS Root configuration and click “Test Connection”. The SSL certificate error should be resolved!
Why This Works
The solution addresses certificate validation at two levels:
- Java/TeamCity Level: The custom truststore allows TeamCity’s Java processes to trust your internal certificate for HTTPS connections
- Git Level: The GIT_SSL_CAINFO environment variable tells Git (which TeamCity uses to clone repositories) where to find the trusted CA certificate
Persistence Across Container Updates
Because both the certificate (git-cert.pem) and the truststore (custom-truststore.jks) are stored in the persistent data directory, they will survive TeamCity container updates. You won’t need to repeat this process when upgrading TeamCity versions - just restart with the same launch script.
Troubleshooting
If you’re still experiencing issues:
- Verify the certificate is in the correct format:
openssl x509 -in data/git-cert.pem -text -noout2. Check if the environment variables are set in the container:
docker exec server env | grep -E 'JAVA_OPTS|GIT_SSL_CAINFO'3. Verify the files exist at the expected paths inside the container:
docker exec server ls -la /data/teamcity_server/datadir/git-cert.pem
docker exec server ls -la /data/teamcity_server/datadir/config/custom-truststore.jks4. Check TeamCity logs for SSL-related errors:
docker logs server | grep -i sslAlternative: Per-VCS Root Configuration
If you prefer not to configure Git globally, you can set the certificate path per VCS Root in TeamCity:
- Go to your VCS Root configuration
- Scroll to Advanced Settings
- Add a custom Git configuration parameter:
- Name: http.sslCAInfo
- Value: /data/teamcity_server/datadir/git-cert.pem
This approach is useful if you only want specific repositories to use the custom certificate.
Security Note
While it might be tempting to disable SSL verification entirely (via git config http.sslVerify false), this is strongly discouraged as it exposes you to man-in-the-middle attacks. Always use proper certificate validation in production environments.
Conclusion
SSL certificate issues with Docker containers can be tricky because the container’s environment is isolated from the host machine. By properly configuring both Java and Git to trust your internal certificates, you can securely connect TeamCity to self-hosted Git repositories without disabling security features.
The key insight is that certificates stored in your host’s keychain are not accessible to containerized applications — you must explicitly configure the container to trust your internal CAs.
This solution was tested with TeamCity 2025.7 running in Docker on macOS with a self-hosted Bitbucket server using internal SSL certificates.