How to Manage Multiple CAs Using the CAROOT Environment Variable in mkcert
Set the CAROOT environment variable to a custom directory path before running mkcert to isolate certificate authorities for different projects or testing environments.
The CAROOT environment variable in mkcert (FiloSottile/mkcert) controls where the tool stores its root CA files (rootCA.pem and rootCA-key.pem). By default, mkcert places these files in an OS-specific data directory, but overriding this location enables you to maintain completely independent certificate authorities for distinct development contexts or CI pipelines.
Understanding the CAROOT Mechanism
In main.go, the getCAROOT() function determines the storage location for CA files by first checking for an environment variable override:
func getCAROOT() string {
if env := os.Getenv("CAROOT"); env != "" {
return env
}
// Falls back to OS-specific default directory...
}
When CAROOT is set, mkcert stores the m.CAROOT string in its internal struct and passes this path to every helper function that reads or writes CA files, including loadCA() and newCA() in cert.go, as well as the platform-specific truststore installers (truststore_linux.go, truststore_darwin.go, truststore_windows.go, etc.). This design ensures that all subsequent operations—whether installing the CA to system trust stores or generating leaf certificates—operate exclusively within the specified directory.
Creating Isolated Certificate Authorities
Because mkcert generates the CA files (rootCA.pem and rootCA-key.pem) on first run if they do not exist, setting a unique CAROOT for each project creates fully separate trust anchors.
Project-Specific CA Setup
To establish an isolated CA for a single project:
# Define a private directory for this project's CA
export CAROOT="${HOME}/.mkcert-myproject"
# Generate the CA and install it (creates rootCA.pem and rootCA-key.pem)
mkcert -install
After running -install, mkcert places the CA certificate and key in ${HOME}/.mkcert-myproject/. This CA remains independent from your default mkcert CA stored in the standard OS data directory.
Generating Certificates with a Custom CAROOT
With CAROOT exported, all certificate generation commands use the isolated authority:
# Still using the custom CAROOT from above
mkcert example.test localhost 127.0.0.1
The resulting example.test+2.pem and example.test+2-key.pem files are signed by the CA in ${HOME}/.mkcert-myproject/, not your global default CA.
Managing Multiple CAs Simultaneously
You can operate multiple independent CAs in parallel by switching the environment variable between commands. This is useful for testing applications that validate certificates against different trust anchors or for maintaining separate PKI hierarchies for different microservices.
# Use the default CA location
unset CAROOT
mkcert -install
mkcert myapp.local
# Switch to an alternative CA in /tmp
export CAROOT="/tmp/alt-ca"
mkcert -install
mkcert -cert-file alt-cert.pem -key-file alt-key.pem otherapp.local
Each set of certificates trusts only the CA that resides in its respective CAROOT directory. The two CAs do not interfere with each other, and you can uninstall either independently using mkcert -uninstall while its specific CAROOT is set.
Distributing Custom CAs Across Systems
The CAROOT variable also simplifies deploying a development CA to remote servers or Docker containers. Instead of regenerating a new CA on each system, copy the existing root certificate and initialize the remote environment with the same CAROOT structure:
# On the source machine: copy the CA certificate
cp "$(mkcert -CAROOT)/rootCA.pem" /tmp/ca-copy.pem
# On the remote host
export CAROOT="/etc/mkcert-remote"
mkdir -p "$CAROOT"
mv /tmp/ca-copy.pem "$CAROOT/rootCA.pem"
mkcert -install
This workflow, documented in the mkcert README under "Installing the CA on other systems," ensures that the remote system trusts exactly the same root authority as your local development environment.
Summary
CAROOToverrides the default storage location forrootCA.pemandrootCA-key.pem, as implemented inmain.goand referenced throughoutcert.goandtruststore_*.go.- Each unique directory represents an independent CA, allowing per-project or per-environment isolation without code changes.
- Switching contexts requires only changing the environment variable before invoking mkcert commands.
- CAs created with custom
CAROOTvalues can be shared across systems by copying the certificate files and setting the same path on the target machine.
Frequently Asked Questions
What is the default CAROOT location when the variable is not set?
When CAROOT is unset, mkcert derives the location from OS-specific data directories: $HOME/.local/share/mkcert on Linux, $HOME/Library/Application Support/mkcert on macOS, and %LOCALAPPDATA%\mkcert on Windows. The getCAROOT() function in main.go handles this fallback logic after checking for the environment variable.
Can I switch between multiple CAs without reinstalling mkcert globally?
Yes. Simply change the CAROOT environment variable between commands. Each invocation of mkcert operates only on the CA files present in the currently specified directory. You can generate certificates from CA "A", then immediately switch to CA "B" by exporting a different path, without affecting the system trust store entries created by previous -install commands.
How do I completely remove a CA created with a custom CAROOT?
Run mkcert -uninstall while the specific CAROOT is exported. This removes the CA certificate from all system trust stores (Java, NSS, and OS stores) via the platform-specific truststore_*.go implementations, and then you can safely delete the directory contents including rootCA.pem and rootCA-key.pem.
Does setting CAROOT affect where leaf certificates are saved?
No. The CAROOT variable only controls the location of the root CA files. Leaf certificates generated by mkcert are always saved to the current working directory (or paths specified by -cert-file and -key-file flags) regardless of where the CA is stored.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →