Adding mTLS across services
mTLS helps secure inter-microservice communication but adds a bit of complexity to it.

n a microservices architecture, services talk to each other constantly — fetching user data, checking inventory, updating orders. If that traffic isn’t secured, you’re one misconfigured firewall away from a data leak. TLS is table stakes, but in a zero-trust world, it’s not enough for only the client to verify the server. Mutual TLS (mTLS) ensures both sides authenticate each other, making it much harder for a rogue service to sneak into the mesh.
With standard TLS, the server proves its identity to the client. With mTLS, both sides present certificates issued by the same certificate authority (CA). If either side fails verification, the connection is dropped. This guarantees that only trusted workloads can talk to each other.
In Go, you can enforce mTLS by configuring tls.Config
with both server and client validation:
server := &http.Server{
Addr: ":443",
TLSConfig: &tls.Config{
Certificates: []tls.Certificate{cert}, // service’s own cert
ClientAuth: tls.RequireAndVerifyClientCert,
ClientCAs: caPool, // trusted CA for clients
},
}
log.Fatal(server.ListenAndServeTLS("", ""))
Here the server rejects any client without a valid cert signed by your CA.
If you front services with NGINX or HAProxy, you can also enforce mTLS there. Example for NGINX upstreams:
upstream backend {
server backend1.example.com:443 ssl verify required;
server backend2.example.com:443 ssl verify required;
}
server {
listen 443 ssl;
ssl_client_certificate /etc/nginx/ca.crt;
ssl_verify_client on;
}
This way, the edge enforces trust before traffic ever hits your services.
Lessons learned
- the crypto is easy; the hard part is issuing, rotating, and revoking certificates at scale. Tools like cert-manager (Kubernetes) or Vault PKI can automate this.
- mTLS adds a handshake on every connection. In high-QPS systems, you’ll want connection pooling to amortize that cost.
- when something fails, it might be because of expired certs, CA mismatches, or client misconfigurations. Good logging is essential.
Trade-offs
mTLS gives you strong service-to-service trust guarantees. The cost is operational complexity — you need a proper PKI and automation around certs. Without that, it will break in production.
For us, enabling mTLS across microservices was less about encryption (we already had TLS) and more about identity: proving that the caller was really the service it claimed to be. In a distributed system, that’s worth the extra complexity.