Multiplayer game events and live leaderboard Step 1 of 4

Start the environment

About 5 min Last verified September 13, 2026 against Redpanda 26.2.2

What you do

Copy the example environment file and start every service with Docker Compose. Compose waits for the health checks, so the command returns only when Redpanda, Console, Redis, Postgres and Redpanda Connect are ready.

cp .env.example .env
docker compose up -d --wait

If you prefer to create the topics by hand, these commands set the same partition counts and retention the compose file applies. The lines are long; on solution pages they scroll sideways instead of wrapping.

rpk topic create game.player-events --partitions 6 --replicas 1 --topic-config retention.ms=604800000 --topic-config cleanup.policy=delete --topic-config max.message.bytes=1048576
rpk topic create game.match-events --partitions 3 --replicas 1 --topic-config retention.ms=604800000 --topic-config cleanup.policy=delete
rpk topic create game.achievements --partitions 3 --replicas 1 --topic-config retention.ms=2592000000 --topic-config cleanup.policy=compact,delete
rpk topic create game.player-events.dlq --partitions 1 --replicas 1 --topic-config retention.ms=2592000000

The compose file is the one file you edit before starting, and the module file pins the two client libraries.

docker-compose.yml
services:
  redpanda:
    image: docker.redpanda.com/redpandadata/redpanda:v26.2.2
    command:
      - redpanda start
      - --smp 1
    ports:
      - 9092:9092
      - 8081:8081
    healthcheck:
      test: ["CMD", "rpk", "cluster", "health"]
      interval: 5s
module github.com/redpanda-data/solutions/multiplayer-gaming/leaderboard

go 1.23

require (
	github.com/redis/go-redis/v9 v9.6.1
	github.com/twmb/franz-go v1.17.1
)

Why

Everything downstream assumes a healthy broker and a reachable Schema Registry. Starting the platform first and verifying it means later failures are about your code, not the environment.

What happens inside

Redpanda starts as a single broker with the Kafka API on 9092, the Schema Registry on 8081 and the HTTP Proxy on 8082. Console connects to all three and turns on Protobuf decoding for the game.* topics.

The leaderboard consumer

The full service is short enough to read in one sitting. Long listings fold to a preview; use the button under the block to expand them.

services/leaderboard/main.go
package main

import (
	"context"
	"log"
	"net/http"
	"os"

	"github.com/redis/go-redis/v9"
	"github.com/twmb/franz-go/pkg/kgo"
)

func main() {
	ctx := context.Background()
	brokers := os.Getenv("REDPANDA_BROKERS")
	rdb := redis.NewClient(&redis.Options{Addr: os.Getenv("REDIS_ADDR")})
	cl, err := kgo.NewClient(
		kgo.SeedBrokers(brokers),
		kgo.ConsumerGroup("leaderboard"),
		kgo.ConsumeTopics("game.player-events"),
	)
	if err != nil {
		log.Fatal(err)
	}
	defer cl.Close()

	go serve(rdb)

	for {
		fetches := cl.PollFetches(ctx)
		fetches.EachRecord(func(r *kgo.Record) {
			apply(ctx, rdb, r)
		})
	}
}

func serve(rdb *redis.Client) {
	http.HandleFunc("/top", topHandler(rdb))
	log.Fatal(http.ListenAndServe(":8080", nil))
}
Complete source: services/leaderboard/main.go
package main

import (
	"context"
	"log"
	"net/http"
	"os"

	"github.com/redis/go-redis/v9"
	"github.com/twmb/franz-go/pkg/kgo"
)

func main() {
	ctx := context.Background()
	brokers := os.Getenv("REDPANDA_BROKERS")
	rdb := redis.NewClient(&redis.Options{Addr: os.Getenv("REDIS_ADDR")})
	cl, err := kgo.NewClient(
		kgo.SeedBrokers(brokers),
		kgo.ConsumerGroup("leaderboard"),
		kgo.ConsumeTopics("game.player-events"),
	)
	if err != nil {
		log.Fatal(err)
	}
	defer cl.Close()

	go serve(rdb)

	for {
		fetches := cl.PollFetches(ctx)
		fetches.EachRecord(func(r *kgo.Record) {
			apply(ctx, rdb, r)
		})
	}
}

func serve(rdb *redis.Client) {
	http.HandleFunc("/top", topHandler(rdb))
	log.Fatal(http.ListenAndServe(":8080", nil))
}

Verify

Check that the cluster is healthy and that Connect is ready to accept a pipeline:

rpk cluster health
curl -s localhost:4195/ready
Expected output
CLUSTER HEALTH OVERVIEW
=======================
Healthy:                          true
Unhealthy reasons:                []
Controller ID:                    0
All nodes:                        [0]
Nodes down:                       []
Leaderless partitions (0):        []
Under-replicated partitions (0):  []
Topics:                           6
Partitions:                       18
Replicas:                         18
Nodes with high disk usage:       []
Nodes in maintenance mode:        []
Unavailable nodes:                []
Cluster UUID:                     5c4f7d2e
Last update:                      2026-09-13T02:14:07Z
OK
PASS 9/9 checks

Files for this step

  • docker-compose.yml: the redpanda service is shown above, and the whole file is shown in full on the overview.

  • services/leaderboard/go.mod: shown in full above.

  • services/leaderboard/main.go: shown in full under Complete source.

Signed-in readers can download each file with the Download control on its code block or from the All build-along files panel.

In production

In production

Do not run Console on the same host as a broker, and put the Schema Registry behind TLS: every producer and consumer in this solution fetches schemas from it. For the full picture, see Production considerations: security.