Configure Consul Connect with Kubernetes integration for secure service mesh communication

Advanced 45 min Apr 23, 2026 12 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Set up Consul Connect as a service mesh on Kubernetes with Helm, enabling automatic mTLS encryption, service discovery, and traffic routing between microservices for secure inter-service communication.

Prerequisites

  • Kubernetes cluster with admin access
  • Helm 3 installed
  • kubectl configured
  • At least 4GB RAM available for Consul components

What this solves

Consul Connect provides a service mesh that secures service-to-service communication with automatic mutual TLS encryption, service discovery, and traffic management. This tutorial shows you how to deploy Consul Connect on Kubernetes using Helm charts, configure service mesh networking with sidecar proxies, and implement secure communication policies between microservices.

Step-by-step installation

Update system packages and install dependencies

Start by updating your system and installing required tools including Helm and kubectl.

sudo apt update && sudo apt upgrade -y
sudo apt install -y curl wget gnupg lsb-release
sudo dnf update -y
sudo dnf install -y curl wget gnupg

Install kubectl

Download and install the Kubernetes command-line tool for cluster management.

curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
sudo mv kubectl /usr/local/bin/
kubectl version --client

Install Helm 3

Install Helm to manage Kubernetes applications using charts.

curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
helm version

Add HashiCorp Helm repository

Add the official HashiCorp Helm repository to access Consul charts.

helm repo add hashicorp https://helm.releases.hashicorp.com
helm repo update

Create Consul namespace

Create a dedicated namespace for Consul components to isolate them from other applications.

kubectl create namespace consul

Create Consul Connect configuration

Create a Helm values file to configure Consul with Connect service mesh enabled.

global:
  name: consul
  datacenter: dc1
  tls:
    enabled: true
    enableAutoEncrypt: true
  acls:
    manageSystemACLs: true
  gossipEncryption:
    autoGenerate: true

server:
  replicas: 3
  storage: 10Gi
  resources:
    requests:
      memory: 100Mi
      cpu: 100m
    limits:
      memory: 100Mi
      cpu: 100m

client:
  enabled: true
  resources:
    requests:
      memory: 100Mi
      cpu: 100m
    limits:
      memory: 100Mi
      cpu: 100m

connectInject:
  enabled: true
  default: false
  transparentProxy:
    defaultEnabled: true
  resources:
    requests:
      memory: 50Mi
      cpu: 50m
    limits:
      memory: 50Mi
      cpu: 50m

controller:
  enabled: true

meshGateway:
  enabled: true
  replicas: 1
  resources:
    requests:
      memory: 100Mi
      cpu: 100m
    limits:
      memory: 100Mi
      cpu: 100m

ui:
  enabled: true
  service:
    type: LoadBalancer

Deploy Consul with Connect

Install Consul using Helm with the Connect-enabled configuration.

helm install consul hashicorp/consul --namespace consul --values consul-values.yaml
kubectl get pods -n consul

Wait for deployment to complete

Monitor the deployment status and wait for all pods to be running.

kubectl wait --for=condition=ready pod --all -n consul --timeout=300s
kubectl get svc -n consul

Configure service mesh networking

Create service intentions

Define service-to-service communication policies using Consul intentions.

apiVersion: consul.hashicorp.com/v1alpha1
kind: ServiceIntentions
metadata:
  name: web-to-api
spec:
  destination:
    name: api
  sources:
  - name: web
    action: allow
---
api: consul.hashicorp.com/v1alpha1
kind: ServiceIntentions
metadata:
  name: api-to-database
spec:
  destination:
    name: database
  sources:
  - name: api
    action: allow

Apply service intentions

Deploy the service intentions to configure allowed communication paths.

kubectl apply -f service-intentions.yaml
kubectl get serviceintentions

Create example application with Connect

Deploy a sample application with Consul Connect sidecar injection enabled.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  labels:
    app: web
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
      annotations:
        consul.hashicorp.com/connect-inject: "true"
    spec:
      containers:
      - name: web
        image: nginx:1.25
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  selector:
    app: web
  ports:
  - port: 80
    targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
  labels:
    app: api
spec:
  replicas: 2
  selector:
    matchLabels:
      app: api
  template:
    metadata:
      labels:
        app: api
      annotations:
        consul.hashicorp.com/connect-inject: "true"
    spec:
      containers:
      - name: api
        image: hashicorp/http-echo:latest
        args:
        - -text="Hello from API"
        ports:
        - containerPort: 5678
---
apiVersion: v1
kind: Service
metadata:
  name: api
spec:
  selector:
    app: api
  ports:
  - port: 5678
    targetPort: 5678

Deploy the example application

Apply the application configuration with Connect sidecar injection.

kubectl apply -f example-app.yaml
kubectl get pods -l app=web
kubectl get pods -l app=api

Configure mTLS and traffic routing

Create traffic splitting configuration

Configure traffic routing and splitting between service versions.

apiVersion: consul.hashicorp.com/v1alpha1
kind: ServiceSplitter
metadata:
  name: api
spec:
  splits:
  - weight: 80
    service: api
    serviceSubset: v1
  - weight: 20
    service: api
    serviceSubset: v2
---
apiVersion: consul.hashicorp.com/v1alpha1
kind: ServiceResolver
metadata:
  name: api
spec:
  subsets:
    v1:
      filter: "Service.Meta.version == v1"
    v2:
      filter: "Service.Meta.version == v2"

Configure service defaults

Set default protocol and mesh gateway configuration for services.

apiVersion: consul.hashicorp.com/v1alpha1
kind: ServiceDefaults
metadata:
  name: web
spec:
  protocol: http
  meshGateway:
    mode: local
---
apiVersion: consul.hashicorp.com/v1alpha1
kind: ServiceDefaults
metadata:
  name: api
spec:
  protocol: http
  meshGateway:
    mode: local

Apply traffic routing configuration

Deploy the traffic management and service default configurations.

kubectl apply -f service-defaults.yaml
kubectl apply -f traffic-split.yaml
kubectl get servicedefaults
kubectl get servicesplitter

Configure ingress gateway

Set up an ingress gateway to expose services outside the mesh.

apiVersion: consul.hashicorp.com/v1alpha1
kind: IngressGateway
metadata:
  name: ingress-gateway
spec:
  listeners:
  - port: 8080
    protocol: http
    services:
    - name: web
---
apiVersion: v1
kind: Service
metadata:
  name: consul-ingress-gateway
  annotations:
    consul.hashicorp.com/service-name: ingress-gateway
spec:
  selector:
    app: consul
    component: ingress-gateway
  ports:
  - port: 8080
    targetPort: 8080
  type: LoadBalancer

Deploy ingress gateway

Apply the ingress gateway configuration to expose services externally.

kubectl apply -f ingress-gateway.yaml
kubectl get ingressgateway
kubectl get svc consul-ingress-gateway

Implement service discovery

Create service resolver for failover

Configure automatic failover between service instances.

apiVersion: consul.hashicorp.com/v1alpha1
kind: ServiceResolver
metadata:
  name: web
spec:
  defaultSubset: v1
  subsets:
    v1:
      filter: "Service.Meta.version == v1"
    v2:
      filter: "Service.Meta.version == v2"
  failover:
    v1:
    - service: web
      serviceSubset: v2

Configure health checks

Set up health checking for service discovery and load balancing.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-with-health
  labels:
    app: web-with-health
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web-with-health
  template:
    metadata:
      labels:
        app: web-with-health
      annotations:
        consul.hashicorp.com/connect-inject: "true"
        consul.hashicorp.com/service-meta-version: "v1"
        consul.hashicorp.com/connect-service-upstreams: "api:5678"
    spec:
      containers:
      - name: web
        image: nginx:1.25
        ports:
        - containerPort: 80
        livenessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 10
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 5

Apply service discovery configuration

Deploy the service resolver and health check configurations.

kubectl apply -f service-resolver-failover.yaml
kubectl apply -f health-check.yaml
kubectl get serviceresolver
kubectl get pods -l app=web-with-health

Configure monitoring and observability

Enable metrics collection

Configure Consul Connect to expose Prometheus metrics for monitoring.

global:
  name: consul
  datacenter: dc1
  metrics:
    enabled: true
    enableAgentMetrics: true
    agentMetricsRetentionTime: 1m
    enableGatewayMetrics: true
  tls:
    enabled: true
    enableAutoEncrypt: true
  acls:
    manageSystemACLs: true
  gossipEncryption:
    autoGenerate: true

server:
  replicas: 3
  storage: 10Gi
  extraConfig: |
    {
      "telemetry": {
        "prometheus_retention_time": "60s",
        "disable_hostname": true
      }
    }

connectInject:
  enabled: true
  default: false
  transparentProxy:
    defaultEnabled: true
  metrics:
    defaultEnabled: true
    defaultEnableMerging: true

Update Consul with metrics enabled

Upgrade the Consul deployment to enable metrics collection.

helm upgrade consul hashicorp/consul --namespace consul --values consul-metrics.yaml
kubectl rollout status deployment/consul-connect-injector -n consul

Verify your setup

kubectl get pods -n consul
kubectl get svc -n consul
kubectl logs -l app=consul -n consul --tail=10

Check Connect injection status

kubectl get pods -l app=web -o jsonpath='{.items[].spec.containers[].name}'

Verify service mesh connectivity

kubectl exec -it $(kubectl get pod -l app=web -o jsonpath='{.items[0].metadata.name}') -c web -- curl localhost:5678

Check Consul UI (if LoadBalancer is available)

kubectl get svc consul-ui -n consul

View service intentions

kubectl get serviceintentions

Check service mesh metrics

kubectl port-forward -n consul svc/consul-server 8500:8500 & curl http://localhost:8500/v1/agent/metrics?format=prometheus

Common issues

SymptomCauseFix
Pods stuck in pending stateInsufficient cluster resourcesCheck node capacity with kubectl describe nodes and scale cluster
Connect injection failingWebhook not readyCheck kubectl logs -l app=consul-connect-injector -n consul
Service communication blockedMissing service intentionsCreate intentions with kubectl apply -f service-intentions.yaml
mTLS certificate errorsTLS configuration mismatchVerify TLS settings in Helm values and restart pods
UI not accessibleService type misconfigurationChange UI service type to LoadBalancer or use port-forward
High memory usageDefault resource limits too highAdjust resource limits in Helm values file

Next steps

Running this in production?

Want this handled for you? Running this at scale adds a second layer of work: capacity planning, failover drills, cost control, and on-call. See how we run infrastructure like this for European teams.

Automated install script

Run this to automate the entire setup

Need help?

Don't want to manage this yourself?

We handle managed devops services for businesses that depend on uptime. From initial setup to ongoing operations.