# mTLS Setup (/sdks/elixir/how-to/tls/mtls-setup)



## Overview [#overview]

Standard TLS only proves the server's identity — the server itself accepts any client that knows the address and client ID. &#x2A;*Mutual TLS (mTLS)** closes that gap: the client also presents a certificate, so the server verifies who is connecting before accepting the connection. That matters on zero-trust networks and in regulated environments where "reaches the port" isn't an acceptable authorization model — the certificate becomes the credential.

The `tls:` option's `cacertfile`, `certfile`, and `keyfile`, combined with `verify: :verify_peer`, wire in three artifacts at `KubeMQ.Client.start_link/1`: the CA certificate (to verify the server, same as one-way TLS) plus the client's own certificate and private key (for the server to verify in return). Verification happens during the handshake, before any messaging traffic flows — a failed handshake surfaces as an `{:error, reason}` tuple.

**Gotchas:** the certificate and key must be a matched pair signed by a CA the server trusts — a mismatch fails the handshake outright; all three files must be valid, unexpired PEM, and expiry breaks connections with no warning; and the CA that signed the *client* cert isn't necessarily the CA that verifies the *server* — mixing them up causes "works with TLS, fails with mTLS" confusion.

## Prerequisites [#prerequisites]

* KubeMQ server running with mTLS enabled
* CA certificate, client certificate, and client key files available
* Elixir SDK installed (`{:kubemq, "~> 1.0"}` in mix.exs)

## Code [#code]

```elixir title="main.exs"
ca_cert = System.get_env("KUBEMQ_CA_CERT", "/path/to/ca.pem")
client_cert = System.get_env("KUBEMQ_CLIENT_CERT", "/path/to/client.pem")
client_key = System.get_env("KUBEMQ_CLIENT_KEY", "/path/to/client-key.pem")

IO.puts("Connecting with mTLS (mutual TLS)...")
IO.puts("  CA cert: #{ca_cert}")
IO.puts("  Client cert: #{client_cert}")
IO.puts("  Client key: #{client_key}")

case KubeMQ.Client.start_link(
       address: "localhost:50000",
       client_id: "elixir-mtls-example",
       tls: [
         cacertfile: ca_cert,
         certfile: client_cert,
         keyfile: client_key,
         verify: :verify_peer
       ]
     ) do
  {:ok, client} ->
    IO.puts("mTLS connection established!")

    case KubeMQ.Client.ping(client) do
      {:ok, info} -> IO.puts("Server version: #{info.version}")
      {:error, err} -> IO.puts("Ping failed: #{err.message}")
    end

    KubeMQ.Client.close(client)

  {:error, reason} ->
    IO.puts("mTLS connection failed: #{inspect(reason)}")
    IO.puts("Ensure all certificate paths are correct and broker has mTLS enabled.")
end
```

## How It Works [#how-it-works]

* `certfile` and `keyfile` specify the client's certificate and private key
* `verify: :verify_peer` enables mutual verification
* The server validates the client certificate against its trusted CA
* All certificate paths should be stored as environment variables in production

## Related [#related]

* [TLS Setup](/sdks/elixir/how-to/tls/tls-setup)
* [Token Auth](/sdks/elixir/how-to/connection/token-auth)
