Leaderboard Fantasy: A Tale of Frugal Engineering

A fantasy golf platform that evolved from Excel to GCP to a cost-optimized VPS. This is the story of building a production SaaS application with agentic AI development, then migrating from managed cloud services to a self-hosted Docker stack—proving that prudent engineering decisions and modern tooling can deliver enterprise-grade reliability on a startup budget.

SaaSFantasy SportsDockerVPSCost OptimizationAI-Driven DevelopmentFull-Stack

Leaderboard Fantasy: The Art of Frugal Engineering

Leaderboard Fantasy is a fantasy golf platform with an origin story that captures the essence of pragmatic software development: it started as a color-coded Excel spreadsheet, graduated to a cloud-native GCP deployment, and then evolved into a lean, self-hosted VPS solution—all while serving hundreds of golf fans.

More importantly, it's a case study in frugal engineering: the discipline of building production-quality software without burning through cloud credits, and knowing when managed services are worth the premium versus when they're just expensive convenience.

The Origin Story

Like many great products, Leaderboard Fantasy began with a personal pain point. Managing a fantasy golf pool for friends meant:

  • Manually copying leaderboards every tournament weekend
  • Calculating standings with increasingly complex formulas
  • Sending updates to the group via text and email
  • Hours of work for what should have been simple fun

The spreadsheet grew unwieldy. Friends of friends wanted in. Other groups heard about it and asked to use it too. That's when the realization hit: this wasn't just a personal problem—fantasy golf fans everywhere were either struggling with spreadsheets or settling for platforms that didn't understand the sport.

The Evolution: From GCP to VPS

Phase 1: The Cloud-Native Era

The first production version was built on Google Cloud Platform, embracing every managed service available:

  • Google App Engine for the web frontend
  • Google Cloud Run for the API backend
  • MongoDB Atlas for managed database
  • Redis via Memorystore for sessions
  • Terraform for infrastructure-as-code
  • Google Secret Manager for credentials

It worked beautifully. Auto-scaling, managed SSL, global CDN—the full enterprise experience. But there was a problem: the monthly bill didn't match the usage pattern.

Fantasy golf is seasonal. Traffic spikes during major tournaments, then drops to near-zero between events. Paying for always-on managed services to handle a hobby project's traffic felt increasingly wasteful.

Phase 2: The VPS Migration

In January 2026, Leaderboard Fantasy moved to a budget VPS provider. The goal: maintain production reliability while dramatically reducing costs.

Monthly cost comparison:

Component GCP (Before) VPS (After)
Compute ~$50-80 (App Engine + Cloud Run) ~$12 (VPS)
Database ~$25-40 (MongoDB Atlas) $0 (Self-hosted)
Redis ~$15-25 (Memorystore) $0 (Self-hosted)
Total ~$100-170/month ~$12/month

The lesson: managed services are valuable when you're scaling or need guarantees you can't provide yourself. For a hobby project with predictable traffic, they're often overkill.

The Architecture: Docker + Cloudflare Tunnel

The current production stack runs entirely on a single VPS, orchestrated through Docker Compose with Cloudflare Tunnel providing secure ingress.

┌────────────────────────────────────────────────────────────────────┐
│                           VPS Host                                 │
├────────────────────────────────────────────────────────────────────┤
│                                                                    │
│   ┌──────────────┐                                                 │
│   │  Cloudflare  │◄──── Internet traffic via Cloudflare Edge      │
│   │   Tunnel     │      (DDoS protection, SSL termination)        │
│   └──────┬───────┘                                                 │
│          │                                                         │
│          ▼                                                         │
│   ┌──────────────┐     ┌─────────────────┐     ┌───────────────┐  │
│   │    nginx     │────►│ leaderboard-web │────►│    lfs-data   │  │
│   │   (Proxy)    │     │   (Frontend)    │     │     (API)     │  │
│   └──────────────┘     └─────────────────┘     └───────┬───────┘  │
│                                │                        │          │
│                                ▼                        ▼          │
│                         ┌─────────────┐         ┌─────────────┐   │
│                         │    Redis    │         │   MongoDB   │   │
│                         │ (Sessions)  │         │ (Database)  │   │
│                         └─────────────┘         └─────────────┘   │
│                                                                    │
│   ┌────────────────────────────────────────────────────────────┐  │
│   │                    Docker Compose                           │  │
│   │   • Named volumes for data persistence                      │  │
│   │   • Health checks for automatic recovery                    │  │
│   │   • Isolated Docker network                                 │  │
│   └────────────────────────────────────────────────────────────┘  │
│                                                                    │
└────────────────────────────────────────────────────────────────────┘

Why Cloudflare Tunnel?

The VPS has no ports exposed to the internet except SSH for management. All web traffic flows through Cloudflare Tunnel:

  • Zero public attack surface: No ports to scan, no IPs to DDoS directly
  • Free SSL/TLS: Cloudflare handles certificate management
  • Built-in DDoS protection: Cloudflare's edge network filters malicious traffic
  • Simple networking: No NAT configuration, no firewall rules to manage
# Traffic flow
Internet → Cloudflare Edge → Tunnel → nginx → Application Services

Docker Compose Services

The docker-compose.yml defines six interconnected services:

Service Image Purpose
nginx Custom build Reverse proxy, rate limiting, static caching
leaderboard-web Artifact Registry Spring Boot frontend with Thymeleaf
lfs-data Artifact Registry Spring Boot API with MongoDB
mongodb mongo:7.0 Document database
redis redis:7-alpine Session cache with LRU eviction
cloudflared cloudflare/cloudflared Tunnel client

Each service includes health checks, restart policies, and proper dependency ordering:

leaderboard-web:
  depends_on:
    redis:
      condition: service_healthy
    lfs-data:
      condition: service_healthy
  healthcheck:
    test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
    interval: 30s
    start_period: 90s

The Tech Stack

Backend (lfs-data)

  • Java 21 with Spring Boot 3.5
  • MongoDB 7.0 for document-oriented data storage
  • Spring AI with Google Vertex AI Gemini for AI-powered chat
  • Spring Data MongoDB for repository pattern
  • ULID for time-sortable unique identifiers

Frontend (leaderboard-web)

  • Spring Boot 3.5 with Thymeleaf for server-side rendering
  • Spring Security 6 with OAuth2 (Google, Facebook)
  • Spring Session with Redis for distributed sessions
  • Tailwind CSS for utility-first styling
  • Spring Actuator for health monitoring

Infrastructure

  • Linux VPS (Ubuntu) for compute
  • Docker Compose for container orchestration
  • Cloudflare Tunnel for secure ingress
  • nginx for reverse proxy and rate limiting
  • Google Artifact Registry for container images
  • Google Cloud Storage for database backups

CI/CD

  • GitHub Actions for automated builds and deployments
  • Docker multi-stage builds for optimized images
  • Automatic deployment: Tagged releases (rel_*) trigger production deployment
  • SSH-based deployment: GitHub Actions SSHs to VPS and runs docker compose up

Key Platform Features

Live Tournament Scoring

The platform integrates with PGA Tour data feeds to provide:

  • Real-time leaderboard updates during tournaments
  • Automatic score calculations based on customizable rules
  • Support for weather delays, playoffs, and international events
  • Mobile-optimized views for checking scores on the course

Groups: Persistent Communities

Inspired by the original friend group that started it all:

  • Create public or private groups
  • Invite members via email
  • Leader/member role hierarchy
  • Group message boards for trash talk and strategy discussion
  • Season-long rivalries and persistent standings

Flexible Contest Engine

Every group can customize their experience:

  • Configurable roster sizes (4-20 players)
  • Adjustable scores-to-count rules
  • Custom missed cut penalties
  • Public contests for global competition
  • Private contests for friends only

AI-Powered Chat Assistant

Built with Spring AI and Google Vertex AI Gemini:

  • Player Statistics: Career performance, recent form, tournament history
  • Roster Analysis: Evaluate your picks against field strength
  • Head-to-Head Comparisons: Compare any two players across metrics
  • Smart Alternatives: AI-suggested replacements for injured or underperforming players
  • Context-Aware Advice: Responses tailored to specific tournaments and contests

nginx Configuration Deep Dive

The nginx configuration handles production traffic with proper security and performance tuning:

Domain Routing

# Web frontend
server {
    server_name leaderboardfantasy.com golf.leaderboardfantasy.com;

    location / {
        proxy_pass http://leaderboard_web;
        proxy_set_header X-Forwarded-Proto https;
    }
}

# API backend
server {
    server_name api.leaderboardfantasy.com;

    location / {
        limit_req zone=api burst=50 nodelay;
        proxy_pass http://lfs_data_api;
    }
}

Security Headers

add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy "strict-origin-when-cross-origin";

Rate Limiting

# API: 10 requests/second with burst
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;

# Login: 5 requests/minute (brute force protection)
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;

Cloudflare IP Trust

# Trust Cloudflare proxy headers to get real client IP
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
# ... (all Cloudflare IP ranges)
real_ip_header CF-Connecting-IP;

Automated Backups

Database backups run twice daily via cron, uploading to Google Cloud Storage:

# Cron schedule (UTC)
0 0 * * * root /opt/leaderboard/scripts/backup-mongodb.sh
0 12 * * * root /opt/leaderboard/scripts/backup-mongodb.sh

The backup script:

  1. Runs mongodump inside the container
  2. Compresses to gzip archive
  3. Uploads to GCS bucket
  4. Sends failure notifications via Resend API
  5. Cleans up temporary files
docker exec leaderboard-mongodb mongodump \
    --db=leaderboard-db \
    --archive \
    --gzip > mongodb-backup-${TIMESTAMP}.gz

gcloud storage cp mongodb-backup-${TIMESTAMP}.gz gs://lfs-mongodb-backups/

GCS lifecycle rules automatically clean up old backups, keeping costs minimal.

Deployment Pipeline

GitHub Actions Workflow

Every tagged release triggers automated deployment:

deploy:
  name: Deploy to VPS
  runs-on: ubuntu-latest
  environment:
    name: production
    url: https://leaderboardfantasy.com

  steps:
    - name: Pull and deploy services
      run: |
        ssh ${{ secrets.VPS_USER }}@${{ secrets.VPS_HOST }} << 'EOF'
          cd /opt/leaderboard/app
          docker compose pull
          docker compose up -d
        EOF

Deployment Process

  1. Build: GitHub Actions builds Docker images
  2. Push: Images pushed to Google Artifact Registry
  3. SSH: Workflow SSHs to VPS
  4. Pull: docker compose pull fetches new images
  5. Deploy: docker compose up -d restarts services
  6. Verify: Health checks confirm services are running

Rollback

If deployment fails, rollback is simple:

# SSH to VPS
docker compose pull leaderboard-web:previous-tag
docker compose up -d leaderboard-web

The AI Development Story

Leaderboard Fantasy was developed primarily through agentic AI orchestration—using AI agents for implementation while human judgment guided architecture and product decisions.

The Workflow

  1. Specification: Detailed feature specs written with Claude, divided into 4-8 hour implementation units
  2. Implementation: Claude Code reads the repository, analyzes patterns, implements end-to-end
  3. Quality Gates: GitHub Actions runs linting, tests, and security checks
  4. Review: Human review maintains final say on architecture and quality
  5. Deployment: Tagged releases trigger automated production deployment

Velocity Multiplier

What would traditionally require:

  • Solo developer: 4-6 months of full-time work
  • Small team: 3-4 people, 2-3 months

Actual development: Nights and weekends over 3 months, with AI agents handling mechanical implementation while human judgment guided architecture and user experience.

Lessons in Frugal Engineering

Know When to Self-Host

Self-host when:

  • Traffic is predictable and modest
  • You have the skills to operate the service
  • Cost savings justify the operational overhead
  • The service isn't business-critical 24/7

Use managed services when:

  • You need guaranteed SLAs
  • Scaling is unpredictable or bursty
  • The operational burden exceeds the cost savings
  • Your time is better spent on product

Docker Compose is Production-Ready

For single-server deployments, Docker Compose provides:

  • Service dependency management
  • Health checks and automatic restarts
  • Volume persistence
  • Network isolation
  • Easy rollbacks

You don't always need Kubernetes.

Cloudflare Tunnel Simplifies Everything

No more:

  • Managing SSL certificates
  • Configuring firewalls
  • Setting up NAT rules
  • Worrying about DDoS

Just run the tunnel container and configure hostnames in Cloudflare Dashboard.

Backups Are Non-Negotiable

Even on a budget:

  • Automate backups (cron + script)
  • Store offsite (GCS is cheap)
  • Test restores periodically
  • Set up failure notifications

A VPS can fail. Your data shouldn't be lost.

Future Roadmap

  • Payment Processing: Premium features and prize pools
  • Native Mobile Apps: iOS and Android for on-course score checking
  • Advanced Analytics: Historical trends, predictive modeling
  • API Platform: Public API for third-party integrations

The Real Takeaway

Leaderboard Fantasy proves that production software doesn't require production budgets.

A solo developer with:

  • Clear vision and product sense
  • Appropriate AI agent orchestration
  • Pragmatic infrastructure decisions
  • Basic operations knowledge

...can build and operate a production SaaS for the cost of a Netflix subscription.

The spreadsheet is still saved in Google Drive—a reminder not just of where this started, but of how software development has fundamentally changed. Those friends are still playing, by the way. Now with custom trophies, years of history tracked automatically, and rivalries that have outlasted the original Excel file by years.


Want to play? Join at leaderboardfantasy.com

Curious about the development process? Read the full AI-assisted development story

Technologies Used
  • Java 21
  • Spring Boot 3.5
  • MongoDB
  • Docker
  • Cloudflare Tunnel
  • nginx
  • Redis
  • Spring AI
  • Gemini
  • GitHub Actions
  • Tailwind CSS