This tutorial provides a complete step-by-step demonstration of XZepr using Docker and Docker Compose. You will learn how to:
- Start backend services (PostgreSQL, Redpanda, Keycloak)
- Build and run XZepr in a Docker container
- Create users with the admin CLI
- Use Redpanda Console to monitor event streams
- Interact with the GraphQL Playground
- Test the API with curl
- Docker Engine 20.10 or later
- Docker Compose v2.0 or later
- Basic understanding of Docker and command line
- At least 4GB of available RAM
- Ports available: 5432, 8042, 8080, 8081, 18081, 18082, 19092, 19644
┌─────────────────────────────────────────────────────────┐
│ XZepr Server (Docker Container) │
│ - REST API (port 8042) │
│ - GraphQL API (port 8042) │
└─────────────────────────────────────────────────────────┘
│
├─── PostgreSQL (port 5432)
├─── Redpanda (ports 19092, 18081)
├─── Redpanda Console (port 8081)
└─── Keycloak (port 8080)
git clone <repository-url>
cd xzeprXZepr requires TLS certificates for secure communication. Generate self-signed certificates for development:
# Create certificates directory
mkdir -p certs
# Generate self-signed certificate (valid for 365 days)
openssl req -x509 -newkey rsa:4096 \
-keyout certs/key.pem \
-out certs/cert.pem \
-days 365 -nodes \
-subj "/C=US/ST=State/L=City/O=XZepr/CN=localhost"
# Verify certificates were created
ls -lh certs/Expected output:
-rw-r--r-- 1 user user 1.9K cert.pem
-rw------- 1 user user 3.2K key.pem
Start PostgreSQL, Redpanda, and Keycloak using Docker Compose:
# Start backend services in detached mode
docker compose -f docker-compose.services.yaml up -d
# View logs to monitor startup
docker compose -f docker-compose.services.yaml logs -fWait for the following log messages indicating services are ready:
- PostgreSQL:
database system is ready to accept connections - Redpanda:
Successfully started Redpanda - Keycloak:
Keycloak started - Redpanda Console:
Listening on port 8080
Press Ctrl+C to exit log viewing.
Verify all services are running:
docker compose -f docker-compose.services.yaml psExpected output:
NAME IMAGE STATUS
postgres postgres:16 Up
keycloak quay.io/keycloak/keycloak:24.0 Up
redpanda-0 docker.redpanda.com/redpandadata/... Up
redpanda-console docker.redpanda.com/redpandadata/... Up
Build the XZepr Docker image:
# Build the image with tag 'xzepr:demo'
docker build -t xzepr:demo .
# Verify the image was created
docker images | grep xzeprExpected output:
xzepr demo <image-id> 2 minutes ago XXX MB
The build process may take 5-10 minutes on first run as it downloads dependencies and compiles the Rust code.
The XZepr Docker image includes sqlx-cli for managing database migrations. Run migrations to set up the schema:
# Run migrations using sqlx-cli built into the Docker image
docker run --rm \
--network xzepr_redpanda_network \
-e DATABASE_URL=postgres://xzepr:password@postgres:5432/xzepr \
xzepr:demo \
./sqlx migrate runThis command:
- Uses sqlx-cli which is built into the xzepr:demo image
- Tracks migrations in the
_sqlx_migrationstable - Is idempotent and safe to run multiple times
- Provides proper error handling and rollback support
Check migration status:
# View applied migrations
docker run --rm \
--network xzepr_redpanda_network \
-e DATABASE_URL=postgres://xzepr:password@postgres:5432/xzepr \
xzepr:demo \
./sqlx migrate infoExpected output:
Applied migrations:
20240101000001/create_users_table (applied)
20240101000002/create_events_table (applied)
20240101000003/create_event_receivers_table (applied)
Alternatively, if you have sqlx-cli installed locally:
export DATABASE_URL=postgres://xzepr:password@localhost:5432/xzepr
sqlx migrate runUse the XZepr admin CLI to create an administrative user:
# Create admin user
docker run --rm \
--network xzepr_redpanda_network \
-e XZEPR__DATABASE__URL=postgres://xzepr:password@postgres:5432/xzepr \
--entrypoint ./admin \
xzepr:demo \
create-user \
--username admin \
--email admin@xzepr.local \
--password SecurePassword123! \
--role adminExpected output:
✓ User created successfully!
ID: <uuid>
Username: admin
Roles: user, admin
Create users with different roles for testing:
# Create an EventManager user
docker run --rm \
--network xzepr_redpanda_network \
-e XZEPR__DATABASE__URL=postgres://xzepr:password@postgres:5432/xzepr \
--entrypoint ./admin \
xzepr:demo \
create-user \
--username eventmanager \
--email manager@xzepr.local \
--password Manager123! \
--role event_manager
# Create an EventViewer user
docker run --rm \
--network xzepr_redpanda_network \
-e XZEPR__DATABASE__URL=postgres://xzepr:password@postgres:5432/xzepr \
--entrypoint ./admin \
xzepr:demo \
create-user \
--username viewer \
--email viewer@xzepr.local \
--password Viewer123! \
--role event_viewer
# Create a regular User
docker run --rm \
--network xzepr_redpanda_network \
-e XZEPR__DATABASE__URL=postgres://xzepr:password@postgres:5432/xzepr \
--entrypoint ./admin \
xzepr:demo \
create-user \
--username user \
--email user@xzepr.local \
--password User123! \
--role userVerify users were created:
docker run --rm \
--network xzepr_redpanda_network \
-e XZEPR__DATABASE__URL=postgres://xzepr:password@postgres:5432/xzepr \
--entrypoint ./admin \
xzepr:demo \
list-usersExpected output:
ID Username Email Roles
----------------------------------------------------------------------------------------------------
<uuid> admin admin@xzepr.local user, admin
<uuid> eventmanager manager@xzepr.local user, event_manager
<uuid> viewer viewer@xzepr.local user, event_viewer
<uuid> user user@xzepr.local user
Generate an API key for programmatic access:
docker run --rm \
--network xzepr_redpanda_network \
-e XZEPR__DATABASE__URL=postgres://xzepr:password@postgres:5432/xzepr \
--entrypoint ./admin \
xzepr:demo \
generate-api-key \
--username admin \
--name "Demo API Key" \
--expires-days 30Expected output:
✓ API Key generated successfully!
Key ID: <uuid>
API Key: xzepr_<random-string>
Name: Demo API Key
Expires: <date>
IMPORTANT: Save this API key now. It will not be shown again.
Save the API key for later use. Store it in an environment variable:
export XZEPR_API_KEY="xzepr_<random-string>"Run the XZepr server in a Docker container:
docker run \
--name xzepr-server \
--network xzepr_redpanda_network \
-p 8042:8443 \
-e XZEPR__SERVER__HOST=0.0.0.0 \
-e XZEPR__SERVER__PORT=8443 \
-e XZEPR__SERVER__ENABLE_HTTPS=false \
-e XZEPR__DATABASE__URL=postgres://xzepr:password@postgres:5432/xzepr \
-e XZEPR__KAFKA__BROKERS=redpanda-0:9092 \
-e XZEPR__AUTH__KEYCLOAK__ISSUER_URL=http://keycloak:8080/realms/xzepr \
-e RUST_LOG=info,xzepr=debug \
-v "$(pwd)/certs:/app/certs:ro" \
xzepr:demoNote: We're running without HTTPS for simplicity in this demo. In production, set ENABLE_HTTPS=true.
View server logs:
docker logs -f xzepr-serverWait for the log message:
Server listening on http://0.0.0.0:8443
Health check: http://0.0.0.0:8443/health
API documentation: http://0.0.0.0:8443/api/v1
GraphQL endpoint: http://0.0.0.0:8443/graphql
GraphQL Playground: http://0.0.0.0:8443/graphql/playground
XZepr server ready to accept connections
Press Ctrl+C to exit log viewing.
Check that the server is responding:
curl http://localhost:8042/healthExpected output:
{
"status": "healthy",
"version": "0.1.0",
"timestamp": "2024-01-15T14:30:00Z"
}Check GraphQL health:
curl http://localhost:8042/graphql/healthExpected output:
{
"status": "healthy",
"service": "graphql"
}Open your browser and navigate to:
http://localhost:8081
Explore the Redpanda Console interface:
- View topics and messages
- Monitor consumer groups
- Check schema registry
- View cluster health
In the Redpanda Console:
- Click "Topics" in the sidebar
- Click "Create Topic"
- Enter name:
xzepr.demo.events - Set partitions:
3 - Click "Create"
Open your browser and navigate to:
http://localhost:8042/graphql/playground
The GraphQL Playground provides an interactive IDE for exploring the API.
In the playground, enter this query:
query {
eventReceivers(eventReceiver: {}) {
id
name
type
version
description
createdAt
}
}Click the "Play" button or press Ctrl+Enter to execute.
mutation {
createEventReceiver(
eventReceiver: {
name: "webhook-receiver"
type: "xzepr.event.webhook"
version: "1.0.0"
description: "Webhook event receiver for CI/CD"
schema: {
type: "object"
properties: { event: { type: "string" }, status: { type: "string" } }
required: ["event"]
}
}
)
}Expected response:
{
"data": {
"createEventReceiver": "<uuid>"
}
}Save the returned UUID for the next steps.
Query an event receiver by ID using variables:
Query:
query GetReceiver($id: ID!) {
eventReceiversById(id: $id) {
id
name
type
version
schema
fingerprint
}
}Variables (click "Query Variables" at bottom):
{
"id": "<uuid-from-previous-step>"
}mutation CreateGroup($input: CreateEventReceiverGroupInput!) {
createEventReceiverGroup(eventReceiverGroup: $input)
}Variables:
{
"input": {
"name": "production-webhooks",
"type": "webhook-group",
"version": "1.0.0",
"description": "Production webhook receivers",
"enabled": true,
"eventReceiverIds": ["<uuid-from-receiver>"]
}
}curl -X POST http://localhost:8042/api/v1/receivers \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $XZEPR_API_KEY" \
-d '{
"name": "rest-webhook",
"type": "webhook",
"version": "1.0.0",
"description": "REST API webhook receiver",
"schema": {
"type": "object",
"properties": {
"message": {"type": "string"}
}
}
}'Expected response:
{"data":"01K935TSPGKP0S63PKM5EPBW3F"}curl http://localhost:8042/api/v1/receivers \
-H "Authorization: Bearer $XZEPR_API_KEY"curl http://localhost:8042/api/v1/receivers/<receiver-id> \
-H "Authorization: Bearer $XZEPR_API_KEY"curl -X POST http://localhost:8042/api/v1/events \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $XZEPR_API_KEY" \
-d '{
"name": "build-completed",
"version": "1.0.0",
"release": "v2024.01.15",
"platform_id": "linux-x64",
"package": "myapp",
"description": "Build completed successfully",
"payload": {
"status": "success",
"duration": 120,
"tests_passed": 45
},
"event_receiver_id": "<receiver-id>",
"success": true
}'curl -X POST http://localhost:8042/api/v1/groups \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $XZEPR_API_KEY" \
-d '{
"name": "ci-cd-group",
"type": "ci-group",
"version": "1.0.0",
"description": "CI/CD event receivers",
"enabled": true,
"event_receiver_ids": ["<receiver-id>"]
}'curl -X POST http://localhost:8042/graphql \
-H "Content-Type: application/json" \
-d '{
"query": "{ eventReceivers(eventReceiver: {}) { id name type version } }"
}'curl -X POST http://localhost:8042/graphql \
-H "Content-Type: application/json" \
-d '{
"query": "query GetReceiver($id: ID!) { eventReceiversById(id: $id) { id name type } }",
"variables": {
"id": "<receiver-id>"
}
}'curl -X POST http://localhost:8042/graphql \
-H "Content-Type: application/json" \
-d '{
"query": "mutation CreateReceiver($input: CreateEventReceiverInput!) { createEventReceiver(eventReceiver: $input) }",
"variables": {
"input": {
"name": "curl-webhook",
"type": "webhook",
"version": "1.0.0",
"description": "Created via curl",
"schema": {
"type": "object",
"properties": {
"data": {"type": "string"}
}
}
}
}
}'- Go to Redpanda Console:
http://localhost:8081 - Click "Topics" in the sidebar
- Click on
xzepr.dev.eventstopic (if created) - View messages being produced by XZepr
- Inspect message keys, values, and headers
docker logs xzepr-serverView real-time logs:
docker logs -f xzepr-server# PostgreSQL logs
docker compose -f docker-compose.services.yaml logs postgres
# Redpanda logs
docker compose -f docker-compose.services.yaml logs redpanda-0
# Keycloak logs
docker compose -f docker-compose.services.yaml logs keycloakdocker run --rm \
--network xzepr_redpanda_network \
-e XZEPR__DATABASE__URL=postgres://xzepr:password@postgres:5432/xzepr \
--entrypoint ./admin \
xzepr:demo \
list-api-keys \
--username admindocker run --rm \
--network xzepr_redpanda_network \
-e XZEPR__DATABASE__URL=postgres://xzepr:password@postgres:5432/xzepr \
--entrypoint ./admin \
xzepr:demo \
add-role \
--username user \
--role event_managerdocker run --rm \
--network xzepr_redpanda_network \
-e XZEPR__DATABASE__URL=postgres://xzepr:password@postgres:5432/xzepr \
--entrypoint ./admin \
xzepr:demo \
remove-role \
--username user \
--role event_manager#!/bin/bash
# Save as test-workflow.sh
echo "1. Creating event receiver..."
RECEIVER_RESPONSE=$(curl -s -X POST http://localhost:8042/api/v1/receivers \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $XZEPR_API_KEY" \
-d '{
"name": "test-receiver",
"type": "webhook",
"version": "1.0.0",
"description": "Test receiver",
"schema": {"type": "object"}
}')
RECEIVER_ID=$(echo $RECEIVER_RESPONSE | jq .data)
echo "✓ Receiver created: $RECEIVER_ID"
echo ""
echo "2. Creating event..."
EVENT_RESPONSE=$(curl -s -X POST http://localhost:8042/api/v1/events \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $XZEPR_API_KEY" \
-d "{
\"name\": \"test-event\",
\"version\": \"1.0.0\",
\"release\": \"v1.0.0\",
\"platform_id\": \"linux\",
\"package\": \"test\",
\"description\": \"Test event\",
\"payload\": {\"test\": true},
\"event_receiver_id\": \"$RECEIVER_ID\",
\"success\": true
}")
EVENT_ID=$(echo $EVENT_RESPONSE | jq .data)
echo "✓ Event created: $EVENT_ID"
echo ""
echo "3. Creating receiver group..."
GROUP_RESPONSE=$(curl -s -X POST http://localhost:8042/api/v1/groups \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $XZEPR_API_KEY" \
-d "{
\"name\": \"test-group\",
\"type\": \"test\",
\"version\": \"1.0.0\",
\"description\": \"Test group\",
\"enabled\": true,
\"event_receiver_ids\": [\"$RECEIVER_ID\"]
}")
GROUP_ID=$(echo $GROUP_RESPONSE | jq .data)
echo "✓ Group created: $GROUP_ID"
echo ""
echo "4. Verifying via GraphQL..."
curl -s -X POST http://localhost:8042/graphql \
-H "Content-Type: application/json" \
-d "{
\"query\": \"{ eventReceiversById(id: \\\"$RECEIVER_ID\\\") { id name } }\"
}" | grep -q "$RECEIVER_ID" && echo "✓ GraphQL query successful"
echo ""
echo "Workflow test complete!"Run the test:
chmod +x test-workflow.sh
./test-workflow.shdocker stop xzepr-server
docker rm xzepr-serverdocker compose -f docker-compose.services.yaml downTo completely reset and remove all data:
# Stop and remove containers, networks, and volumes
docker compose -f docker-compose.services.yaml down -v
# Remove XZepr image
docker rmi xzepr:demo
# Remove certificates
rm -rf certs/To keep data but stop services:
# Stop services but keep volumes
docker compose -f docker-compose.services.yaml downRestart later with:
docker compose -f docker-compose.services.yaml up -dIf you see errors about ports being in use:
# Check what's using a port
lsof -i :8042
lsof -i :5432
# Stop conflicting services or use different ports
docker run -p 8043:8443 ... # Use port 8043 instead of 8042Check logs:
docker logs xzepr-server
docker compose -f docker-compose.services.yaml logsVerify PostgreSQL is running:
docker compose -f docker-compose.services.yaml ps postgres
# Test connection
docker exec -it $(docker ps -qf "name=postgres") psql -U xzepr -d xzepr -c "SELECT 1;"If migrations fail, check the status and fix issues:
# Check migration status
docker run --rm \
--network xzepr_redpanda_network \
-e DATABASE_URL=postgres://xzepr:password@postgres:5432/xzepr \
xzepr:demo \
./sqlx migrate info
# View migration table directly
docker exec -it $(docker ps -qf "name=postgres") \
psql -U xzepr -d xzepr -c "SELECT * FROM _sqlx_migrations ORDER BY installed_on;"
# If a migration is marked as failed, you may need to manually fix it
# Then revert the failed migration and try again
docker run --rm \
--network xzepr_redpanda_network \
-e DATABASE_URL=postgres://xzepr:password@postgres:5432/xzepr \
xzepr:demo \
./sqlx migrate revert
# Run migrations again
docker run --rm \
--network xzepr_redpanda_network \
-e DATABASE_URL=postgres://xzepr:password@postgres:5432/xzepr \
xzepr:demo \
./sqlx migrate runVerify Redpanda is running:
docker compose -f docker-compose.services.yaml ps redpanda-0
# Check Redpanda logs
docker compose -f docker-compose.services.yaml logs redpanda-0Verify XZepr is running and accessible:
curl http://localhost:8042/health
curl http://localhost:8042/graphql/healthCheck browser console for errors and ensure JavaScript is enabled.
Ensure you're using a valid API key:
# Verify API key is set
echo $XZEPR_API_KEY
# Generate new key if needed
docker run --rm \
--network xzepr_redpanda_network \
-e XZEPR__DATABASE__URL=postgres://xzepr:password@postgres:5432/xzepr \
--entrypoint ./admin \
xzepr:demo \
generate-api-key \
--username admin \
--name "New Key" \
--expires-days 30You have successfully:
- Started backend services with Docker Compose
- Built and run XZepr in a Docker container
- Created users and generated API keys
- Explored the API using GraphQL Playground
- Monitored events with Redpanda Console
- Tested endpoints with curl
- Completed an end-to-end workflow
- Read the GraphQL API Reference
- Learn about Authentication and Authorization
- Explore How to Use GraphQL Playground
- Deploy to production with Docker Production Guide
- XZepr GitHub Repository:
<repository-url> - Redpanda Documentation: https://docs.redpanda.com/
- GraphQL Specification: https://spec.graphql.org/
- Docker Documentation: https://docs.docker.com/