Deployment & Infrastructure Architecture

Deployment Topology

graph TB subgraph GitHub["GitHub Repository (Keglev/stockease)"] MainBranch["main branch
(Source Code)"] DocsBranch["docs branch
(GH Pages output)"] PRs["Pull Requests
(Reviews)"] end MainBranch -->|Push to origin/main| Actions subgraph Actions["GitHub Actions (CI/CD Pipelines)"] Build["Build & Test Workflow
(deploy-backend.yml)
1. Checkout code
2. Run Maven build
3. Run tests (65+)
4. Build Docker image
5. Push to GHCR
6. Deploy to Koyeb"] Docs["Docs Generation Workflow
(docs-pipeline.yml + docs-coverage-deploy.yml)
1. Extract OpenAPI spec
2. Convert Markdown β†’ HTML
3. Generate Redoc HTML
4. Collect coverage
5. Commit to docs branch
6. GitHub Pages auto-sync"] end Build -->|GHCR Image
ghcr.io/keglev/...| Koyeb Docs -->|Generated HTML
docs branch| Pages Koyeb["Koyeb
Container Service
(Production)"] Pages["GitHub Pages
(docs branch)
https://Keglev..."] Koyeb -->|JDBC Connection| Neon Neon["Neon PostgreSQL 17.5
(Serverless Database)
- Auto-backups
- Connection pooling
- Failover support"] style GitHub fill:#f9f9f9 style Actions fill:#e3f2fd style Koyeb fill:#fff3e0 style Pages fill:#e8f5e9 style Neon fill:#fce4ec

CI/CD Pipeline Details

Build & Deploy Workflow (deploy-backend.yml)

Trigger Events: - Push to main branch - Pull request creation - Manual trigger (workflow_dispatch)

Pipeline Stages:

graph TD A[Stage 1 - Checkout & Setup] --> B[Stage 2 - Build] B --> C[Stage 3 - Test] C --> D[Stage 4 - Build Docker Image] D --> E[Stage 5 - Push to Container Registry] E --> F[Stage 6 - Deploy to Koyeb] F --> G[Stage 7 - Verification] A -->|Details| A1["- Checkout code from GitHub
- Set up JDK 17
- Cache Maven dependencies
- Verify build environment"] B -->|Details| B1["- Run Maven clean package
- Compile all Java source code
- Skip tests in this stage
- Generate JAR file"] C -->|Details| C1["- Run 65+ unit tests
- Tests use H2 in-memory database
- Generate coverage reports (JaCoCo)
- Fail pipeline if tests don't pass"] D -->|Details| D1["- Read Dockerfile from repository
- Build image based on Dockerfile
- Tag as ghcr.io/keglev/stockease latest
- Tag as ghcr.io/keglev/stockease commit-sha
- Generate SBOM"] E -->|Details| E1["- Authenticate with GHCR
- Push tagged images to GHCR
- Make images available for deployment
- Store in ghcr.io/keglev/stockease"] F -->|Details| F1["- Get Koyeb API token from secrets
- Trigger Koyeb deployment
- Pull latest image from GHCR
- Perform blue-green deployment
- Health checks (retry up to 5 times)
- Rollback if health check fails"] G -->|Details| G1["- Verify deployment status
- Check health endpoint /health
- Log deployment results
- Notify on success/failure"] style A fill:#e3f2fd style B fill:#e3f2fd style C fill:#e3f2fd style D fill:#e3f2fd style E fill:#e3f2fd style F fill:#e3f2fd style G fill:#c8e6c9

Documentation Pipeline (docs-pipeline.yml + docs-coverage-deploy.yml)

Trigger Events: - Push to main when /backend/docs/ changes - Manual trigger

Pipeline Stages:

graph TD A[Stage 1 - Extract OpenAPI Spec] --> B[Stage 2 - Generate Redoc HTML] B --> C[Stage 3 - Convert Markdown to HTML] C --> D[Stage 4 - Collect Coverage Reports] D --> E[Stage 5 - Commit to Docs Branch] E --> F[Stage 6 - Cleanup] A -->|Details| A1["- Build backend application
- Start Spring Boot server
- Fetch OpenAPI spec from /v3/api-docs
- Validate OpenAPI spec
- Save to docs/api/openapi/openapi.json"] B -->|Details| B1["- Install Redoc CLI
- Read OpenAPI spec
- Generate interactive HTML documentation
- Output to docs/api/redoc/index.html
- Optimize for web"] C -->|Details| C1["- Install pandoc
- For each .md file in docs/architecture/:
* Convert to HTML
* Apply styling
* Output to docs/architecture/*.html
- Convert docs/index.md to docs/index.html
- Generate table of contents"] D -->|Details| D1["- Run Maven test with JaCoCo
- Generate coverage report
- Copy to docs/coverage/
- Generate index.html for coverage dashboard
- Calculate coverage percentage"] E -->|Details| E1["- Switch to docs branch
- Remove old generated files
- Copy all new HTML files
- Commit with auto-generated documentation
- Push to origin/docs
- GitHub Pages auto-publishes"] F -->|Details| F1["- Remove temporary files
- Clean build artifacts
- Log completion"] style A fill:#e3f2fd style B fill:#e3f2fd style C fill:#e3f2fd style D fill:#e3f2fd style E fill:#e3f2fd style F fill:#c8e6c9

Deployment Environments

Development Environment

  • Branch: Any feature branch
  • Triggered By: Manual trigger or PR
  • Database: H2 in-memory (testing only)
  • Deployment: Skipped
  • Testing: Full suite (65+ tests)

Staging Environment

  • Branch: develop (if used)
  • Deployment Target: Separate Koyeb service
  • Database: PostgreSQL test instance
  • Testing: Full test suite + smoke tests
  • Duration: 10-15 minutes

Production Environment

  • Branch: main
  • Deployment Target: Koyeb production service
  • Database: Neon PostgreSQL (production)
  • URL: https://stockease-backend-production.koyeb.app
  • Testing: Full test suite + health checks
  • Duration: 3-5 minutes
  • Rollback: Automatic if health checks fail

Koyeb Deployment Configuration

Service Details

Service Name: stockease-backend-production
Status: HEALTHY
Runtime: Docker (custom image)
Region: US (Auto)
Replicas: 1-2 (auto-scaling)
Domain: stockease-backend-production.koyeb.app

Environment Variables:
  SPRING_PROFILES_ACTIVE: production
  DB_HOST: [Neon endpoint]
  DB_PORT: 5432
  DB_NAME: stockease
  DB_USER: [from secrets]
  DB_PASSWORD: [from secrets]
  JWT_SECRET: [from secrets]
  JAVA_OPTS: -Xmx512m -Xms256m

Port Mapping:
  Container Port: 8080
  Public Port: 443 (HTTPS)
  Protocol: HTTP2

Health Check:
  Endpoint: /health
  Interval: 30 seconds
  Timeout: 5 seconds
  Consecutive Failures: 3
  Consecutive Successes: 1

Auto-Scaling Configuration

Min Replicas: 1
Max Replicas: 2
Target CPU: 70%
Target Memory: 80%
Scale Up Delay: 60 seconds
Scale Down Delay: 300 seconds

Database Management (Neon PostgreSQL)

Connection Details

Host: [neon-project].neon.tech
Port: 5432
Database: stockease
SSL Mode: require
Connection Pooling: Enabled (connection limit: 100)

Automated Backups

  • Frequency: Automatic daily
  • Retention: 7 days
  • Backup Type: Full backup
  • Recovery: Point-in-time restore available

Database Migrations (Flyway)

Migration Files (in src/main/resources/db/migration/):

V1__init_schema.sql
  β”œβ”€β”€ CREATE TABLE users
  β”‚   β”œβ”€β”€ id (UUID, PK)
  β”‚   β”œβ”€β”€ username (VARCHAR, UNIQUE)
  β”‚   β”œβ”€β”€ password (VARCHAR, BCrypt)
  β”‚   β”œβ”€β”€ role (VARCHAR)
  β”‚   β”œβ”€β”€ created_at (TIMESTAMP)
  β”‚   └── updated_at (TIMESTAMP)
  β”‚
  └── CREATE TABLE products
      β”œβ”€β”€ id (UUID, PK)
      β”œβ”€β”€ name (VARCHAR)
      β”œβ”€β”€ description (TEXT)
      β”œβ”€β”€ price (DECIMAL)
      β”œβ”€β”€ quantity (INTEGER)
      β”œβ”€β”€ sku (VARCHAR, UNIQUE)
      β”œβ”€β”€ category (VARCHAR)
      β”œβ”€β”€ created_at (TIMESTAMP)
      β”œβ”€β”€ updated_at (TIMESTAMP)
      └── created_by (UUID, FK β†’ users)

V2__add_indexes.sql
  β”œβ”€β”€ CREATE INDEX idx_products_sku
  β”œβ”€β”€ CREATE INDEX idx_products_category
  β”œβ”€β”€ CREATE INDEX idx_users_username
  └── CREATE INDEX idx_products_created_by

V3__seed_data.sql
  β”œβ”€β”€ INSERT admin user (admin/admin123)
  β”œβ”€β”€ INSERT regular user (user/user123)
  β”œβ”€β”€ INSERT sample products
  └── Uses DB-agnostic existence check for H2 compatibility

Migrations Overview:

erDiagram users { UUID id PK VARCHAR username UK VARCHAR password VARCHAR email UK VARCHAR role TIMESTAMP created_at TIMESTAMP updated_at } products { UUID id PK VARCHAR name TEXT description DECIMAL price INTEGER quantity VARCHAR sku UK VARCHAR category TIMESTAMP created_at TIMESTAMP updated_at UUID created_by FK } users ||--o{ products : "creates"

Migration Files:

graph TD V1[V1__initial_schema.sql] --> V2[V2__add_indexes.sql] V2 --> V3[V3__seed_data.sql] V1 -->|Creates| V1A["- users table
- products table"] V2 -->|Adds| V2A["- idx_products_sku
- idx_products_category
- idx_users_username
- idx_products_created_by"] V3 -->|Inserts| V3A["- admin user (admin/admin123)
- regular user (user/user123)
- sample products
- H2 compatibility checks"] style V1 fill:#e3f2fd style V2 fill:#e3f2fd style V3 fill:#c8e6c9

Migration Execution:

graph TD A[Application Startup] --> B[Spring Boot loads] B --> C[Flyway initializes] C --> D[Checks flyway_schema_history table] D --> E[Identifies new migrations] E --> F[Executes migrations in order] F --> G[Updates schema version] G --> H[Application proceeds with data access] style A fill:#fff3e0 style H fill:#c8e6c9
Application Startup
  β”œβ”€β”€ Spring Boot loads
  β”œβ”€β”€ Flyway initializes
  β”œβ”€β”€ Checks flyway_schema_history table
  β”œβ”€β”€ Identifies new migrations
  β”œβ”€β”€ Executes migrations in order
  β”œβ”€β”€ Updates schema version
  └── Application proceeds with data access

Containerization

Dockerfile

# Multi-stage build for optimization
FROM eclipse-temurin:17-jdk-alpine AS builder

WORKDIR /app
COPY mvnw .
COPY pom.xml .
COPY src src

# Build application
RUN ./mvnw clean package -DskipTests

# Runtime stage
FROM eclipse-temurin:17-jre-alpine

WORKDIR /app

# Copy JAR from builder
COPY --from=builder /app/target/stockease-backend-*.jar app.jar

# Health check
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
    CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1

# Expose port
EXPOSE 8080

# Run application
ENTRYPOINT ["java", "-jar", "app.jar"]

Image Details: - Base Image: eclipse-temurin:17-jre-alpine (122MB) - Final Size: ~350MB (with JAR and dependencies) - Build Time: ~2 minutes - Startup Time: ~8-10 seconds

Image Registry (GHCR)

Registry: ghcr.io/keglev/stockease
Authentication: GitHub PAT (Personal Access Token)

Image Tags:
  - ghcr.io/keglev/stockease:latest (always latest build)
  - ghcr.io/keglev/stockease:v1.0.0 (semantic versioning)
  - ghcr.io/keglev/stockease:sha-abc123 (commit hash)

Monitoring & Observability

Health Checks

Endpoint: GET /health

Response (when healthy):

{
  "status": "UP",
  "components": {
    "db": {
      "status": "UP",
      "details": {
        "database": "PostgreSQL",
        "validationQuery": "isValid()"
      }
    },
    "diskSpace": {
      "status": "UP",
      "details": {
        "total": 10737418240,
        "free": 5368709120,
        "threshold": 10485760,
        "exists": true
      }
    }
  }
}

Response (when unhealthy):

{
  "status": "DOWN",
  "components": {
    "db": {
      "status": "DOWN",
      "details": {
        "error": "Connection refused"
      }
    }
  }
}

Application Logs

Log Output (captured by Koyeb):

[2025-10-31 10:30:00] INFO  o.s.b.StartupInfoLogger : Starting StockEaseApplication
[2025-10-31 10:30:01] INFO  o.s.d.r.c.EnableJpaRepositoriesRegistrar : Scanning JPA repositories
[2025-10-31 10:30:02] INFO  o.f.c.internal.license.VersionPrinter : Flyway Community Edition
[2025-10-31 10:30:02] INFO  o.f.c.internal.database.base.DatabaseFactory : Database: PostgreSQL 17.5
[2025-10-31 10:30:02] INFO  o.f.c.i.s.sql.PlaceholderReplacer : Locations: [classpath:db/migration]
[2025-10-31 10:30:02] INFO  o.f.c.i.c.sql.SqlScriptExecutor : Successfully validated 3 migrations
[2025-10-31 10:30:02] INFO  o.f.c.i.c.sql.SqlScriptExecutor : Current version of schema "public": 3
[2025-10-31 10:30:03] INFO  o.s.s.web.DefaultSecurityFilterChain : Will secure any request
[2025-10-31 10:30:03] INFO  o.s.b.w.e.tomcat.TomcatWebServer : Tomcat started on port(s): 8080
[2025-10-31 10:30:04] INFO  o.s.b.StartupInfoLogger : Started StockEaseApplication in 4.123s

Metrics Monitoring

Tracked Metrics: - Request Count: Requests per minute - Response Time: Average, P50, P95, P99 - Error Rate: 4xx, 5xx errors - Database Connections: Active, idle, queued - Memory Usage: Heap, non-heap - CPU Usage: Container CPU %

Alerts (future): - Health check failures (trigger rollback) - High error rate (>5%) - Slow response times (>500ms avg) - Memory pressure (>85%) - Database connection pool exhaustion

Disaster Recovery

Backup Strategy

  • Database: Automated daily backups (Neon)
  • Code: Git repository with full history
  • Configuration: Environment variables in GitHub Secrets
  • Docker Images: Retained in GHCR

Recovery Procedures

If Deployment Fails: 1. CI pipeline detects failure 2. Health checks fail 3. Koyeb automatically rolls back to previous version 4. Notification sent to team 5. Review logs for root cause

If Database is Corrupted: 1. Determine recovery point from backups 2. Create database restore job 3. Test restore in staging environment 4. Point Koyeb to restored database 5. Verify application functionality

If Koyeb Service Goes Down: 1. Manual trigger of deployment pipeline 2. Redeploy from latest commit 3. Verify health checks pass 4. Monitor for stability

Performance Tuning

JVM Configuration

-Xmx512m          # Max heap: 512MB
-Xms256m          # Initial heap: 256MB
-XX:+UseG1GC      # G1 garbage collection
-XX:MaxGCPauseMillis=200

Database Optimization

Connection Pool: HikariCP
Max Connections: 10
Idle Timeout: 600 seconds
Connection Timeout: 30 seconds

Caching (Future)

  • Spring Cache abstraction
  • Redis for session/data caching
  • TTL: 1 hour for product data

Security in Deployment

Secrets Management

Environment Variables (GitHub Secrets):
  - DB_PASSWORD
  - JWT_SECRET
  - GITHUB_TOKEN (for deployments)

Never Committed to Repository: - Database passwords - API keys - JWT secrets - Personal access tokens

Network Security

  • HTTPS/TLS enforced
  • CORS configured for frontend domain only
  • WAF rules (via Koyeb/CloudFlare)
  • DDoS protection enabled

Image Security

  • No hardcoded secrets in Docker image
  • Alpine Linux for minimal attack surface
  • Regular base image updates
  • SBOM (Software Bill of Materials) generated

Deployment Checklist

Before production deployment: - [ ] All tests passing (65+) - [ ] Code reviewed - [ ] Secrets configured in GitHub - [ ] Database migrations validated - [ ] Docker image built and tested - [ ] Health checks verified - [ ] CORS configured correctly - [ ] SSL certificates valid - [ ] Backups configured - [ ] Monitoring/alerting set up - [ ] Documentation updated - [ ] Rollback plan prepared


Main Architecture Topics

Architecture Decisions (ADRs)

Design Patterns & Practices

Deployment Details


Document Version: 1.0
Last Updated: October 31, 2025
Status: Production Ready