Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/disgoorg/disgo/llms.txt

Use this file to discover all available pages before exploring further.

Sharding distributes your bot’s guilds across multiple gateway connections. This is required for bots in 2,500+ guilds and improves performance for large-scale bots.

When to use sharding

Use a single gateway connection:
client, err := disgo.New(token,
    bot.WithDefaultGateway(),
)
Simpler setup, easier to manage.

How sharding works

Sharding distributes guilds using a deterministic formula:
shardID = (guildID >> 22) % shardCount
Each shard:
  • Maintains its own WebSocket connection
  • Receives events only for its assigned guilds
  • Has its own session that can be resumed independently
Discord recommends 1,000 guilds per shard for optimal performance.

Creating a shard manager

Automatic shard count

Let Discord determine the shard count:
import (
    "github.com/disgoorg/disgo"
    "github.com/disgoorg/disgo/bot"
    "github.com/disgoorg/disgo/gateway"
)

client, err := disgo.New(token,
    bot.WithDefaultShardManager(),
    bot.WithShardManagerConfigOpts(
        sharding.WithGatewayConfigOpts(
            gateway.WithIntents(
                gateway.IntentGuilds,
                gateway.IntentGuildMessages,
            ),
        ),
    ),
)

Manual shard count

Specify the exact number of shards:
import "github.com/disgoorg/disgo/sharding"

client, err := disgo.New(token,
    bot.WithShardManagerConfigOpts(
        sharding.WithShardCount(4),
        sharding.WithShardIDs(0, 1, 2, 3),
    ),
)

Shard manager configuration

Auto-scaling

Automatically split shards when they grow too large:
client, err := disgo.New(token,
    bot.WithShardManagerConfigOpts(
        sharding.WithAutoScaling(true),
        sharding.WithShardSplitCount(2), // Split into 2 shards
    ),
)
When a shard exceeds Discord’s limits, it’s automatically split into multiple smaller shards.

Partial sharding

Run only specific shards (useful for distributed systems):
// Instance 1: Shards 0-1
client1, _ := disgo.New(token,
    bot.WithShardManagerConfigOpts(
        sharding.WithShardCount(4),
        sharding.WithShardIDs(0, 1),
    ),
)

// Instance 2: Shards 2-3
client2, _ := disgo.New(token,
    bot.WithShardManagerConfigOpts(
        sharding.WithShardCount(4),
        sharding.WithShardIDs(2, 3),
    ),
)

Gateway options for all shards

client, err := disgo.New(token,
    bot.WithShardManagerConfigOpts(
        sharding.WithGatewayConfigOpts(
            gateway.WithIntents(gateway.IntentGuilds),
            gateway.WithCompression(gateway.CompressionZstdStream),
            gateway.WithPresenceOpts(
                gateway.WithPresenceActivities(
                    discord.Activity{
                        Name: "across shards",
                        Type: discord.ActivityTypeGame,
                    },
                ),
            ),
        ),
    ),
)

Managing shards

Opening shards

// Open all shards
ctx := context.Background()
client.OpenShardManager(ctx)

// Open specific shard
err := client.ShardManager.OpenShard(ctx, 0)

// Close specific shard
client.ShardManager.CloseShard(ctx, 0)

// Close all shards
client.Close(ctx)

Accessing shards

import "github.com/disgoorg/snowflake/v2"

// Get shard for a specific guild
guildID := snowflake.ID(123456789)
shard := client.ShardManager.ShardByGuildID(guildID)

// Get shard by ID
shard := client.ShardManager.Shard(0)
if shard != nil {
    latency := shard.Latency()
    status := shard.Status()
}

// Iterate all shards
for shard := range client.ShardManager.Shards() {
    log.Printf("Shard %d: %v latency",
        shard.ShardID(),
        shard.Latency(),
    )
}

Per-shard operations

// Set presence for specific shard
err := client.SetPresenceForShard(ctx, 0,
    gateway.WithPresenceActivities(
        discord.Activity{
            Name: "Shard 0",
            Type: discord.ActivityTypeGame,
        },
    ),
)

// Request members on specific shard
shard := client.ShardManager.Shard(0)
err := shard.Send(ctx,
    gateway.OpcodeRequestGuildMembers,
    gateway.MessageDataRequestGuildMembers{
        GuildID:   guildID,
        Query:     "",
        Limit:     0,
        Presences: true,
    },
)

Shard lifecycle events

import "github.com/disgoorg/disgo/events"

client, err := disgo.New(token,
    bot.WithDefaultShardManager(),
    bot.WithEventListeners(&events.ListenerAdapter{
        OnGuildReady: func(e *events.GuildReady) {
            log.Printf("Guild %s ready on shard %d",
                e.GuildID,
                e.ShardID,
            )
        },
        OnGuildsReady: func(e *events.GuildsReady) {
            log.Printf("All guilds ready on shard %d", e.ShardID)
        },
    }),
)

Shard state persistence

Save and restore shard sessions:
import "github.com/disgoorg/disgo/sharding"

// Save shard states
type ShardStates map[int]sharding.ShardState

func saveShardStates(client *bot.Client) ShardStates {
    states := make(ShardStates)
    for shard := range client.ShardManager.Shards() {
        if sessionID := shard.SessionID(); sessionID != nil {
            states[shard.ShardID()] = sharding.ShardState{
                SessionID: *sessionID,
                Sequence:  *shard.LastSequenceReceived(),
                ResumeURL: *shard.ResumeURL(),
            }
        }
    }
    return states
}

// Restore shard states
func createWithStates(token string, states ShardStates) (*bot.Client, error) {
    return disgo.New(token,
        bot.WithShardManagerConfigOpts(
            sharding.WithShardIDsWithStates(states),
        ),
    )
}

Shard close handling

import "github.com/disgoorg/disgo/gateway"

client, err := disgo.New(token,
    bot.WithShardManagerConfigOpts(
        sharding.WithCloseHandler(func(
            shard gateway.Gateway,
            err error,
            reconnect bool,
        ) {
            log.Printf("Shard %d closed: %v (reconnect=%v)",
                shard.ShardID(),
                err,
                reconnect,
            )
            
            if !reconnect {
                // Handle fatal errors
                log.Printf("Shard %d cannot reconnect", shard.ShardID())
            }
        }),
    ),
)

Identify rate limiting

Discord limits how fast you can identify shards:
import "github.com/disgoorg/disgo/gateway"

client, err := disgo.New(token,
    bot.WithShardManagerConfigOpts(
        sharding.WithIdentifyRateLimiterConfigOpt(
            gateway.WithIdentifyMaxConcurrency(1),
        ),
    ),
)
DisGo automatically handles identify rate limiting based on Discord’s recommendations.
You can identify at most max_concurrency shards simultaneously. Discord provides this value in the Gateway Bot endpoint. DisGo fetches and applies this automatically.

Calculating shard IDs

Manually calculate which shard a guild belongs to:
import "github.com/disgoorg/disgo/sharding"

guildID := snowflake.ID(123456789)
shardCount := 4

shardID := sharding.ShardIDByGuild(guildID, shardCount)
log.Printf("Guild %d belongs to shard %d", guildID, shardID)

Complete sharding example

package main

import (
    "context"
    "log"
    "os"
    "os/signal"
    "syscall"

    "github.com/disgoorg/disgo"
    "github.com/disgoorg/disgo/bot"
    "github.com/disgoorg/disgo/discord"
    "github.com/disgoorg/disgo/events"
    "github.com/disgoorg/disgo/gateway"
    "github.com/disgoorg/disgo/sharding"
)

func main() {
    client, err := disgo.New(os.Getenv("TOKEN"),
        bot.WithShardManagerConfigOpts(
            // Let Discord decide shard count
            // Or specify manually:
            // sharding.WithShardCount(4),
            // sharding.WithShardIDs(0, 1, 2, 3),
            
            // Enable auto-scaling
            sharding.WithAutoScaling(true),
            sharding.WithShardSplitCount(2),
            
            // Configure all shards
            sharding.WithGatewayConfigOpts(
                gateway.WithIntents(
                    gateway.IntentGuilds,
                    gateway.IntentGuildMessages,
                    gateway.IntentMessageContent,
                ),
                gateway.WithCompression(gateway.CompressionZstdStream),
            ),
            
            // Handle shard close events
            sharding.WithCloseHandler(func(
                shard gateway.Gateway,
                err error,
                reconnect bool,
            ) {
                log.Printf("Shard %d: %v", shard.ShardID(), err)
            }),
        ),
        
        bot.WithEventListeners(&events.ListenerAdapter{
            OnGuildReady: func(e *events.GuildReady) {
                log.Printf("Guild %s ready (shard %d)",
                    e.GuildID,
                    e.ShardID,
                )
            },
            OnGuildsReady: func(e *events.GuildsReady) {
                log.Printf("Shard %d ready", e.ShardID)
                
                // Count guilds on this shard
                count := 0
                for shard := range e.Client().ShardManager.Shards() {
                    if shard.ShardID() == e.ShardID {
                        // Get guilds for this shard...
                    }
                }
                log.Printf("Shard %d has %d guilds", e.ShardID, count)
            },
            OnMessageCreate: func(e *events.MessageCreate) {
                if e.Message.Content == "!shardinfo" {
                    // Find which shard this guild is on
                    if e.GuildID != nil {
                        shard := e.Client().ShardManager.ShardByGuildID(*e.GuildID)
                        _, _ = e.Client().Rest.CreateMessage(
                            e.ChannelID,
                            discord.MessageCreate{
                                Content: fmt.Sprintf(
                                    "Shard %d/%d - Latency: %v",
                                    shard.ShardID(),
                                    shard.ShardCount(),
                                    shard.Latency(),
                                ),
                            },
                        )
                    }
                }
            },
        }),
    )
    if err != nil {
        log.Fatal(err)
    }

    defer client.Close(context.Background())

    // Open all shards
    client.OpenShardManager(context.Background())

    // Wait for interrupt
    s := make(chan os.Signal, 1)
    signal.Notify(s, syscall.SIGINT, syscall.SIGTERM)
    <-s

    log.Println("Shutting down...")
}
The shard manager opens shards sequentially, respecting Discord’s identify rate limits. For 16 shards with max_concurrency=1, this takes about 80 seconds (5 seconds per identify).

Build docs developers (and LLMs) love