Run a service locally

Running a service locally is helpful to test and debug. The following sections provide commands for starting and stopping a single service locally.

Prerequisites

In order to run your service locally, you’ll need to have the following prerequisites:

  • Docker 20.10.14 or higher

  • Access to the gcr.io/kalix-public container registry. This is a public container registry that provides a Kalix Proxy container suited for local development. Make sure this registry is not blocked by your firewall.

The samples provided with the Kalix SDKs all have docker-compose files preconfigured to run your service locally.

Starting your service

To start your service locally, run the following command from the root of your project:

mvn kalix:runAll

This command will start your Kalix service and a Kalix Proxy using the included docker-compose.yml file.

If you prefer, you can instead start docker-compose manually by running docker-compose up in one terminal and in another terminal start your Kalix service with:

mvn kalix:run

Invoking your service

After you start the service it will accept invocations on localhost:9000. You can use cURL to invoke your service.

As an example, we will use the customer-registry sample.

Using cURL

Create a customer:

Linux or macOS
curl localhost:9000/customer/one/create \
  --header "Content-Type: application/json" \
  -XPOST \
  --data '{"email":"test@example.com","name":"Test","address":{"street":"Test street 25","city":"Test City"}}'
Windows 10+
curl localhost:9000/customer/one/create ^
  --header "Content-Type: application/json" ^
  -XPOST ^
  --data '{"email":"test@example.com","name":"Test","address":{"street":"Test street 25","city":"Test City"}}'

Retrieve an existing customer:

curl -XGET localhost:9000/customer/one

Shut down the service

Use Ctrl+C to shut down the service. When stopping your service, it will also shutdown the Kalix Proxy container and any other container you have defined in docker-compose.yml. Unless you have chosen to start docker-compose manually in a separate terminal, in which case you will need to stop it manually as well.

Running multiple services locally

A typical Kalix application is composed of one or more services deployed to the same Kalix project. When deployed under the same Kalix project, two different services can make calls to each other or subscribe to each other’s event streams by simply using their logical names.

The same can be done on your local machine by configuring the services to run on different ports and by configuring them to "discover" each other using some extra configurations.

In this section, we will show you how to configure your local development environment to run two services and have them call each other. For that we will use two of our existing samples: customer-registry and customer-registry-subscriber.

The customer-registry sample provides a service to register customers and the customer-registry-subscriber subscribes to an event stream produced by the customer-registry service, building a View from it.

Customer Registry Sample

The docker-compose.yml file from customer-registry is left untouched and use the usual default ports. The user function will run on port 8080 and the Kalix Proxy on port 9000.

version: "3"
services:
  kalix-proxy:
    image: gcr.io/kalix-public/kalix-proxy:1.1.18
    ports:
      - "9000:9000"
    extra_hosts:
      - "host.docker.internal:host-gateway"
    environment:
      JAVA_TOOL_OPTIONS: >
        -Dconfig.resource=dev-mode.conf
        -Dlogback.configurationFile=logback-dev-mode.xml
      USER_FUNCTION_HOST: ${USER_FUNCTION_HOST:-host.docker.internal}
      USER_FUNCTION_PORT: ${USER_FUNCTION_PORT:-8080}

Customer Registry Subscriber Sample

On the other hand, in the customer-registry-subscriber we will use port 8081 and 9001 respectively to avoid port conflicts with the customer-registry service.

Moreover, since customer-registry-subscriber needs to subscribe to customer-registry and since we will be running it on our local machine, we need to show it where to find the customer-registry service.

This is done by passing an extra property (kalix.dev-mode.service-port-mappings.customer-registry) to its Kalix Proxy to let it create a mapping between the logical name customer-registry and the host and port where the customer-registry service is running.

Note that you need to add service port mappings to the Kalix Proxy configuration for the service that depends on the other service. Here, customer-registry-subscriber depends on customer-registry, therefore we add the service port mapping to the Kalix Proxy associated with the customer-registry-subscriber.

version: "3"
services:
  kalix-proxy:
    image: gcr.io/kalix-public/kalix-proxy:1.1.18
    ports:
      - "9001:9000"
    extra_hosts:
      - "host.docker.internal:host-gateway"
    environment:
      JAVA_TOOL_OPTIONS: >
        -Dconfig.resource=dev-mode.conf
        -Dlogback.configurationFile=logback-dev-mode.xml
        -Dkalix.dev-mode.service-port-mappings.customer-registry=host.docker.internal:9000
      USER_FUNCTION_HOST: ${USER_FUNCTION_HOST:-host.docker.internal}
      USER_FUNCTION_PORT: "8081"

With both services configured, we can start them independently by running mvn kalix:runAll in two separate terminals.

From a third terminal, we can create a customer on customer-registry service.

Linux or macOS
curl localhost:9000/customer/one/create \
  --header "Content-Type: application/json" \
  -XPOST \
  --data '{"email":"test@example.com","name":"Test","address":{"street":"Test street 25","city":"Test City"}}'
Windows 10+
curl localhost:9000/customer/one/create ^
  --header "Content-Type: application/json" ^
  -XPOST ^
  --data '{"email":"test@example.com","name":"Test","address":{"street":"Test street 25","city":"Test City"}}'

While watching the logs in customer-registry-subscriber service, we will see it receiving the customer created event. After that we can query its View.

curl localhost:9001/customers/by_name/Test

Running other services with Docker

In the previous example, we run two services independently with mvn kalix:runAll. Each time, the service is started together with a Kalix Proxy running in a Docker container.

An alternative is to build a larger docker-compose file containing dependent services. In the case of customer-registry-subscriber, we can have a second docker-compose file containing a Kalix Proxy for customer-registry-subscriber, a Kalix Proxy for customer-registry and the customer-registry itself.

At the root of the customer-registry-subscriber service, we can find an alternative docker-compose file called docker-compose-integration.yml.

version: "3"
services:
  kalix-proxy:
    image: gcr.io/kalix-public/kalix-proxy:1.1.18
    depends_on:
      - kalix-proxy-customer-registry
    ports:
      - "9001:9000"
    extra_hosts:
      - "host.docker.internal:host-gateway"
    environment:
      JAVA_TOOL_OPTIONS: >
        -Dconfig.resource=dev-mode.conf
        -Dlogback.configurationFile=logback-dev-mode.xml
        -Dkalix.dev-mode.service-port-mappings.customer-registry=host.docker.internal:9000
      USER_FUNCTION_HOST: ${USER_FUNCTION_HOST:-host.docker.internal}
      USER_FUNCTION_PORT: "8081"

  kalix-proxy-customer-registry:
    image: gcr.io/kalix-public/kalix-proxy:1.1.18
    ports:
      - "9000:9000"
    extra_hosts:
      - "host.docker.internal:host-gateway"
    environment:
      JAVA_TOOL_OPTIONS: >
        -Dconfig.resource=dev-mode.conf
        -Dlogback.configurationFile=logback-dev-mode.xml
      USER_FUNCTION_HOST: ${USER_FUNCTION_HOST:-host.docker.internal}
      USER_FUNCTION_PORT: "8080"

  customer-registry:
    image: my-docker-repo/eventsourced-customer-registry:latest
    ports:
      - "8080:8080"
    environment:
      HOST: customer-registry

But first, we need to build an image for customer-registry. For local development, we don’t need to publish it to a remote container registry. It suffices to build it locally.

We can build the image by calling the following command at the root of customer-registry:

mvn package docker:build

Next we can run customer-registry-subscriber and instead pass docker-compose-integration.yml to it.

Run the following command at the root of customer-registry-subscriber:

mvn -Dkalix.dev-mode.docker-compose-file=docker-compose-integration.yml kalix:runAll

This time, kalix:runAll will start docker-compose-integration.yml instead. The customer-registry service and its companion Kalix Proxy will run alongside customer-registry-subscriber and its own Kalix Proxy.

Note that the configuration is exactly the same except that now we are running one single mvn kalix:runAll command and the docker-compose file we are using contains all the dependencies required by customer-registry-subscriber.

This approach can be extended to any service you might want to integrate with. It can be any other Kalix service that you plan to deploy to the same Kalix project or even external services. The only requirement is to have a Docker image for it.