Skip to content

Infrastructure Philosophy & Technology Decisions

Core Principle: Pragmatism With Future-Ready Architecture

Jubiloop's infrastructure embodies pragmatic decision-making with deliberate growth paths. Every choice is made to:

  1. Optimize for today while ensuring tomorrow's scaling is straightforward
  2. Keep costs predictable without limiting future options
  3. Maintain simplicity that doesn't sacrifice flexibility
  4. Protect what matters (data) while staying lean elsewhere

Key Technology Decisions

Frontend Hosting: Why Cloudflare Pages

The Numbers:

  • Vercel: 100GB bandwidth/month on free tier, only 1 project
  • Cloudflare Pages: Unlimited bandwidth, unlimited projects
  • Vercel Pro ($20/month): Still only 1 team member
  • Vercel scaling: Need $150/month plan for 3 projects

Key Advantage: Unlimited projects means separate projects per environment:

  • jubiloop-webapp-dev
  • jubiloop-webapp-qa
  • jubiloop-webapp-prod
  • jubiloop-marketing-dev
  • jubiloop-marketing-qa
  • jubiloop-marketing-prod

Decision: Cloudflare's unlimited projects enable proper environment isolation at zero cost

Backend Hosting: Why DigitalOcean

Current Setup:

  • 2 droplets: $12/month total ($6 dev-qa + $6 production)
  • Scaling: Resize droplets → Add droplets → Move to AWS

Why Not Hetzner?

  • Considered Hetzner (cheaper VPS option)
  • Chose DigitalOcean for:
    • Superior developer experience
    • Extensive documentation
    • Managed services (databases, spaces, etc.)
    • Better community support

Comparison:

  • AWS: Enterprise-grade but complex pricing, steep learning curve
  • DigitalOcean: Predictable pricing, excellent developer experience
  • Future: Can move to AWS when we need its advanced services

Database: Why Neon + PostgreSQL

The Logic:

  • Database = critical data = must not fail
  • Backups, failover, scaling are complex
  • Neon's free tier: 0.5GB storage, good for starting
  • Scaling path: Free → $19/month → Enterprise
  • Built-in branching for development/preview environments

Key Insight: "Never self-host what you can't afford to lose"

Backend Framework: Why AdonisJS

Why AdonisJS:

  • Batteries included: Auth, ORM, validation, migrations out of the box
  • Laravel-like: Proven patterns, great DX
  • TypeScript first: Not bolted on
  • Integrated: No need to wire 20 packages together

Code Impact:

typescript
// This is all built-in, no setup needed:
@column()
public email: string

@hasMany(() => Event)
public events: HasMany<typeof Event>

// vs Express: install 20 packages, wire them up, hope they work together

Frontend Framework: Why React + TanStack

React + Vite:

  • Team knows it well
  • Massive ecosystem
  • TanStack Router for type-safe routing
  • Zustand for simple state management

Why Not Next.js for Web App:

  • We don't need SSR for an authenticated app
  • Adds complexity without benefits for our use case
  • Marketing site uses Next.js where SEO matters

Infrastructure as Code: Why Not Kubernetes?

Current Reality:

  • Team of 2 developers
  • No dedicated DevOps engineer
  • ~100-1000 initial users expected

The Math:

  • EKS: ~$75/month just for control plane
  • Plus: 2-3 nodes minimum (~$150/month)
  • Plus: Time cost of Kubernetes expertise
  • Total: ~$225/month + significant complexity

Our Choice: Docker + docker compose

  • Cost: $0 (runs on existing infrastructure)
  • Deployment: Simple, debuggable, rollback-friendly
  • Future path: Can migrate to Kubernetes when justified

Infrastructure Evolution Path

Our infrastructure is designed to evolve with clear triggers:

Phase 1: Current (0-1K users)

  • Cost: ~$12-15/month
  • Stack: 2 DO droplets ($12), Neon free tier, Cloudflare free
  • Monitoring: Basic health checks (planned: Sentry for error tracking)

Phase 2: Growth (1K-10K users)

  • Trigger: API response time >500ms consistently
  • Changes:
    • Larger droplet (vertical scaling)
    • Neon paid tier for better performance
    • Add Redis for caching
    • Implement Sentry for error tracking
  • Cost: ~$100-200/month

Phase 3: Scale (10K-50K users)

  • Trigger: Vertical scaling limits reached
  • Changes:
    • Multiple droplets (horizontal scaling)
    • Load balancer
    • Dedicated Redis instance
    • Consider managed services
  • Cost: ~$300-500/month

Phase 4: Enterprise (50K+ users)

  • Trigger: Worth dedicated DevOps engineer
  • Changes:
    • Migrate to AWS/Kubernetes
    • Multi-region deployment
    • Advanced monitoring
  • Cost: ~$1000+/month

Configuration Philosophy

The env.deploy.yml Pattern

Problem: Environment variables scattered everywhere Solution: Single source of truth

yaml
# env.deploy.yml
DEV:
  server:
    DATABASE_URL: from_secrets # Becomes DEV_SERVER_DATABASE_URL
    REDIS_URL: 'redis://localhost:6379'
  webapp:
    API_URL: 'https://dev-api.jubiloop.ca'

Benefits:

  1. Developers add variables to env.deploy.yml file
  2. Add secrets to GitHub Secrets once
  3. Scripts auto-generate all needed files
  4. GitHub Actions injects them everywhere
  5. No manual Cloudflare/DigitalOcean console work

Conventions Over Configuration

Naming Conventions:

  • Service names match everywhere (docker-compose, env.deploy.yml)
  • Environment prefixes are predictable (DEV*, QA*, PROD_)
  • File locations follow patterns

Development Philosophy

Local Development = Production-Like

Principle: If it works locally, it should work in production

Implementation:

  • Docker Compose locally mirrors production
  • Same PostgreSQL version
  • Same Redis setup
  • Same environment variable patterns

Deployment Simplicity

Current Process:

  1. Push to GitHub
  2. GitHub Actions builds and tests
  3. Ansible deploys to DigitalOcean
  4. Zero-downtime deployment via Docker

No:

  • Complex orchestration
  • Service meshes
  • Multi-stage builds (yet)
  • Helm charts

Yes:

  • Simple, understandable process
  • Quick rollbacks
  • Clear logs
  • Fast iteration

Monitoring & Observability Strategy

Current State

  • Application logs via Docker
  • Basic uptime monitoring
  • Manual error checking

Planned Additions

  • Sentry (coming soon): Error tracking and performance monitoring
    • Generous free tier (5K errors/month)
    • Easy integration with both frontend and backend
    • Real user monitoring
  • Future: OpenTelemetry when complexity justifies it

Security Principles

Data Security First

  • Managed database: Automatic encryption, backups
  • HTTPS everywhere: Via Cloudflare
  • Secrets in GitHub: Never in code
  • Session-based auth: HTTP-only cookies, not JWT

Simple Security

  • No: Complex IAM policies, VPNs, bastion hosts
  • Yes: SSH keys, firewall rules, regular updates

Cost Management

Current Monthly Breakdown

  • DigitalOcean Droplets: $12 (2x $6 droplets)
  • Neon Database: $0 (free tier)
  • Cloudflare: $0 (free tier)
  • Domains: ~$2/month (annual cost divided)
  • Total: ~$14/month

Cost Scaling Principles

  1. Use free tiers while they meet needs
  2. Pay for managed services for critical components
  3. Self-host only when cost savings are significant
  4. Monitor usage to predict scaling needs

Technology Integration Benefits

Single Dashboard Approach

  • Cloudflare: DNS + Frontend hosting + SSL
  • DigitalOcean: Backend + Redis + future services
  • GitHub: Code + CI/CD + Secrets
  • Neon: Database with branching

Migration-Ready Architecture

  • Containerized: Apps portable to any cloud
  • Terraform: Infrastructure reproducible anywhere
  • Standard tools: PostgreSQL, Redis, Docker
  • No vendor lock-in: Can switch any component

Future Considerations

When to Migrate Components

Database → Paid Tier:

  • Approaching 0.5GB data limit
  • Need better performance SLAs
  • Require advanced features

Hosting → AWS:

  • Need auto-scaling
  • Require multiple regions
  • Have dedicated DevOps resources

Monitoring → Advanced:

  • Debugging becomes time-consuming
  • Need distributed tracing
  • Require custom metrics

What We're NOT Doing (Yet)

  1. Microservices: Monolith works great for our scale
  2. Kubernetes: Overkill for current needs
  3. Multi-region: Single region is fine for now
  4. CDN for API: Cloudflare handles static assets
  5. Message queues: Direct HTTP calls suffice
  6. GraphQL: REST API is simpler and sufficient

Decision Framework

When evaluating new technology, we ask:

  1. Does it solve a real problem we have today?
  2. Is the complexity worth the benefit?
  3. Can we migrate away from it easily?
  4. Does it fit our budget constraints?
  5. Can our small team manage it?

If not 5/5 "yes", we wait.

Summary

Our infrastructure philosophy is simple:

  1. Start simple, with clear paths to scale
  2. Protect the data, everything else is replaceable
  3. Optimize for developer productivity, not theoretical scale
  4. Pay for what matters, self-host what doesn't
  5. Monitor costs and performance, scale when data justifies it

This approach lets us focus on building features while maintaining a robust, scalable foundation that can grow with our success.

Built with ❤️ by the Jubiloop team