Appearance
Deployment Overview
High-Level Deployment Architecture
Jubiloop uses a GitHub Actions-centric deployment system that prioritizes simplicity and single source of truth over complex orchestration. Everything flows through GitHub Actions, making it easy for developers to work without deep infrastructure knowledge.
Core Principle: One source of truth (
env.deploy.yml) → GitHub Actions → Everywhere
Architecture Components
1. Configuration Management
All deployment configuration is centralized in a single source of truth with dynamic generation scripts:
infra/deploy/
├── env.deploy.yml # Single source of truth for all configuration
├── scripts/ # Dynamic configuration generation
│ ├── generate-env-files.js # Generate .env files from env.deploy.yml
│ ├── extract-config.js # Extract terraform, ansible, and docs config
│ └── common.js # Shared utilities for config parsing
├── terraform/ # Infrastructure as code
├── ansible/ # Server configuration
├── dev-qa-docker-compose/ # Dev and QA Docker compositions
└── prod-docker-compose/ # Production Docker compositionThe env.deploy.yml file is processed by scripts that:
- Generate environment files for all services dynamically
- Extract infrastructure configuration for Terraform
- Create Ansible inventories from Terraform state
- Document all required secrets and variables
- Support both auto-generated and explicit secret references
2. Infrastructure Stack
Development/QA Environment (Shared Infrastructure)
┌─────────────────────────────────────────────────────────────┐
│ Cloudflare │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────┐ │
│ │ Cloudflare Pages│ │ Cloudflare Pages│ │ DNS/CDN │ │
│ │ (Dev Web App) │ │ (QA Web App) │ │ │ │
│ └─────────────────┘ └─────────────────┘ └─────────────┘ │
└───────────────────────────┬─────────────────────────────────┘
│ API Requests
┌───────────────────────────▼─────────────────────────────────┐
│ DigitalOcean Droplet ($6/month) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Docker Containers │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ │ │
│ │ │ Caddy Proxy │ │ Caddy Proxy │ │ │
│ │ │ (dev.domain) │ │ (qa.domain) │ │ │
│ │ └────────┬────────┘ └────────┬────────┘ │ │
│ │ │ │ │ │
│ │ ┌────────▼────────┐ ┌────────▼────────┐ │ │
│ │ │ Dev API Server │ │ QA API Server │ │ │
│ │ │ (Port 3333) │ │ (Port 3334) │ │ │
│ │ └────────┬────────┘ └────────┬────────┘ │ │
│ │ │ │ │ │
│ │ ┌────────▼──────────────────────────────┐ │ │
│ │ │ Database Services │ │ │
│ │ │ ┌─────────┐ ┌─────────┐ ┌────────┐│ │ │
│ │ │ │Postgres │ │Postgres │ │ Redis ││ │ │
│ │ │ │ (Dev) │ │ (QA) │ │ (Dev) ││ │ │
│ │ │ └─────────┘ └─────────┘ └────────┘│ │ │
│ │ │ ┌────────┐ │ │ │
│ │ │ │ Redis │ │ │ │
│ │ │ │ (QA) │ │ │ │
│ │ │ └────────┘ │ │ │
│ │ └───────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘Production Environment (Dedicated Infrastructure)
┌─────────────────────────────────────────────────────────────┐
│ Cloudflare │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────┐ │
│ │ Cloudflare Pages│ │ Cloudflare Pages│ │ DNS/CDN │ │
│ │ (Prod Web App) │ │ (Prod Marketing)│ │ │ │
│ └─────────────────┘ └─────────────────┘ └─────────────┘ │
└───────────────────────────┬─────────────────────────────────┘
│ API Requests
┌───────────────────────────▼─────────────────────────────────┐
│ DigitalOcean Droplet ($6/month) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Docker Containers │ │
│ │ ┌─────────────────────────────────────────┐ │ │
│ │ │ Caddy Proxy │ │ │
│ │ │ (api.jubiloop.ca) │ │ │
│ │ └────────────────┬─────────────────────────┘ │ │
│ │ │ │ │
│ │ ┌────────────────▼─────────────────────────┐ │ │
│ │ │ Production API Server │ │ │
│ │ │ (Port 3333) │ │ │
│ │ └────────────────┬─────────────────────────┘ │ │
│ │ │ │ │
│ │ ┌────────────────▼─────────────────────────┐ │ │
│ │ │ Redis Cache │ │ │
│ │ └──────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
│
│ Database Connection
┌───────────────────────────▼─────────────────────────────────┐
│ Neon Database │
│ (Managed PostgreSQL) │
│ Region: US East (Ohio) │
└──────────────────────────────────────────────────────────────┘3. CI/CD Pipeline
Developer Push → GitHub Actions → Build & Test → Deploy
│
├─→ Terraform (Infrastructure for Backend)
├─→ Docker Build (Backend API only)
├─→ Cloudflare Pages (Frontend Apps)
└─→ Ansible (Backend Configuration)Environment Overview
For detailed environment information including URLs, infrastructure, and costs, see the Environment Reference.
Key Environment Features
Development & QA (Shared Infrastructure):
- Automatic deployment on branch push
- Containerized PostgreSQL and Redis
- Let's Encrypt SSL certificates
- Isolated services on same server
- Production-like configuration
Production:
- Dedicated infrastructure
- Managed PostgreSQL (Neon)
- Enhanced security
- Automated backups
- Health monitoring
Deployment Flow
1. Configuration Phase
All environment variables and secrets are defined in env.deploy.yml:
yaml
DEV:
server:
PORT: 3333
DATABASE_URL: from_secrets # Auto-generates DEV_SERVER_DATABASE_URL
APP_KEY: '${{ secrets.DEV_APP_KEY }}' # Explicit secret reference
webapp:
REACT_APP_API_URL: 'https://dev-api.jubiloop.ca'2. Infrastructure Provisioning
Terraform manages infrastructure with state stored in Cloudflare R2:
bash
terraform apply # Creates/updates infrastructure
# → Provisions DigitalOcean droplets
# → Configures Cloudflare DNS
# → Sets up networking
# → Stores state in R2 bucket3. Application Build
GitHub Actions builds and packages applications:
bash
# Docker images built and pushed to GitHub Container Registry
docker build -t ghcr.io/org/jubiloop-server:latest .
docker push ghcr.io/org/jubiloop-server:latest4. Configuration Deployment
Dynamic configuration generation and deployment via Ansible:
bash
# Generate environment files from env.deploy.yml
node scripts/generate-env-files.js DEV --server
# Deploy with Ansible using dynamic inventory
ansible-playbook -i inventory.ini playbook.yml5. Service Orchestration
Backend services are managed via Docker Compose:
yaml
services:
caddy: # Reverse proxy with automatic HTTPS for API
server: # AdonisJS API
postgres: # Database (dev/qa only, production uses managed DB)
redis: # Cache layerFrontend apps are deployed separately to Cloudflare Pages:
- Web App: Built and deployed via
deploy-web-app.ymlworkflow - Marketing: Built and deployed via
deploy-marketing.ymlworkflow
Key Features
Dynamic Configuration System
- Single Source of Truth: All configuration in
env.deploy.yml - Automatic Secret Management: Generates secret names from patterns
- Environment Inheritance: Share common configs across environments
- Validation: Built-in configuration validation and documentation
Infrastructure State Management
Terraform → Cloudflare R2 → Application Deployment
│ │ │
└── Creates ───┴── Stores IP ──┴── Reads State- No hardcoded server addresses
- Dynamic inventory generation
- State-driven deployments
Security Architecture
Network Security
- Cloudflare DDoS protection
- Firewall rules via DigitalOcean
- Internal Docker networks
- SSL/TLS everywhere
Application Security
- Environment variable isolation
- Secrets never in code
- Automated security updates
- Fail2ban for intrusion prevention
Access Control
- SSH key-based authentication
- Deploy user with minimal permissions
- GitHub Actions OIDC for cloud access
- Role-based deployment permissions
Monitoring and Observability
- Health Checks: Application and infrastructure health endpoints
- Logging: Centralized Docker logging with rotation
- Metrics: Resource usage and performance monitoring
- Alerting: Automated alerts for failures
Deployment Automation
GitHub Actions Workflows
Infrastructure Deployment (
terraform-deploy.yml)- Manual trigger with environment selection
- Plans and applies Terraform changes
- Updates infrastructure state in R2
Application Deployment (
deploy-app.yml)- Automatic on branch push
- Builds and tests applications
- Deploys via Ansible
- Runs post-deployment checks
Deployment Triggers
| Branch | Environment | Trigger Type |
|---|---|---|
develop | Development | Automatic |
qa | QA | Automatic |
main | Production | Automatic |
| any | any | Manual |
Cost Optimization
Development/QA Strategy
- Shared Infrastructure: Single droplet for both environments
- Container Isolation: Separate services via Docker
- Resource Efficiency: ~60% cost savings vs separate servers
Production Strategy
- Dedicated Resources: Isolated production environment
- Managed Services: DigitalOcean managed database
- Auto-scaling Ready: Infrastructure supports horizontal scaling
Disaster Recovery
Backup Strategy
- Database: Automated daily backups with 7-day retention
- Configuration: Version controlled in Git
- State Files: Redundant storage in Cloudflare R2
- Application Data: File storage not yet implemented
Recovery Procedures
- Infrastructure Recovery: Terraform can recreate from state
- Data Recovery: Restore from managed database backups
- Configuration Recovery: Redeploy from Git repository
- Zero-downtime Updates: Rolling deployments supported
Related Documentation
- CI/CD Pipeline - GitHub Actions workflows
- Infrastructure - Cloud infrastructure details
- Environments - Environment configurations
- Secrets Management - Credential handling
- Terraform Setup - Infrastructure as code guide
- Server Configuration - Ansible deployment details
- Architecture Overview - System architecture
- Local Development - Development setup