Deploy Kuma on Docker

This quick start guide demonstrates how to run Kuma in Universal mode using Docker containers.

You’ll set up and secure a simple demo application to explore how Kuma works. The application consists of two services:

  • demo-app: A web application that lets you increment a numeric counter.
  • redis: A data store that keeps the counter’s value.
 
---
title: service graph of the demo app
---
flowchart LR
demo-app(demo-app :5000)
redis(redis :6379)
demo-app --> redis
  

Prerequisites

  1. Make sure the following tools are installed and ready to use:

    • docker
    • curl
    • jq
    • base64

    Note: This guide has been tested with Docker Engine, Docker Desktop, OrbStack, and Colima. For Colima, a small adjustment is required (explained later).

  2. If you previously followed the Deploy Kuma on Universal quickstart on the same machine, we recommend cleaning up your environment to ensure no control plane, Redis, demo-app, or their data plane proxies are still running.

    This isn’t mandatory, but it’s easy to accidentally mix up ports when typing commands manually, leading to unexpected results. If you copy the commands from this guide exactly or know what you’re doing and want to compare results between guides, you can skip this step.

Prepare the environment

  1. Install Kuma

    You can download and install Kuma using the official installer. The installer automatically detects your operating system (Amazon Linux, CentOS, RedHat, Debian, Ubuntu, macOS) and downloads the appropriate binaries:

    curl --location https://kuma.io/installer.sh | VERSION="2.9.1" sh -
    

    To finalize the installation add the Kuma binaries to your system’s PATH so the commands are easily accessible:

    export PATH="$(pwd)/kuma-2.9.1/bin:$PATH"
    
  2. Prepare a temporary directory

    Set up a temporary directory to store resources like data plane tokens, Dataplane templates, and logs. Export its path to the KUMA_DEMO_TMP environment variable for use in later steps. Ensure the path does not end with a trailing /.

    Important: If you are using Colima, you need to adjust the path. Colima only allows sharing paths from the HOME directory or /tmp/colima/. Instead of /tmp/kuma-demo, you can use /tmp/colima/kuma-demo.

    export KUMA_DEMO_TMP="/tmp/kuma-demo"
    

    Check if the directory exists and is empty, and create it if necessary:

    mkdir -p "$KUMA_DEMO_TMP"
    
  3. Prepare a Dataplane resource template

    Create a reusable Dataplane resource template for services:

    echo 'type: Dataplane
    mesh: default
    name: {{ name }}
    networking:
      address: {{ address }}
      inbound:
        - port: {{ port }}
          tags:
            kuma.io/service: {{ name }}
            kuma.io/protocol: {{ protocol }}
      transparentProxying:
        redirectPortInbound: 15006
        redirectPortOutbound: 15001' > $KUMA_DEMO_TMP/dataplane.yaml
    

    This template simplifies creating Dataplane configurations for different services by replacing dynamic values during deployment.

  4. Prepare a transparent proxy configuration file

    echo 'kumaDPUser: kuma-data-plane-proxy
    redirect:
      dns:
        enabled: true
    verbose: true' > $KUMA_DEMO_TMP/config-transparent-proxy.yaml
    
  5. Create a Docker network

    Set up a separate Docker network for the containers. Use IP addresses in the 172.56.78.0/24 range or customize as needed:

    docker network create \
      --subnet 172.56.0.0/16 \
      --ip-range 172.56.78.0/24 \
      --gateway 172.56.78.254 \
      kuma-demo
    

Set up the control plane

  1. Start the control plane

    Run the Kuma control plane in a Docker container:

    docker run \
      --detach \
      --name kuma-demo-control-plane \
      --hostname control-plane \
      --network kuma-demo \
      --ip 172.56.78.1 \
      --publish 25681:5681 \
      --volume $KUMA_DEMO_TMP:/demo \
      kumahq/kuma-cp:2.9.1 run
    

    You can now access the Kuma user interface (GUI) at http://localhost:25681/gui

  2. Configure kumactl

    To use kumactl with our Kuma deployment, we need to connect it to the control plane we set up earlier.

    1. Retrieve the admin token

      Run the following command to get the admin token from the control plane:

      export KUMA_DEMO_ADMIN_TOKEN="$( 
        docker exec --tty --interactive kuma-demo-control-plane \
          wget --quiet --output-document - \
          http://localhost:5681/global-secrets/admin-user-token \
          | jq --raw-output .data \
          | base64 --decode
      )"
      
    2. Connect to the control plane

      Use the retrieved token to link kumactl to the control plane:

      kumactl config control-planes add \
        --name kuma-demo \
        --address http://localhost:25681 \
        --auth-type tokens \
        --auth-conf "token=$KUMA_DEMO_ADMIN_TOKEN" \
        --skip-verify
      
    3. Verify the connection

      Run this command to check if the connection is working:

      kumactl get meshes
      

      You should see a list of meshes with one entry: default. This confirms the configuration is successful.

  3. Configure the default mesh

    Set the default mesh to use MeshServices in exclusive mode. MeshServices are explicit resources that represent destinations for traffic in the mesh. They define which Dataplanes serve the traffic, as well as the available ports, IPs, and hostnames. This configuration ensures a clearer and more precise way to manage services and traffic routing in the mesh.

    echo 'type: Mesh
    name: default
    meshServices:
      mode: Exclusive' | kumactl apply --file -
    

Set up services

Redis

  1. Generate a data plane token

    Generate a token that the Redis data plane proxy will use to authenticate with the control plane.

    kumactl generate dataplane-token \
      --tag "kuma.io/service=redis" \
      --valid-for 720h \
      > $KUMA_DEMO_TMP/token-redis
    
  2. Start the Redis container

    docker run \
      --detach \
      --name kuma-demo-redis \
      --hostname redis \
      --network kuma-demo \
      --ip 172.56.78.2 \
      --volume $KUMA_DEMO_TMP:/demo \
      redis:7.4.1 redis-server --protected-mode no
    
  3. Prepare the Redis container

    Enter the container to run further commands.

    docker exec --tty --interactive --privileged kuma-demo-redis bash
    

    Important: The following steps must be executed inside the container.

    1. Set a zone name

      Give the Redis instance a zone name. The demo application will use this name to show which Redis instance is being accessed.

      redis-cli set zone local-demo-zone
      
    2. Install required tools

      Install the necessary tools for downloading Kuma binaries and setting up the transparent proxy:

      • curl: Needed to download the Kuma binaries.
      • iptables: Required to configure the transparent proxy.

      Run the following command:

      apt-get update && \
        apt-get install --yes curl iptables
      
    3. Install Kuma

      Now, install Kuma and move its binaries to /usr/local/bin/ so they are accessible to all users:

      curl --location https://kuma.io/installer.sh | VERSION="2.9.1" sh -
            
      mv kuma-2.9.1/bin/* /usr/local/bin/
      
    4. Create a separate user for the data plane proxy

      useradd --uid 5678 --user-group kuma-data-plane-proxy
      
    5. Start the data plane proxy

      runuser --user kuma-data-plane-proxy -- \
        /usr/local/bin/kuma-dp run \
        --cp-address https://control-plane:5678 \
        --dataplane-token-file /demo/token-redis \
        --dataplane-file /demo/dataplane.yaml \
        --dataplane-var "name=redis" \
        --dataplane-var "address=172.56.78.2" \
        --dataplane-var "port=6379" \
        --dataplane-var "protocol=tcp" \
        > /demo/logs-data-plane-proxy-redis.log 2>&1 &
      
    6. Install the transparent proxy

      Important: Make sure this command is executed inside the container. It changes iptables rules to redirect all traffic to the data plane proxy. Running it on your computer or a virtual machine without the data plane proxy can disrupt network connectivity. On a virtual machine, this might lock you out until you restart it.

      kumactl install transparent-proxy \
        --config-file /demo/config-transparent-proxy.yaml \
        > /demo/logs-transparent-proxy-install-redis.log 2>&1
      
    7. Exit the container

      Redis is now set up and running. You can safely exit the container as the configuration is complete:

      exit
      
  4. Check if service is running

    To confirm the service is set up correctly and running, use the kumactl to inspect the services:

    kumactl inspect services
    

    The output should show a single service, redis, with the status Online.

    You can also open the Kuma GUI at http://localhost:25681/gui/meshes/default/services/mesh-services. Look for the redis service, and verify that its state is Available.

Demo Application

The steps are the same as those explained earlier, with only the names changed. We won’t repeat the explanations here, but you can refer to the Redis service instructions if needed.

  1. Generate a data plane token

    kumactl generate dataplane-token \
      --tag "kuma.io/service=demo-app" \
      --valid-for 720h \
      > $KUMA_DEMO_TMP/token-demo-app
    
  2. Start the application container

    docker run \
      --detach \
      --name kuma-demo-app \
      --hostname demo-app \
      --network kuma-demo \
      --ip 172.56.78.3 \
      --publish 25000:5000 \
      --volume $KUMA_DEMO_TMP:/demo \
      --env "REDIS_HOST=redis.svc.mesh.local" \
      kumahq/kuma-demo
    
  3. Prepare the application container

    Enter the container to run further commands.

    docker exec \
      --tty \
      --interactive \
      --privileged \
      --workdir /root \
      kuma-demo-app bash
    

    Important: The following steps must be executed inside the container.

    1. Install required tools

      apt-get update && \
        apt-get install --yes curl iptables
      
    2. Install Kuma

      curl --location https://kuma.io/installer.sh | VERSION="2.9.1" sh -
            
      mv kuma-2.9.1/bin/* /usr/local/bin/
      
    3. Create a separate user for the data plane proxy

      useradd --uid 5678 --user-group kuma-data-plane-proxy
      
    4. Start the data plane proxy

      runuser --user kuma-data-plane-proxy -- \
        /usr/local/bin/kuma-dp run \
        --cp-address https://control-plane:5678 \
        --dataplane-token-file /demo/token-demo-app \
        --dataplane-file /demo/dataplane.yaml \
        --dataplane-var "name=demo-app" \
        --dataplane-var "address=172.56.78.3" \
        --dataplane-var "port=5000" \
        --dataplane-var "protocol=http" \
        > /demo/logs-data-plane-proxy-demo-app.log 2>&1 &     
      
    5. Install the transparent proxy

      Important: Make sure this command is executed inside the container. It changes iptables rules to redirect all traffic to the data plane proxy. Running it on your computer or a virtual machine without the data plane proxy can disrupt network connectivity. On a virtual machine, this might lock you out until you restart it.

      kumactl install transparent-proxy \
        --config-file /demo/config-transparent-proxy.yaml \
        > /demo/logs-transparent-proxy-install-demo-app.log 2>&1
      
    6. Exit the container

      Demo application is now set up and running. You can safely exit the container as the configuration is complete:

      exit
      
  4. Verify the application

    Open http://localhost:25000 in your browser and use the demo application to increment the counter. The demo application is now fully set up and running.

Introduction to zero-trust security

By default, the network is insecure and unencrypted. With Kuma, you can enable the Mutual TLS (mTLS) policy to secure the network. This works by setting up a Certificate Authority (CA) that automatically provides TLS certificates to your services (specifically to the data plane proxies running next to each service).

To enable Mutual TLS using a builtin CA backend, run the following command:

echo 'type: Mesh
name: default
meshServices:
  mode: Exclusive
mtls:
  enabledBackend: ca-1
  backends:
  - name: ca-1
    type: builtin' | kumactl apply --file -

After enabling mTLS, all traffic is encrypted and secure. However, you can no longer access the demo-app directly, meaning http://localhost:25000 will no longer work. This happens for two reasons:

  1. When mTLS is enabled, Kuma doesn’t create traffic permissions by default. This means no traffic will flow until you define a MeshTrafficPermission policy to allow demo-app to communicate with redis.

  2. When you try to call demo-app using a browser or other HTTP client, you are essentially acting as an external client without a valid TLS certificate. Since all services are now required to present a certificate signed by the ca-1 Certificate Authority, the connection is rejected. Only services within the default mesh, which are assigned valid certificates, can communicate with each other.

To address the first issue, you need to apply an appropriate MeshTrafficPermission policy:

echo 'type: MeshTrafficPermission 
name: allow-redis-from-demo-app
mesh: default 
spec: 
  targetRef: 
    kind: MeshSubset
    tags:
      kuma.io/service: redis
  from: 
  - targetRef: 
      kind: MeshSubset 
      tags:
        kuma.io/service: demo-app
    default: 
      action: Allow' | kumactl apply --file -

The second issue is a bit more challenging. You can’t just get the necessary certificate and set up your web browser to act as part of the mesh. To handle traffic from outside the mesh, you need a gateway proxy. You can use tools like Kong, or you can use the Built-in Gateway that Kuma provides.

Note: For more information, see the Managing incoming traffic with gateways section in the documentation.

In this guide, we’ll use the built-in gateway. It allows you to configure a data plane proxy to act as a gateway and manage external traffic securely.

Setting up the built-in gateway

The built-in gateway works like the data plane proxy for a regular service, but it requires its own configuration. Here’s how to set it up step by step.

  1. Create a Dataplane resource

    For regular services, we reused a single Dataplane configuration file and provided dynamic values (like names and addresses) when starting the data plane proxy. This made it easier to scale or deploy multiple instances. However, since we’re deploying only one instance of the gateway, we can simplify things by hardcoding all the values directly into the file, as shown below:

    echo 'type: Dataplane
    mesh: default
    name: gateway-instance-1
    networking:
      address: 172.56.78.4
      gateway:
        type: BUILTIN
        tags:
          kuma.io/service: gateway' > $KUMA_DEMO_TMP/dataplane-gateway.yaml
    

    If you prefer to keep the flexibility of dynamic values, you can use the same template mechanisms for the gateway’s Dataplane configuration as you did for regular services.

  2. Generate a data plane token

    The gateway proxy requires a data plane token to securely register with the control plane. You can generate the token using the following command:

    kumactl generate dataplane-token \
      --tag "kuma.io/service=gateway" \
      --valid-for 720h \
      > $KUMA_DEMO_TMP/token-gateway
    
  3. Start the gateway container

    With the configuration and token in place, you can start the gateway proxy as a container:

    docker run \
      --detach \
      --name kuma-demo-gateway \
      --hostname gateway \
      --network kuma-demo \
      --ip 172.56.78.4 \
      --publish 25001:5000 \
      --volume $KUMA_DEMO_TMP:/demo \
      kumahq/kuma-dp:2.9.1 run \
        --cp-address https://control-plane:5678 \
        --dataplane-token-file /demo/token-gateway \
        --dataplane-file /demo/dataplane-gateway.yaml \
        --dns-enabled="false"
    

    This command starts the gateway proxy and registers it with the control plane. However, the gateway is not yet ready to route traffic.

  4. Configure the gateway with MeshGateway

    To enable the gateway to accept external traffic, configure it with a MeshGateway. This setup defines listeners that specify the port, protocol, and tags for incoming traffic, allowing policies like MeshHTTPRoute or MeshTCPRoute to route traffic to services.

    Apply the configuration:

    echo 'type: MeshGateway
    mesh: default
    name: gateway
    selectors:
    - match:
        kuma.io/service: gateway
    conf:
      listeners:
      - port: 5000
        protocol: HTTP
        tags:
          port: http-5000' | kumactl apply --file -
    

    This sets up the gateway to listen on port 5000 using the HTTP protocol and adds a tag (port: http-5000) to identify this listener in routing policies.

    You can test the gateway by visiting http://localhost:25001. You should see a message saying no routes match this MeshGateway. This means the gateway is running, but no routes are set up yet to handle traffic.

  5. Create a route to connect the gateway to demo-app

    To route traffic from the gateway to the service, create a MeshHTTPRoute policy:

    echo 'type: MeshHTTPRoute
    name: gateway-demo-app-route
    mesh: default
    spec:
      targetRef:
        kind: MeshGateway
        name: gateway
        tags:
          port: http-5000
      to:
      - targetRef:
          kind: Mesh
        rules:
        - matches:
          - path:
              type: PathPrefix
              value: "/"
          default:
            backendRefs:
            - kind: MeshService
              name: demo-app' | kumactl apply --file -
    

    This route connects the gateway and its listener (port: http-5000) to the demo-app service. It forwards any requests with the path prefix / to demo-app.

    After setting up this route, the gateway will try to send traffic to demo-app. However, if you test it by visiting http://localhost:25001, you’ll see:

    RBAC: access denied
    

    This happens because there is no MeshTrafficPermission policy allowing traffic from the gateway to demo-app. You’ll need to create one in the next step.

  6. Allow traffic from the gateway to demo-app

    To fix the RBAC: access denied error, create a MeshTrafficPermission policy to allow the gateway to send traffic to demo-app:

    echo 'type: MeshTrafficPermission
    name: allow-demo-app-from-gateway
    mesh: default
    spec:
      targetRef:
        kind: MeshSubset
        tags:
          kuma.io/service: demo-app
      from:
      - targetRef:
          kind: MeshSubset
          tags:
            kuma.io/service: gateway
        default:
          action: Allow' | kumactl apply --file -
    

    This policy allows traffic from the gateway to demo-app. After applying it, you can access http://localhost:25001, and the traffic will reach the demo-app service successfully.

Cleanup

To clean up your environment, remove the Docker containers, network, temporary directory, and the control plane configuration from kumactl. Run the following commands:

kumactl config control-planes remove --name kuma-demo

docker rm --force \
   kuma-demo-control-plane \
   kuma-demo-redis \
   kuma-demo-app \
   kuma-demo-gateway

docker network rm kuma-demo

rm -rf "$KUMA_DEMO_TMP"