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.


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

  • Docker 20.10.14 or higher

  • Access to the container registry

The samples provided with the Kalix SDKs all have docker-compose files preconfigured to run your service locally. They also contain the configuration to start a local Google Pub/Sub emulator that the Kalix proxy will connect to when you make use of topic-based Eventing in your service.

Starting your service

To start the proxy, run the following command from the directory containing the docker-compose.yml file:

  1. Start the proxy

    docker compose up
  2. Start the service

    npm start

By default this command will start your containers in foreground mode. This means the terminal window will show all output from all containers. To start in "detached" mode, add the -d or --detach flag to the docker compose commands.

Invoking your service

After you start the service it will accept invocations on localhost:9000. You can use cURL to invoke your service. For services developed using a protocol-first SDK, you can also use gRPCurl.

Using cURL

Linux or macOS
curl \
  -XPOST \ (1)
  -H "Content-Type: application/json" \ (2)
  -d '{"counterId": "foo"}' \ (3)
  localhost:9000/com.example.CounterService/GetCurrentCounter (4)
Windows 10+
curl ^
  -XPOST ^ (1)
  -H "Content-Type: application/json" ^ (2)
  -d '{"counterId": "foo"}' ^ (3)
  localhost:9000/com.example.CounterService/GetCurrentCounter (4)
1 The XPOST flag indicates cURL will send a POST request
2 The content type for this request is set to application/json
3 The message payload in JSON format
4 The URL with the RPC procedure name which is deduced from the protobuf definition of the component you’re calling.

Using gRPCurl

For protocol-first SDKs, inspect your services with the gRPC list and describe commands:

grpcurl -plaintext localhost:9000 list

grpcurl -plaintext localhost:9000 describe com.example.CounterService

Be sure to use gRPCurl 1.8.7 (or above) since previous versions have been found to have some issues while describing an endpoint.

Get a current value from the CounterService:

Linux or macOS
grpcurl \
  -d '{"counterId": "foo"}' \ (1)
  -plaintext localhost:9000 \ (2)
  com.example.CounterService/GetCurrentCounter (3)
Windows 10+
grpcurl ^
  -d '{"counterId": "foo"}' ^ (1)
  -plaintext localhost:9000 ^ (2)
  com.example.CounterService/GetCurrentCounter (3)
1 The message payload in JSON format with -d
2 The address of the proxy (using plaintext instead of TLS)
3 The RPC procedure name which is deduced from the protobuf definition of the component you’re calling.

Shut down the proxy

To shut down the proxy, which removes all data kept in memory, run the command below:

docker stop kalix-proxy

To delete the proxy container, run

docker rm kalix-proxy

Running multiple services

You can run multiple services locally at the same time. To do that:

  1. Each service will need the user function to run on its own port.

  2. Each service will need its own proxy container and each proxy container will need its own port.

See below how to do such configuration.

Run user function on a custom port

When using the defaults, the Kalix proxy connects to the user function on port 8080. However, if you’re looking to have 2 services running at the same time, the second service will need to be run at a different port (e.g. 8081).

export PORT=8081 (1)
npm start
1 Setting this user function to run on port 8081.

Run Kalix Proxy on a custom port

To update the docker compose file to run multiple proxy instances, add the snippet below:

kalix-proxy: (1)
    - "900x:900x" (2)
    -Dkalix.proxy.http-port=900x (2)
    USER_FUNCTION_HOST: ${USER_FUNCTION_HOST:-host.docker.internal}
1 This name has to be unique in your docker compose file (docker compose uses this name to uniquely identify the running container).
2 The host port ("900x") in the port mapping must be unique for each proxy instance. Note that the internal proxy port should also be changed hence the extra -Dkalix.proxy.http-port=900x option.
3 The user function port must be unique for each service you start. See the previous section for how to define this.