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.
As your Discord bot grows, you’ll need to shard your gateway connection across multiple WebSocket connections. DisGo provides a powerful shard manager with support for custom sharding strategies, auto-scaling, and advanced shard management.
Understanding sharding
Sharding splits your bot’s guilds across multiple WebSocket connections. Each shard handles a subset of guilds based on the formula:
shard_id = (guild_id >> 22) % shard_count
Discord recommends approximately 1,000 guilds per shard, but the optimal count depends on your bot’s workload.
Discord requires sharding once your bot reaches 2,500 guilds. You can implement sharding earlier to improve performance and reliability.
Setting up the shard manager
Replace the standard gateway connection with a shard manager:
import (
" context "
" github.com/disgoorg/disgo "
" github.com/disgoorg/disgo/bot "
" github.com/disgoorg/disgo/gateway "
" github.com/disgoorg/disgo/sharding "
)
client , err := disgo . New ( token ,
bot . WithShardManagerConfigOpts (
sharding . WithGatewayConfigOpts (
gateway . WithIntents (
gateway . IntentGuilds ,
gateway . IntentGuildMessages ,
),
),
),
)
if err != nil {
panic ( err )
}
defer client . Close ( context . TODO ())
// Open all shards
if err = client . OpenShardManager ( context . TODO ()); err != nil {
panic ( err )
}
When using the shard manager, Discord automatically determines the recommended shard count based on your bot’s guild count.
Manual shard configuration
You can manually specify shard IDs and shard count:
client , err := disgo . New ( token ,
bot . WithShardManagerConfigOpts (
sharding . WithShardIDs ( 0 , 1 , 2 , 3 ), // Manage shards 0-3
sharding . WithShardCount ( 4 ), // Total of 4 shards
sharding . WithGatewayConfigOpts (
gateway . WithIntents ( gateway . IntentGuilds ),
),
),
)
If you specify shard IDs manually, you must also set the shard count. The shard count must match across all instances of your bot.
Distributed sharding
For very large bots, you can distribute shards across multiple processes or servers:
Process 1 (shards 0-1)
client , err := disgo . New ( token ,
bot . WithShardManagerConfigOpts (
sharding . WithShardIDs ( 0 , 1 ),
sharding . WithShardCount ( 4 ),
),
)
Process 2 (shards 2-3)
client , err := disgo . New ( token ,
bot . WithShardManagerConfigOpts (
sharding . WithShardIDs ( 2 , 3 ),
sharding . WithShardCount ( 4 ),
),
)
Calculate shard distribution
Determine how many total shards you need and distribute them across processes: totalShards := 16
shardsPerProcess := 4
processID := 0 // 0, 1, 2, or 3
startShard := processID * shardsPerProcess
endShard := startShard + shardsPerProcess
Configure each process
Each process manages its assigned shard range: shardIDs := make ([] int , shardsPerProcess )
for i := 0 ; i < shardsPerProcess ; i ++ {
shardIDs [ i ] = startShard + i
}
client , err := disgo . New ( token ,
bot . WithShardManagerConfigOpts (
sharding . WithShardIDs ( shardIDs ... ),
sharding . WithShardCount ( totalShards ),
),
)
Coordinate shard count
Ensure all processes use the same total shard count. Consider using environment variables: totalShards , _ := strconv . Atoi ( os . Getenv ( "TOTAL_SHARDS" ))
processID , _ := strconv . Atoi ( os . Getenv ( "PROCESS_ID" ))
Auto-scaling shards
DisGo can automatically re-shard when Discord requests it:
client , err := disgo . New ( token ,
bot . WithShardManagerConfigOpts (
sharding . WithAutoScaling ( true ),
sharding . WithShardSplitCount ( 2 ), // Split shards in 2 when scaling
),
)
When a shard receives a SHARDING_REQUIRED close code, the shard manager:
Closes the problematic shard
Calculates new shard IDs based on ShardSplitCount
Opens new shards to replace the old one
Split count example
With ShardSplitCount: 2:
Shard 0 splits into shards 0 and 4
Shard 1 splits into shards 1 and 5
Total shard count doubles from 4 to 8
sharding . WithAutoScaling ( true )
sharding . WithShardSplitCount ( 2 ) // Default
Auto-scaling changes your shard count dynamically. If you’re using distributed sharding, you’ll need to coordinate scaling across processes.
Shard lifecycle management
Opening specific shards
// Open shard 0
err := client . ShardManager (). OpenShard ( context . TODO (), 0 )
if err != nil {
slog . Error ( "failed to open shard" , slog . Int ( "shard" , 0 ), slog . Any ( "err" , err ))
}
Closing specific shards
// Close shard 0
client . ShardManager (). CloseShard ( context . TODO (), 0 )
Resuming shards
If you’ve stored session information, you can resume shards:
state := sharding . ShardState {
SessionID : "saved_session_id" ,
Sequence : 12345 ,
ResumeURL : "wss://gateway.discord.gg" ,
}
err := client . ShardManager (). ResumeShard ( context . TODO (), 0 , state )
if err != nil {
slog . Error ( "failed to resume shard" , slog . Any ( "err" , err ))
}
// Get specific shard
shard := client . ShardManager (). Shard ( 0 )
if shard != nil {
fmt . Printf ( "Shard %d status: %s \n " , shard . ShardID (), shard . Status ())
}
// Get shard for a guild
guildShard := client . ShardManager (). ShardByGuildID ( guildID )
// Iterate all shards
for shard := range client . ShardManager (). Shards () {
fmt . Printf ( "Shard %d : %d guilds \n " , shard . ShardID (), shard . GuildCount ())
}
Shard-aware events
Some events provide shard-specific information:
import " github.com/disgoorg/disgo/events "
client , err := disgo . New ( token ,
bot . WithEventListeners ( & events . ListenerAdapter {
OnGuildReady : func ( event * events . GuildReady ) {
slog . Info ( "guild ready" ,
slog . Any ( "guild_id" , event . GuildID ),
slog . Int ( "shard_id" , event . ShardID ),
)
},
OnGuildsReady : func ( event * events . GuildsReady ) {
slog . Info ( "all guilds ready on shard" ,
slog . Int ( "shard_id" , event . ShardID ),
slog . Int ( "guild_count" , len ( event . Guilds )),
)
},
}),
)
Rate limiting
The shard manager includes a global identify rate limiter to prevent exceeding Discord’s limits.
Custom rate limiter
import " golang.org/x/time/rate "
type customRateLimiter struct {
limiter * rate . Limiter
}
func ( r * customRateLimiter ) Wait ( ctx context . Context ) error {
return r . limiter . Wait ( ctx )
}
func ( r * customRateLimiter ) Close () {
// Cleanup
}
client , err := disgo . New ( token ,
bot . WithShardManagerConfigOpts (
sharding . WithIdentifyRateLimiter ( & customRateLimiter {
limiter : rate . NewLimiter ( rate . Every ( 6 * time . Second ), 1 ),
}),
),
)
client , err := disgo . New ( token ,
bot . WithShardManagerConfigOpts (
sharding . WithIdentifyRateLimiterConfigOpt (
gateway . WithIdentifyRateLimiterMaxConcurrency ( 1 ),
),
),
)
Persisting shard state
For zero-downtime restarts, persist shard session information:
type ShardState struct {
ShardID int
SessionID string
Sequence int
ResumeURL string
}
// Save state before shutdown
func saveShardStates ( manager sharding . ShardManager ) map [ int ] sharding . ShardState {
states := make ( map [ int ] sharding . ShardState )
for shard := range manager . Shards () {
states [ shard . ShardID ()] = sharding . ShardState {
SessionID : shard . SessionID (),
Sequence : shard . Sequence (),
ResumeURL : shard . ResumeURL (),
}
}
return states
}
// Resume with saved states
func resumeShards ( manager sharding . ShardManager , states map [ int ] sharding . ShardState ) {
for shardID , state := range states {
err := manager . ResumeShard ( context . TODO (), shardID , state )
if err != nil {
slog . Error ( "failed to resume shard" ,
slog . Int ( "shard_id" , shardID ),
slog . Any ( "err" , err ),
)
}
}
}
Complete sharding example
Here’s a complete example with distributed sharding and state persistence:
package main
import (
" context "
" encoding/json "
" log/slog "
" os "
" os/signal "
" strconv "
" syscall "
" github.com/disgoorg/disgo "
" github.com/disgoorg/disgo/bot "
" github.com/disgoorg/disgo/events "
" github.com/disgoorg/disgo/gateway "
" github.com/disgoorg/disgo/sharding "
)
func main () {
token := os . Getenv ( "TOKEN" )
totalShards , _ := strconv . Atoi ( os . Getenv ( "TOTAL_SHARDS" ))
processID , _ := strconv . Atoi ( os . Getenv ( "PROCESS_ID" ))
shardsPerProcess := 4
// Calculate shard range for this process
startShard := processID * shardsPerProcess
shardIDs := make ([] int , shardsPerProcess )
for i := 0 ; i < shardsPerProcess ; i ++ {
shardIDs [ i ] = startShard + i
}
// Load saved states if available
savedStates := loadShardStates ()
states := make ( map [ int ] sharding . ShardState )
for _ , id := range shardIDs {
if state , ok := savedStates [ id ]; ok {
states [ id ] = state
}
}
client , err := disgo . New ( token ,
bot . WithShardManagerConfigOpts (
sharding . WithShardIDsWithStates ( states ),
sharding . WithShardCount ( totalShards ),
sharding . WithAutoScaling ( true ),
sharding . WithGatewayConfigOpts (
gateway . WithIntents (
gateway . IntentGuilds ,
gateway . IntentGuildMessages ,
),
),
),
bot . WithEventListeners ( & events . ListenerAdapter {
OnGuildsReady : func ( event * events . GuildsReady ) {
slog . Info ( "shard ready" ,
slog . Int ( "shard_id" , event . ShardID ),
slog . Int ( "guilds" , len ( event . Guilds )),
)
},
}),
)
if err != nil {
panic ( err )
}
if err = client . OpenShardManager ( context . TODO ()); err != nil {
panic ( err )
}
slog . Info ( "bot running" ,
slog . Int ( "process_id" , processID ),
slog . Any ( "shard_ids" , shardIDs ),
)
// Wait for interrupt
s := make ( chan os . Signal , 1 )
signal . Notify ( s , syscall . SIGINT , syscall . SIGTERM )
<- s
// Save shard states before shutdown
saveShardStates ( client . ShardManager ())
client . Close ( context . TODO ())
}
func loadShardStates () map [ int ] sharding . ShardState {
data , err := os . ReadFile ( "shard_states.json" )
if err != nil {
return nil
}
var states map [ int ] sharding . ShardState
json . Unmarshal ( data , & states )
return states
}
func saveShardStates ( manager sharding . ShardManager ) {
states := make ( map [ int ] sharding . ShardState )
for shard := range manager . Shards () {
states [ shard . ShardID ()] = sharding . ShardState {
SessionID : shard . SessionID (),
Sequence : shard . Sequence (),
ResumeURL : shard . ResumeURL (),
}
}
data , _ := json . MarshalIndent ( states , "" , " " )
os . WriteFile ( "shard_states.json" , data , 0644 )
}
Configuration options
WithShardIDs(...int) - Specify shard IDs to manage
WithShardIDsWithStates(map[int]ShardState) - Specify shards with resume states
WithShardCount(int) - Set total shard count
WithShardSplitCount(int) - How many shards to split into when scaling
WithAutoScaling(bool) - Enable automatic re-sharding
WithLogger(*slog.Logger) - Custom logger
WithGatewayCreateFunc(gateway.CreateFunc) - Custom gateway factory
WithGatewayConfigOpts(...gateway.ConfigOpt) - Configure all gateways
WithIdentifyRateLimiter(gateway.IdentifyRateLimiter) - Custom rate limiter
WithCloseHandler(gateway.CloseHandlerFunc) - Handle shard closures
Best practices
Don’t wait until you hit the 2,500 guild limit. Start sharding around 1,000-1,500 guilds: // Good: Shard early
if guildCount > 1000 {
// Enable sharding
}
Use auto-scaling cautiously
Auto-scaling is powerful but can complicate distributed setups. Consider manual scaling for distributed bots: // For single-process bots
sharding . WithAutoScaling ( true )
// For distributed bots
sharding . WithAutoScaling ( false )
sharding . WithShardCount ( precalculatedCount )
Track shard status and reconnection patterns: for shard := range client . ShardManager (). Shards () {
metrics . RecordShardStatus (
shard . ShardID (),
shard . Status (),
shard . Latency (),
)
}
Implement graceful shutdown
Save shard session information before shutting down for faster reconnection: func shutdown ( manager sharding . ShardManager ) {
saveShardStates ( manager )
manager . Close ( context . Background ())
}