Deploying Bracket
Bracket is a modern, self-hosted tournament system designed to make organizing competitive events simple and intuitive. Built with async Python (FastAPI) backend and a React/Vite frontend using the Mantine UI library, Bracket provides comprehensive tournament management capabilities with beautiful, responsive interfaces. Whether you’re organizing esports competitions, sports tournaments, gaming events, or community competitions, Bracket offers the flexibility and control needed to manage tournaments of any scale with professional-grade features.
Why Bracket?
Bracket stands out as the premier choice for self-hosted tournament management with exceptional features:
- Multiple Tournament Formats: Support for single elimination, round-robin, and swiss tournament formats
- Flexible Tournament Structure: Build tournaments with multiple stages, groups, and brackets
- Drag-and-Drop Interface: Easily reschedule matches between courts or time slots
- Team Management: Create and manage teams, add players, and organize team information
- Public Dashboards: Customizable dashboard pages for public display with logo support
- Multi-Club Support: Create multiple clubs with multiple tournaments per club
- Dynamic Swiss Tournaments: Automatic scheduling and management of swiss format tournaments
- Match Scheduling: Intelligent match scheduling with conflict detection
- Court Management: Assign matches to different courts or venues
- Real-Time Updates: Live updates of match results and standings
- Responsive Design: Works seamlessly on desktop and mobile devices
- Multi-Language Support: Automatic language detection with Crowdin translations
- API-First Architecture: Complete REST API for integrations
- Self-Hosted Control: Complete data ownership and privacy
- Open Source: AGPL-3.0 licensed with active community
- PostgreSQL Backend: Robust database for tournament data
- Docker Support: Easy deployment with Docker and Docker Compose
- Standings Management: Automatic calculation and display of tournament standings
- Player Statistics: Track player performance and statistics
- Export Options: Export tournament data in multiple formats
- Customizable Branding: Add logos and customize tournament appearance
Bracket is ideal for esports organizations hosting gaming tournaments, sports clubs managing competitive leagues, event organizers coordinating large tournaments, gaming communities running tournaments, educational institutions organizing competitions, and enterprises hosting corporate competitions. With persistent storage on Klutch.sh, your tournament management system is always available and secure.
Prerequisites
Before deploying Bracket, ensure you have:
- A Klutch.sh account
- A GitHub repository with your Bracket deployment configuration
- Basic familiarity with Docker and Git
- PostgreSQL database for tournament data storage
- Sufficient storage for tournament data and assets (typically 10-50GB)
- Understanding of tournament formats and structures
- A custom domain for your tournament platform (recommended)
Important Considerations
Deploying Bracket
Create a New Project
Log in to your Klutch.sh dashboard and create a new project for your Bracket tournament system.
Prepare Your Repository
Create a GitHub repository with the following structure for your Bracket deployment:
bracket-deploy/├─ Dockerfile├─ docker-compose.yml├─ .env.example├─ entrypoint.sh├─ .gitignore└─ README.mdHere’s a Dockerfile for Bracket backend:
FROM python:3.11-slimWORKDIR /app# Install system dependenciesRUN apt-get update && apt-get install -y \curl \git \postgresql-client \&& rm -rf /var/lib/apt/lists/*# Clone Bracket repositoryRUN git clone https://github.com/evroon/bracket.git /tmp/bracket && \cp -r /tmp/bracket/* .# Install uv for Python package managementRUN curl -LsSf https://astral.sh/uv/install.sh | sh# Install Python dependenciesRUN ~/.local/bin/uv sync --frozen# Create necessary directoriesRUN mkdir -p /app/data \/app/logs \/app/uploads && \chmod -R 755 /app# Copy entrypoint scriptCOPY entrypoint.sh /RUN chmod +x /entrypoint.sh# Expose portEXPOSE 8000# Health checkHEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \CMD curl -f http://localhost:8000/health || exit 1# Run entrypointENTRYPOINT ["/entrypoint.sh"]CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]Create an
entrypoint.shfile:#!/bin/bashset -e# Create necessary directoriesmkdir -p /app/data \/app/logs \/app/uploads# Set proper permissionschmod -R 755 /appecho "Initializing Bracket..."# Wait for PostgreSQL to be readyecho "Waiting for PostgreSQL..."until PGPASSWORD=$DATABASE_PASSWORD psql -h $DATABASE_HOST -U $DATABASE_USER -d postgres -c "\q" 2>/dev/null; doecho "PostgreSQL is unavailable - sleeping..."sleep 2doneecho "PostgreSQL is up!"# Run database migrationsecho "Running database migrations..."~/.local/bin/uv run alembic upgrade head || true# Create admin user if specifiedif [ -n "$ADMIN_EMAIL" ] && [ -n "$ADMIN_PASSWORD" ]; thenecho "Setting up admin user..."~/.local/bin/uv run python -c "from app.models import Userfrom app.database import get_dbfrom sqlalchemy import create_enginefrom sqlalchemy.orm import sessionmakerimport osengine = create_engine(os.getenv('DATABASE_URL'))Session = sessionmaker(bind=engine)session = Session()user = session.query(User).filter_by(email=os.getenv('ADMIN_EMAIL')).first()if not user:user = User(email=os.getenv('ADMIN_EMAIL'))user.set_password(os.getenv('ADMIN_PASSWORD'))session.add(user)session.commit()print(f'Created admin user: {os.getenv(\"ADMIN_EMAIL\")}')else:print(f'Admin user {os.getenv(\"ADMIN_EMAIL\")} already exists')" || truefiecho "Bracket is starting..."exec "$@"For the frontend, create a separate Dockerfile or use a multi-stage build. Here’s a simple Node.js based Dockerfile:
FROM node:18-alpineWORKDIR /app# Install dependenciesRUN npm install -g pnpm# Clone Bracket repositoryRUN git clone https://github.com/evroon/bracket.git /tmp/bracket && \cp -r /tmp/bracket/frontend/* .# Install frontend dependenciesRUN pnpm install# Build frontendRUN pnpm run build# Expose portEXPOSE 3000# Start development serverCMD ["pnpm", "run", "preview"]Create a
.env.examplefile:Terminal window # Bracket ConfigurationBRACKET_TITLE=Bracket Tournament SystemBRACKET_DESCRIPTION=Manage your tournaments with ease# Database ConfigurationDATABASE_URL=postgresql://bracket:secure_password@postgres.internal:5432/bracketDATABASE_HOST=postgres.internalDATABASE_USER=bracketDATABASE_PASSWORD=secure_passwordDATABASE_NAME=bracket# Admin UserADMIN_EMAIL=admin@yourdomain.comADMIN_PASSWORD=secure_admin_passwordADMIN_NAME=Administrator# Server ConfigurationSERVER_HOST=0.0.0.0SERVER_PORT=8000API_BASE_URL=https://api.tournament.yourdomain.com# Frontend ConfigurationFRONTEND_URL=https://tournament.yourdomain.comVITE_API_URL=https://api.tournament.yourdomain.com# SecuritySECRET_KEY=your-secret-key-32-characters-longALLOW_REGISTRATION=falseREQUIRE_EMAIL_VERIFICATION=false# Email Configuration (optional)SMTP_SERVER=smtp.gmail.comSMTP_PORT=587SMTP_USERNAME=noreply@yourdomain.comSMTP_PASSWORD=app_passwordSMTP_FROM_NAME=Bracket Tournament# Tournament SettingsDEFAULT_LANGUAGE=enTIMEZONE=UTCTOURNAMENT_TIMEZONE=UTC# FeaturesENABLE_API=trueENABLE_TOURNAMENT_EXPORT=trueENABLE_STANDINGS_EXPORT=trueALLOW_PUBLIC_TOURNAMENT_VIEW=true# LoggingLOG_LEVEL=INFOLOG_FILE=/app/logs/bracket.logCreate a
.gitignorefile:.env__pycache__/*.pyc.venv/data/logs/uploads/build/dist/*.egg-info/node_modules/.DS_Store*.db.idea/venv/Commit and push to your GitHub repository:
Terminal window git initgit add .git commit -m "Initial Bracket tournament system deployment"git remote add origin https://github.com/yourusername/bracket-deploy.gitgit push -u origin mainCreate a New App
In the Klutch.sh dashboard:
- Click “Create New App”
- Select your GitHub repository containing the Dockerfile
- Choose the branch (typically
mainormaster) - Klutch.sh will automatically detect the Dockerfile in the root directory
Configure Environment Variables
Set up these essential environment variables in your Klutch.sh dashboard:
Variable Description Example BRACKET_TITLEPlatform title Bracket Tournament SystemDATABASE_URLPostgreSQL connection postgresql://user:password@host:5432/bracketDATABASE_HOSTDatabase host postgres.internalDATABASE_USERDatabase user bracketDATABASE_PASSWORDDatabase password secure_passwordADMIN_EMAILAdmin email admin@yourdomain.comADMIN_PASSWORDAdmin password secure_passwordSERVER_PORTAPI server port 8000API_BASE_URLAPI endpoint https://api.tournament.yourdomain.comFRONTEND_URLFrontend URL https://tournament.yourdomain.comSECRET_KEYSession encryption key your-secret-keyALLOW_REGISTRATIONAllow user signup falseDEFAULT_LANGUAGELanguage enTIMEZONEServer timezone UTCLOG_LEVELLogging verbosity INFOConfigure Persistent Storage
Bracket requires persistent storage for tournament data and uploads. Add persistent volumes:
Mount Path Description Recommended Size /app/dataTournament data and configuration 50GB /app/logsApplication logs 10GB /app/uploadsTournament media and assets 100GB In the Klutch.sh dashboard:
- Navigate to your app settings
- Go to the “Volumes” section
- Click “Add Volume” for each mount path
- Set mount paths and sizes as specified above
- Ensure uploads volume is sized for media files
Set Network Configuration
Configure your app’s network settings:
- Select traffic type: HTTP
- Recommended internal port: 8000 (FastAPI default)
- Klutch.sh will handle HTTPS termination via reverse proxy
- For frontend, you may need a separate deployment for the React app
Configure Custom Domains
Bracket benefits from custom domains for the API and frontend:
- Navigate to your app’s “Domains” section in Klutch.sh
- Add custom domain for API (e.g.,
api.tournament.yourdomain.com) - Add separate custom domain for frontend (e.g.,
tournament.yourdomain.com) - Configure DNS with CNAME records pointing to your Klutch.sh apps
- Update
API_BASE_URLandFRONTEND_URLenvironment variables - Klutch.sh will automatically provision SSL certificates
Deploy Your App
- Review all settings and environment variables carefully
- Verify all persistent volumes are properly configured with adequate sizes
- Ensure database connection string is correct
- Click “Deploy”
- Klutch.sh will build the Docker image and start your Bracket instance
- Wait for the deployment to complete (typically 10-15 minutes)
- Access your Bracket tournament system at your configured domain
- Log in with admin credentials to begin creating tournaments
Initial Setup and Configuration
After deployment completes, access your Bracket instance to configure your tournament system.
Accessing Bracket
Navigate to your domain: https://tournament.yourdomain.com
Log in with the admin credentials you configured during setup.
Admin Dashboard Overview
Explore the main interface:
- Dashboard: Overview of active tournaments and statistics
- Clubs: Manage tournament clubs and organizations
- Tournaments: Create and manage tournaments
- Teams: Manage teams and players
- Settings: Configure system preferences
- Admin Panel: User and system administration
Creating a Club
Organize tournaments under clubs:
- Click “Clubs” in the navigation menu
- Click “Create New Club”
- Enter club information:
- Club name
- Club description
- Logo/image (optional)
- Configure club settings
- Click “Create Club”
Creating a Tournament
Set up a new tournament:
- Navigate to your club
- Click “Create Tournament”
- Enter tournament details:
- Tournament name
- Description
- Tournament format (Single Elimination, Round-Robin, Swiss)
- Number of teams/participants
- Configure tournament settings:
- Date and time
- Location/venue
- Visibility (public/private)
- Click “Create Tournament”
Building Tournament Structure
Design tournament brackets and stages:
- In tournament settings, go to “Structure”
- Add stages:
- Qualifiers stage
- Main stage
- Finals stage
- For each stage, configure:
- Number of groups/brackets
- Advancement rules
- Number of matches per round
- Set match scheduling preferences
- Save tournament structure
Managing Teams and Players
Add participants to your tournament:
- In tournament, click “Teams”
- “Add Teams” or “Import Teams”
- For each team, enter:
- Team name
- Team logo (optional)
- Player roster
- Add player information:
- Player name
- Player ID/tag
- Role (if applicable)
- Finalize team registration
- Save teams
Scheduling Matches
Organize tournament matches and timing:
- Navigate to tournament “Scheduling”
- Configure match settings:
- Match duration
- Break time between matches
- Courts/venues available
- Generate automatic schedule or:
- Manually create match schedule
- Drag-and-drop to reschedule
- Assign to specific courts
- Set match start times
- Publish schedule
Managing Match Results
Record match outcomes:
- In “Matches” or “Schedule” view
- Click on match to update
- Enter match results:
- Winner/scores
- Duration
- Comments/notes
- If tournament auto-advances, standings update automatically
- Save result
Viewing Tournament Standings
Monitor tournament progress:
- Navigate to tournament “Standings”
- View current standings by:
- Points/wins
- Head-to-head
- Tiebreakers
- Filter by group/bracket
- Export standings if needed
Public Dashboard Configuration
Share tournament progress publicly:
- Go to tournament “Dashboard” settings
- Configure public dashboard:
- Visibility (public/private)
- Information shown (standings, schedule, results)
- Logo and branding
- Color scheme
- Customize display:
- Select dashboard widgets
- Arrange information
- Set refresh rate
- Generate public link
- Share with spectators/participants
Configuring Match Courts
Set up venue/court management:
- In tournament settings, go to “Courts”
- Add courts/venues:
- Court name
- Location
- Capacity
- Equipment details
- Assign matches to courts
- View court schedules
- Monitor court utilization
Exporting Tournament Data
Export tournament information:
- Go to tournament “Export”
- Select export format:
- CSV (for spreadsheets)
- JSON (for integrations)
- PDF (for reports)
- Choose data to export:
- Teams
- Standings
- Schedule
- Results
- Download exported file
Managing Users and Access
Control user access to tournaments:
- Go to “Settings” → “Users”
- Add users:
- Email address
- Role (Admin, Manager, Viewer)
- Tournament access
- Edit user permissions
- Remove users as needed
- Configure role permissions
Environment Variable Examples
Basic Configuration
BRACKET_TITLE=Bracket Tournament SystemDATABASE_URL=postgresql://bracket:password@postgres.internal:5432/bracketDATABASE_HOST=postgres.internalDATABASE_USER=bracketDATABASE_PASSWORD=secure_passwordADMIN_EMAIL=admin@yourdomain.comADMIN_PASSWORD=secure_passwordSERVER_PORT=8000API_BASE_URL=https://api.tournament.yourdomain.comFRONTEND_URL=https://tournament.yourdomain.comComplete Production Configuration
# Application SettingsBRACKET_TITLE=Competitive Tournament PlatformBRACKET_DESCRIPTION=Professional tournament management systemBRACKET_VERSION=2.2.5
# Database ConfigurationDATABASE_URL=postgresql://bracket:very_secure_password_32_chars@postgres.internal:5432/bracketDATABASE_HOST=postgres.internalDATABASE_PORT=5432DATABASE_USER=bracketDATABASE_PASSWORD=very_secure_password_32_charsDATABASE_NAME=bracketDATABASE_SSL=true
# Admin UserADMIN_EMAIL=admin@yourdomain.comADMIN_PASSWORD=very_secure_admin_password_32_charsADMIN_NAME=Tournament Administrator
# Server ConfigurationSERVER_HOST=0.0.0.0SERVER_PORT=8000WORKERS=4API_BASE_URL=https://api.tournament.yourdomain.comFRONTEND_URL=https://tournament.yourdomain.com
# Frontend ConfigurationVITE_API_URL=https://api.tournament.yourdomain.comVITE_APP_TITLE=BracketVITE_ENVIRONMENT=production
# SecuritySECRET_KEY=very_secure_secret_key_32_characters_longALLOW_REGISTRATION=falseREQUIRE_EMAIL_VERIFICATION=truePASSWORD_MIN_LENGTH=8SESSION_TIMEOUT=3600COOKIE_SECURE=trueCORS_ALLOWED_ORIGINS=https://tournament.yourdomain.com
# Tournament SettingsDEFAULT_LANGUAGE=enAVAILABLE_LANGUAGES=en,fr,de,es,nl,zhTIMEZONE=UTCTOURNAMENT_TIMEZONE=UTCTOURNAMENT_AUTO_ADVANCE=trueTOURNAMENT_ALLOW_MANUAL_MATCHES=true
# FeaturesENABLE_API=trueENABLE_TOURNAMENT_EXPORT=trueENABLE_STANDINGS_EXPORT=trueENABLE_SCHEDULE_EXPORT=trueALLOW_PUBLIC_TOURNAMENT_VIEW=trueALLOW_TEAM_REGISTRATION=falseENABLE_BRACKETS_DRAW=trueENABLE_SWISS_SYSTEM=true
# Email ConfigurationSMTP_SERVER=smtp.gmail.comSMTP_PORT=587SMTP_USE_TLS=trueSMTP_USERNAME=noreply@yourdomain.comSMTP_PASSWORD=smtp_app_passwordSMTP_FROM_NAME=Bracket Tournament SystemSMTP_FROM_EMAIL=noreply@yourdomain.com
# File Upload ConfigurationMAX_UPLOAD_SIZE=104857600ALLOWED_FILE_TYPES=jpg,png,pdf,xlsxUPLOAD_PATH=/app/uploadsMEDIA_RETENTION_DAYS=365
# LoggingLOG_LEVEL=INFOLOG_FILE=/app/logs/bracket.logLOG_MAX_BYTES=10485760LOG_BACKUP_COUNT=10LOG_FORMAT=%(asctime)s - %(name)s - %(levelname)s - %(message)s
# PerformanceCACHE_ENABLED=trueCACHE_TTL=3600DATABASE_POOL_SIZE=20DATABASE_POOL_RECYCLE=3600Sample Code and Getting Started
Python - Tournament Management Integration
# Bracket Tournament Management Integration
from sqlalchemy import create_enginefrom sqlalchemy.orm import sessionmaker, Sessionfrom typing import List, Optionalfrom datetime import datetimeimport os
class TournamentManager: def __init__(self): database_url = os.getenv('DATABASE_URL') self.engine = create_engine(database_url) self.SessionLocal = sessionmaker(bind=self.engine)
def create_tournament( self, club_id: int, name: str, description: str, format: str, # 'single_elimination', 'round_robin', 'swiss' start_date: datetime, **kwargs ) -> dict: """Create a new tournament""" session: Session = self.SessionLocal()
try: from app.models import Tournament
tournament = Tournament( club_id=club_id, name=name, description=description, format=format, start_date=start_date, status='created', **kwargs )
session.add(tournament) session.commit()
return { 'id': tournament.id, 'name': tournament.name, 'format': tournament.format, 'created_at': tournament.created_at } finally: session.close()
def add_team_to_tournament( self, tournament_id: int, team_id: int ) -> bool: """Add a team to a tournament""" session: Session = self.SessionLocal()
try: from app.models import TournamentTeam
tournament_team = TournamentTeam( tournament_id=tournament_id, team_id=team_id, registered_at=datetime.now() )
session.add(tournament_team) session.commit() return True except Exception as e: session.rollback() print(f"Error adding team: {e}") return False finally: session.close()
def create_match( self, tournament_id: int, stage_id: int, team1_id: int, team2_id: int, court_id: Optional[int] = None, start_time: Optional[datetime] = None ) -> dict: """Create a match in tournament""" session: Session = self.SessionLocal()
try: from app.models import Match
match = Match( tournament_id=tournament_id, stage_id=stage_id, team1_id=team1_id, team2_id=team2_id, court_id=court_id, start_time=start_time or datetime.now(), status='scheduled' )
session.add(match) session.commit()
return { 'id': match.id, 'team1_id': match.team1_id, 'team2_id': match.team2_id, 'status': match.status } finally: session.close()
def update_match_result( self, match_id: int, winner_id: int, team1_score: int, team2_score: int, duration_minutes: int ) -> bool: """Update match result and auto-advance if needed""" session: Session = self.SessionLocal()
try: from app.models import Match, Standings
match = session.query(Match).filter_by(id=match_id).first()
if not match: return False
match.winner_id = winner_id match.team1_score = team1_score match.team2_score = team2_score match.duration_minutes = duration_minutes match.status = 'completed' match.completed_at = datetime.now()
# Update standings standings = session.query(Standings).filter_by( tournament_id=match.tournament_id, team_id=winner_id ).first()
if standings: standings.wins += 1 standings.points += 3
session.commit() return True except Exception as e: session.rollback() print(f"Error updating match: {e}") return False finally: session.close()
def get_tournament_standings(self, tournament_id: int) -> List[dict]: """Get tournament standings""" session: Session = self.SessionLocal()
try: from app.models import Standings, Team
standings = session.query(Standings, Team).filter( Standings.tournament_id == tournament_id ).join(Team).order_by( Standings.points.desc(), Standings.wins.desc() ).all()
return [ { 'team_id': s[0].team_id, 'team_name': s[1].name, 'wins': s[0].wins, 'losses': s[0].losses, 'points': s[0].points, 'position': i + 1 } for i, s in enumerate(standings) ] finally: session.close()
def generate_schedule(self, tournament_id: int) -> bool: """Generate tournament schedule""" session: Session = self.SessionLocal()
try: from app.models import Tournament, TournamentTeam, Match
tournament = session.query(Tournament).filter_by( id=tournament_id ).first()
if not tournament: return False
teams = session.query(TournamentTeam).filter_by( tournament_id=tournament_id ).all()
if tournament.format == 'round_robin': # Generate round-robin schedule matches_to_create = [] team_ids = [t.team_id for t in teams]
for i in range(len(team_ids)): for j in range(i + 1, len(team_ids)): match = Match( tournament_id=tournament_id, team1_id=team_ids[i], team2_id=team_ids[j], status='scheduled' ) matches_to_create.append(match)
session.add_all(matches_to_create) session.commit()
return True except Exception as e: session.rollback() print(f"Error generating schedule: {e}") return False finally: session.close()
# Usage exampleif __name__ == "__main__": manager = TournamentManager()
# Create tournament tournament = manager.create_tournament( club_id=1, name="Spring Gaming Tournament", description="Community gaming competition", format="single_elimination", start_date=datetime(2024, 3, 15) )
print(f"Created tournament: {tournament}")TypeScript - Frontend Tournament Display
// Bracket Tournament Display Component
import React, { useState, useEffect } from 'react';import axios from 'axios';
interface Tournament { id: number; name: string; format: string; status: string; startDate: string;}
interface Match { id: number; team1Id: number; team2Id: number; team1Score?: number; team2Score?: number; status: string; startTime?: string;}
interface Standings { teamId: number; teamName: string; wins: number; losses: number; points: number; position: number;}
const TournamentDashboard: React.FC<{ tournamentId: number }> = ({ tournamentId }) => { const [tournament, setTournament] = useState<Tournament | null>(null); const [matches, setMatches] = useState<Match[]>([]); const [standings, setStandings] = useState<Standings[]>([]); const [loading, setLoading] = useState(true); const [activeTab, setActiveTab] = useState<'schedule' | 'standings'>('standings');
useEffect(() => { loadTournamentData(); }, [tournamentId]);
const loadTournamentData = async () => { try { const apiUrl = process.env.REACT_APP_API_URL || 'http://localhost:8000';
const [tourRes, matchRes, standRes] = await Promise.all([ axios.get(`${apiUrl}/tournaments/${tournamentId}`), axios.get(`${apiUrl}/tournaments/${tournamentId}/matches`), axios.get(`${apiUrl}/tournaments/${tournamentId}/standings`) ]);
setTournament(tourRes.data); setMatches(matchRes.data); setStandings(standRes.data); setLoading(false); } catch (error) { console.error('Failed to load tournament data:', error); setLoading(false); } };
if (loading) { return <div className="loading">Loading tournament data...</div>; }
return ( <div className="tournament-dashboard"> <div className="tournament-header"> <h1>{tournament?.name}</h1> <div className="tournament-info"> <span className="format">{tournament?.format}</span> <span className="status">{tournament?.status}</span> </div> </div>
<div className="tabs"> <button className={`tab ${activeTab === 'standings' ? 'active' : ''}`} onClick={() => setActiveTab('standings')} > Standings </button> <button className={`tab ${activeTab === 'schedule' ? 'active' : ''}`} onClick={() => setActiveTab('schedule')} > Schedule </button> </div>
{activeTab === 'standings' && ( <div className="standings-container"> <table className="standings-table"> <thead> <tr> <th>Position</th> <th>Team</th> <th>Wins</th> <th>Losses</th> <th>Points</th> </tr> </thead> <tbody> {standings.map((standing) => ( <tr key={standing.teamId} className="standings-row"> <td className="position">{standing.position}</td> <td className="team-name">{standing.teamName}</td> <td className="wins">{standing.wins}</td> <td className="losses">{standing.losses}</td> <td className="points">{standing.points}</td> </tr> ))} </tbody> </table> </div> )}
{activeTab === 'schedule' && ( <div className="schedule-container"> <div className="matches-list"> {matches.map((match) => ( <div key={match.id} className="match-card"> <div className="match-time"> {match.startTime && new Date(match.startTime).toLocaleString()} </div> <div className="match-result"> <div className="team team1"> <span className="team-id">{match.team1Id}</span> {match.status === 'completed' && ( <span className="score">{match.team1Score}</span> )} </div> <div className="vs">VS</div> <div className="team team2"> <span className="team-id">{match.team2Id}</span> {match.status === 'completed' && ( <span className="score">{match.team2Score}</span> )} </div> </div> <div className={`match-status ${match.status}`}> {match.status.toUpperCase()} </div> </div> ))} </div> </div> )} </div> );};
export default TournamentDashboard;Bash - Backup and Maintenance Script
#!/bin/bash
# Bracket Backup and Maintenance ScriptBACKUP_DIR="/backups/bracket"TIMESTAMP=$(date +%Y%m%d_%H%M%S)RETENTION_DAYS=30
# Create backup directorymkdir -p $BACKUP_DIR
echo "Starting Bracket backup..."
# Backup PostgreSQL databaseecho "Backing up PostgreSQL database..."PGPASSWORD=$DATABASE_PASSWORD pg_dump \ -h $DATABASE_HOST \ -U $DATABASE_USER \ -d $DATABASE_NAME \ -F c -b -v -f $BACKUP_DIR/bracket_db_$TIMESTAMP.dump
gzip $BACKUP_DIR/bracket_db_$TIMESTAMP.dump
# Backup uploads and mediaecho "Backing up uploads..."tar -czf $BACKUP_DIR/bracket_uploads_$TIMESTAMP.tar.gz \ /app/uploads
# Backup application dataecho "Backing up application data..."tar -czf $BACKUP_DIR/bracket_data_$TIMESTAMP.tar.gz \ /app/data \ --exclude='/app/data/cache' \ 2>/dev/null || true
# Backup logsecho "Backing up logs..."tar -czf $BACKUP_DIR/bracket_logs_$TIMESTAMP.tar.gz \ /app/logs 2>/dev/null || true
# Cleanup old backupsecho "Cleaning up old backups..."find $BACKUP_DIR -name "bracket_*" -mtime +$RETENTION_DAYS -delete
# Verify backupsecho "Verifying backups..."for file in $BACKUP_DIR/*_$TIMESTAMP.*; do if [ -f "$file" ]; then size=$(du -h "$file" | awk '{print $1}') echo "✓ Created: $(basename $file) ($size)" fidone
# Calculate total backup sizeTOTAL_SIZE=$(du -sh $BACKUP_DIR | awk '{print $1}')
echo ""echo "Backup completed at: $TIMESTAMP"echo "Total backup size: $TOTAL_SIZE"echo "Backup location: $BACKUP_DIR"
# Database maintenanceecho ""echo "Performing database maintenance..."PGPASSWORD=$DATABASE_PASSWORD vacuumdb \ -h $DATABASE_HOST \ -U $DATABASE_USER \ -d $DATABASE_NAME \ --analyze
# Optional: Upload to cloud storage# aws s3 sync $BACKUP_DIR s3://your-bucket/bracket-backups/ \# --exclude "*" --include "bracket_*_$TIMESTAMP.*" \# --delete
# Optional: Send backup notification# curl -X POST https://hooks.slack.com/services/YOUR/WEBHOOK/URL \# -d "{\"text\":\"Bracket backup completed: $(du -sh $BACKUP_DIR | awk '{print $1}')\"}"cURL - API Integration Examples
# Bracket API Examples
# Get tournament detailscurl -X GET https://api.tournament.yourdomain.com/tournaments/1 \ -H "Content-Type: application/json"
# List all tournaments in clubcurl -X GET "https://api.tournament.yourdomain.com/clubs/1/tournaments" \ -H "Content-Type: application/json"
# Create new tournamentcurl -X POST https://api.tournament.yourdomain.com/tournaments \ -H "Content-Type: application/json" \ -d '{ "club_id": 1, "name": "Spring Tournament 2024", "format": "single_elimination", "start_date": "2024-03-15T10:00:00Z", "max_teams": 16 }'
# Add team to tournamentcurl -X POST https://api.tournament.yourdomain.com/tournaments/1/teams \ -H "Content-Type: application/json" \ -d '{ "team_id": 5 }'
# Get tournament matchescurl -X GET "https://api.tournament.yourdomain.com/tournaments/1/matches" \ -H "Content-Type: application/json"
# Create matchcurl -X POST https://api.tournament.yourdomain.com/tournaments/1/matches \ -H "Content-Type: application/json" \ -d '{ "stage_id": 1, "team1_id": 5, "team2_id": 8, "start_time": "2024-03-15T10:00:00Z", "court_id": 1 }'
# Update match resultcurl -X PUT https://api.tournament.yourdomain.com/matches/123 \ -H "Content-Type: application/json" \ -d '{ "winner_id": 5, "team1_score": 2, "team2_score": 1, "duration_minutes": 45 }'
# Get tournament standingscurl -X GET https://api.tournament.yourdomain.com/tournaments/1/standings \ -H "Content-Type: application/json"
# Export tournament datacurl -X GET "https://api.tournament.yourdomain.com/tournaments/1/export?format=json" \ -H "Content-Type: application/json" \ -o tournament_data.json
# Get tournament schedulecurl -X GET https://api.tournament.yourdomain.com/tournaments/1/schedule \ -H "Content-Type: application/json"
# Reschedule matchcurl -X PUT https://api.tournament.yourdomain.com/matches/123/reschedule \ -H "Content-Type: application/json" \ -d '{ "start_time": "2024-03-15T14:00:00Z", "court_id": 2 }'Tournament Management Best Practices
Tournament Planning
Organize successful tournaments:
-
Bracket Design:
- Choose appropriate format
- Plan number of stages
- Define advancement rules
- Consider team count
-
Scheduling:
- Allow sufficient break times
- Consider venue constraints
- Manage court availability
- Plan for delays
-
Team Management:
- Collect team information early
- Verify player eligibility
- Set registration deadlines
- Manage substitutions
Real-Time Management
Efficiently run tournaments:
- Monitor all matches
- Update results promptly
- Handle disputes professionally
- Manage delays and rescheduling
- Communicate with participants
Public Presentation
Share tournament progress:
- Configure public dashboard
- Update standings in real-time
- Display schedule prominently
- Stream matches if possible
- Share final results
Troubleshooting
Common Issues and Solutions
Issue: Cannot access tournament system
Solutions:
- Verify Klutch.sh app is running
- Check domain DNS resolution
- Verify SSL certificate validity
- Check network connectivity
Issue: Database connection failed
Troubleshooting:
- Verify PostgreSQL is running
- Check database credentials
- Ensure database exists
- Verify firewall rules
- Check database logs
Issue: Matches not scheduling
Solutions:
- Verify all teams registered
- Check tournament structure
- Verify stage configuration
- Review scheduling rules
- Check for conflicts
Issue: Slow performance
Solutions:
- Optimize database queries
- Check server resources
- Monitor database size
- Clear unused data
- Enable caching
Updating Bracket
To update Bracket to a newer version:
- Backup your database and data
- Update your Dockerfile to latest version
- Commit and push to GitHub
- Klutch.sh will automatically rebuild
- Test all tournament functionality
- Verify data integrity
- Monitor logs for any issues
Use Cases
Esports Tournaments
- Gaming competition organization
- Multiple game titles
- Regional and national tournaments
Sports Leagues
- Team sports management
- Season scheduling
- Standings tracking
Community Events
- Gaming meetups
- Competitive gaming
- Community tournaments
Educational Competitions
- School tournaments
- Academic competitions
- Club championships
Additional Resources
- Bracket GitHub Repository - Source code and releases
- Bracket Official Documentation - Comprehensive guides
- Bracket Live Demo - Try before deploying
- Bracket Community Discussions - Support and community
- Klutch.sh Getting Started Guide
- Klutch.sh Volumes Documentation
- Klutch.sh Custom Domains Guide
Conclusion
Deploying Bracket on Klutch.sh provides you with a powerful, self-hosted tournament management system that supports multiple tournament formats and structures. With drag-and-drop scheduling, team management, real-time standings, customizable dashboards, and complete data control, Bracket enables you to organize professional tournaments with ease. Klutch.sh’s managed infrastructure ensures your tournament system is always available, secure, and performant, allowing you to focus on running great tournaments.
Start managing tournaments today by deploying Bracket on Klutch.sh and experience the power of a dedicated tournament management platform.