Run locally with Docker Compose
This guide walks you through running the complete Kessel stack on your local machine with a single command. The setup includes Kessel Inventory API, Kessel Relations API, SpiceDB, RBAC, Kafka, Kafka Connect, and Kessel Inventory Consumer — everything you need for development and integration testing.
Prerequisites
Section titled “Prerequisites”-
Podman or Docker with the
composeplugin.The startup script auto-detects Podman or Docker. Ensure the
podman compose(ordocker compose) subcommand is available — the standalonedocker-composebinary is not supported. -
yq — used by the startup script to extract RBAC role definitions from the upstream configmap.
Terminal window go install github.com/mikefarah/yq/v4@latestTerminal window brew install yqTerminal window dnf install yq -
curl — used to download the SpiceDB schema and RBAC configuration. Usually pre-installed on Linux and macOS.
-
Git — to clone the repository.
Start the stack
Section titled “Start the stack”-
Clone the Inventory API repository.
Terminal window git clone https://github.com/project-kessel/inventory-api.gitcd inventory-api -
Start all services.
Terminal window make kessel-upThis command:
- Creates a shared
kesselnetwork (if it doesn’t already exist) - Downloads the SpiceDB schema from the stage rbac-config repo
- Fetches and extracts RBAC role definitions from the same repo using
yq - Starts all services via compose with the appropriate profiles
- Creates a shared
To check that all containers are running and healthy (Docker users: substitute docker for podman):
podman compose -f development/full-kessel/docker-compose.yaml \ --profile relations --profile consumer --profile rbac \ ps --format "table {{.Name}}\t{{.Status}}"Services and ports
Section titled “Services and ports”| Port | Service | Protocol |
|---|---|---|
| 8081 | Inventory API | HTTP |
| 9081 | Inventory API | gRPC |
| 8000 | Relations API* | HTTP |
| 9000 | Relations API* | gRPC |
| 50051 | SpiceDB | gRPC |
| 9080 | RBAC Server | HTTP |
| 8083 | Kafka Connect | HTTP |
| 9092 | Kafka | — |
| 5432 | SpiceDB Database | Postgres |
| 5433 | Inventory Database | Postgres |
| 15432 | RBAC Database | Postgres |
| 6379 | Redis | — |
*Relations API will be absorbed into Inventory API in a future release. The Relations API service and its ports will be removed from the compose setup at that time. SpiceDB and its database will remain — Inventory API will use them directly.
Verify the setup
Section titled “Verify the setup”Quick smoke tests to confirm the stack is working:
-
Inventory API (HTTP)
Terminal window curl -sf http://localhost:8081/api/kessel/v1/livez -
RBAC Server (HTTP)
Terminal window curl -sf http://localhost:9080/metrics | head -5 -
Kafka Connect (HTTP)
Terminal window curl -sf http://localhost:8083/connectors
Run the integration test
Section titled “Run the integration test”For a comprehensive end-to-end validation, run the integration test suite. This exercises the full service flow: tenant bootstrapping via RBAC V2, simulated host events via Kafka, Inventory Consumer processing, Relations API tuple verification, and resource deletion.
make kessel-compose-integration-testThis requires additional tools: kcat, grpcurl, jq, and curl.
Configuration
Section titled “Configuration”Custom SpiceDB schema
Section titled “Custom SpiceDB schema”To use your own SpiceDB schema instead of the default one downloaded from GitHub:
SCHEMA_ZED_FILE=/path/to/your/schema.zed make kessel-upYou can also change the download URL by editing SCHEMA_ZED_URL in development/full-kessel/.env.
Custom service images
Section titled “Custom service images”Edit development/full-kessel/.env to use locally built or alternative images:
INVENTORY_API_IMAGE=localhost/kessel-inventory:devRELATIONS_API_IMAGE=localhost/relations-api:devINVENTORY_CONSUMER_IMAGE=localhost/inventory-consumer:devRBAC_IMAGE=localhost/rbac:devMonitoring stack
Section titled “Monitoring stack”To include Prometheus, Grafana, and Alertmanager:
make kessel-up-monitoring| Port | Service |
|---|---|
| 9050 | Prometheus |
| 3000 | Grafana |
| 9093 | Alertmanager |
Grafana is available at http://localhost:3000 with default login admin / admin.
It is pre-loaded with a local Prometheus datasource and the current Kessel dashboards.
Register a custom Debezium connector
Section titled “Register a custom Debezium connector”If your service uses the outbox pattern with Debezium CDC to report resources, you can register your own connector against the Kafka Connect pod that is already running in the full-kessel stack.
-
Create an outbox table in your service’s database. The table schema must match the Debezium Outbox Event Router format:
CREATE TABLE outbox (id UUID NOT NULL,aggregatetype VARCHAR(255) NOT NULL,aggregateid VARCHAR(255) NOT NULL,version VARCHAR(255) NOT NULL,operation VARCHAR(255) NOT NULL,payload JSONB,PRIMARY KEY (id)); -
Ensure your database has WAL-level set to
logical. This is required for Debezium to read from the PostgreSQL write-ahead log. If you’re adding a new database to the compose file, include-c wal_level=logicalin the Postgres command. -
Create a connector configuration file. This example connects to a database called
myservice-dband captures changes frompublic.outbox:my-service-connector.json {"name": "my-service-outbox-connector","config": {"connector.class": "io.debezium.connector.postgresql.PostgresConnector","database.server.name": "my-service","database.dbname": "my_database","database.hostname": "myservice-db","database.port": "5432","database.user": "postgres","database.password": "postgres","topic.prefix": "my-service","table.include.list": "public.outbox","transforms": "outbox","transforms.outbox.type": "io.debezium.transforms.outbox.EventRouter","transforms.outbox.table.fields.additional.placement": "operation:header,version:header","transforms.outbox.table.expand.json.payload": true,"value.converter": "org.apache.kafka.connect.json.JsonConverter","plugin.name": "pgoutput","slot.name": "my_service_debezium","snapshot.mode": "no_data","poll.interval.ms": 250}}Key fields to customize:
nameandslot.name: Must be unique across all connectors on the Kafka Connect poddatabase.*: Connection details for your service’s databasetable.include.list: The schema-qualified outbox table nametopic.prefix: Debezium uses this withaggregatetypeto form the output topic name (outbox.event.{aggregatetype})
-
Register the connector with the Kafka Connect REST API:
Terminal window curl -sf -X POST http://localhost:8083/connectors \-H 'Content-Type: application/json' \-d @my-service-connector.json -
Verify the connector is running:
Terminal window curl -sf http://localhost:8083/connectors/my-service-outbox-connector/status | jq .The output should show
"state": "RUNNING"for both the connector and its task.
Tear down
Section titled “Tear down”make kessel-downThis stops and removes all containers but preserves volumes (database data, Grafana state).
Troubleshooting
Section titled “Troubleshooting”Port conflicts
Section titled “Port conflicts”The compose file uses fixed host ports. If a port is already in use, podman compose up fails with an “address already in use” error. Find the conflicting process:
lsof -i :8081# orss -tlnp | grep 8081Stop the conflicting service before restarting.
Container keeps restarting
Section titled “Container keeps restarting”Check the logs for the specific service:
podman compose -f development/full-kessel/docker-compose.yaml logs inventory-api --tail 50Common causes:
- Database not ready — usually self-resolves via healthcheck dependencies. Wait 30–60 seconds and check again.
- Schema download failure — network issue reaching GitHub. See below.
Schema download fails
Section titled “Schema download fails”The startup script downloads schema.zed and RBAC role definitions from GitHub. If this fails (e.g., behind a firewall or VPN), use local files:
SCHEMA_ZED_FILE=./my-schema.zed RBAC_CONFIG_FILE=./my-rbac-config.yml make kessel-upPodman-specific issues
Section titled “Podman-specific issues”Services not communicating
Section titled “Services not communicating”Verify all containers are on the shared network:
podman network inspect kessel --format '{{range .Containers}}{{.Name}} {{end}}'If the network is missing, make kessel-down followed by make kessel-up recreates it.