Skip to main content

Overview

This guide covers deploying the Product Distribution Dashboard to production environments, with detailed instructions for Render.com deployment and configuration for Azure PostgreSQL.

Deployment Architecture

The application uses a multi-service architecture:
  • Backend: Docker-based Spring Boot application (Java 17)
  • Frontend: Static Angular build served via CDN
  • Database: Azure PostgreSQL managed database

Render.com Deployment

The application is configured for deployment on Render using the render.yaml Blueprint specification.

Prerequisites

1

Render Account

Create a free account at render.com
2

GitHub Repository

Push your code to a GitHub repository
3

Azure PostgreSQL Database

Set up a PostgreSQL database on Azure (or another provider)
Render offers managed PostgreSQL, but the configuration uses Azure for production.

Render Blueprint Configuration

The render.yaml file defines the deployment configuration:
services:
  - type: web
    name: product-distribution-backend
    runtime: docker
    region: frankfurt
    dockerfilePath: ./backend/Dockerfile
    dockerContext: .
    plan: free
    buildFilter:
      paths:
        - backend/**
        - render.yaml
    envVars:
      - key: SPRING_PROFILES_ACTIVE
        value: prod
      - key: DATABASE_HOST
        value: product-distribution-db.postgres.database.azure.com
      - key: DATABASE_PORT
        value: 5432
      - key: DATABASE_NAME
        value: product_distribution_db
      - key: DATABASE_USERNAME
        value: product_distribution
      - key: DATABASE_PASSWORD
        sync: false
      - key: APP_FRONTEND_URL
        value: https://product-distribution-frontend.onrender.com
    healthCheckPath: /actuator/health

  - type: web
    name: product-distribution-frontend
    runtime: static
    buildCommand: cd frontend && npm ci && npm run build
    staticPublishPath: frontend/dist/frontend/browser
    buildFilter:
      paths:
        - frontend/**
        - render.yaml

Deployment Steps

1

Connect Repository to Render

  1. Log in to Render Dashboard
  2. Click “New” → “Blueprint”
  3. Connect your GitHub repository
  4. Render will automatically detect render.yaml
2

Configure Environment Variables

Before deploying, set sensitive environment variables that are not in render.yaml:Backend Service:
  • DATABASE_PASSWORD: Your Azure PostgreSQL password
Navigate to the backend service → Environment → Add Environment Variable
Never commit database passwords to render.yaml. Use Render’s environment variable management.
3

Deploy Services

Click “Apply” to deploy both services:
  1. Backend builds from Docker image
  2. Frontend builds and deploys static assets
  3. Render assigns URLs to each service
4

Verify Deployment

Check deployment status:
  • Backend health check: https://product-distribution-backend.onrender.com/actuator/health
  • Frontend: https://product-distribution-frontend.onrender.com
Initial deployment may take 5-10 minutes as Render builds the Docker image and installs dependencies.

Database Setup (Azure PostgreSQL)

The production environment uses Azure PostgreSQL with SSL enabled.

Creating Azure PostgreSQL Database

1

Create PostgreSQL Server

Using Azure CLI:
az postgres flexible-server create \
  --name product-distribution-db \
  --resource-group <your-resource-group> \
  --location westeurope \
  --admin-user product_distribution \
  --admin-password <secure-password> \
  --sku-name Standard_B1ms \
  --tier Burstable \
  --storage-size 32 \
  --version 16
2

Create Database

az postgres flexible-server db create \
  --resource-group <your-resource-group> \
  --server-name product-distribution-db \
  --database-name product_distribution_db
3

Configure Firewall Rules

Allow connections from Render’s IP addresses:
az postgres flexible-server firewall-rule create \
  --resource-group <your-resource-group> \
  --name product-distribution-db \
  --rule-name AllowRender \
  --start-ip-address 0.0.0.0 \
  --end-ip-address 255.255.255.255
This allows all IPs. For production, restrict to Render’s specific IP ranges.
4

Enable SSL Enforcement

az postgres flexible-server parameter set \
  --resource-group <your-resource-group> \
  --server-name product-distribution-db \
  --name require_secure_transport \
  --value ON

Database Connection String

The production profile (application-prod.properties) uses:
spring.datasource.url=jdbc:postgresql://${DATABASE_HOST}:${DATABASE_PORT}/${DATABASE_NAME}?sslmode=require&reWriteBatchedInserts=true
spring.datasource.username=${DATABASE_USERNAME}
spring.datasource.password=${DATABASE_PASSWORD}
Environment Variables:
  • DATABASE_HOST: product-distribution-db.postgres.database.azure.com
  • DATABASE_PORT: 5432
  • DATABASE_NAME: product_distribution_db
  • DATABASE_USERNAME: product_distribution
  • DATABASE_PASSWORD: (set in Render environment)

Production Configuration

Backend Production Profile

The prod profile includes optimizations for production:
# Connection Pool Configuration (HikariCP)
spring.datasource.hikari.maximum-pool-size=10
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.idle-timeout=600000
spring.datasource.hikari.max-lifetime=1800000

# JPA/Hibernate Batch Configuration for performance
spring.jpa.properties.hibernate.jdbc.batch_size=100
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true

# Server Configuration
server.address=0.0.0.0

# CORS Configuration
app.frontend.url=${APP_FRONTEND_URL}

# Scheduler (runs daily at 2 AM)
scheduler.distribution.cron=${SCHEDULER_DISTRIBUTION_CRON:0 0 2 * * *}

Environment Variables Reference

VariableRequiredDescriptionExample
SPRING_PROFILES_ACTIVEYesSpring profileprod
DATABASE_HOSTYesAzure PostgreSQL host*.postgres.database.azure.com
DATABASE_PORTYesDatabase port5432
DATABASE_NAMEYesDatabase nameproduct_distribution_db
DATABASE_USERNAMEYesDatabase userproduct_distribution
DATABASE_PASSWORDYesDatabase password(secret)
APP_FRONTEND_URLYesFrontend URL for CORShttps://your-app.onrender.com
PORTNoServer port8080 (default)
SCHEDULER_DISTRIBUTION_CRONNoCron schedule0 0 2 * * * (2 AM daily)
DATA_PRODUCTS_URLNoProducts data sourceGitHub raw URL
DATA_STORES_URLNoStores data sourceGitHub raw URL
DATA_WAREHOUSES_URLNoWarehouses data sourceGitHub raw URL

Health Checks

Backend Health Endpoint

The backend exposes Spring Boot Actuator health checks: Endpoint: /actuator/health Response:
{
  "status": "UP"
}
Render Configuration:
healthCheckPath: /actuator/health
Render automatically monitors this endpoint and restarts the service if health checks fail.

Exposed Actuator Endpoints

management.endpoints.web.exposure.include=health,info
Only health and info endpoints are exposed in production for security.

Docker Configuration

Backend Dockerfile

Multi-stage build for optimized production images:
# Build stage
FROM maven:3.9-eclipse-temurin-17 AS build
WORKDIR /app

COPY backend/pom.xml .
RUN mvn dependency:go-offline -B

COPY backend/src ./src
RUN mvn clean package -DskipTests

# Runtime stage
FROM eclipse-temurin:17-jre-jammy
WORKDIR /app
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*

COPY --from=build /app/target/*.jar app.jar

# Create non-root user
RUN groupadd -r spring && useradd -r -g spring spring && \
chown -R spring:spring /app
USER spring:spring

EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
  CMD curl -f http://localhost:8080/actuator/health || exit 1

ENTRYPOINT ["java", "-jar", "app.jar"]
Key Features:
  • Multi-stage build reduces final image size
  • Non-root user for security
  • Built-in health check
  • Cached dependency layer for faster builds

Frontend Build

The frontend is built and deployed as static files:
cd frontend && npm ci && npm run build
Output: frontend/dist/frontend/browser/ Render serves these files via CDN with automatic HTTPS.

Deployment Workflow

Automatic Deployments

Render automatically deploys when changes are pushed to the main branch:
1

Push Code to GitHub

git add .
git commit -m "Update feature"
git push origin main
2

Render Detects Changes

Based on buildFilter paths, Render determines which services to rebuild:
  • Changes in backend/** → rebuild backend
  • Changes in frontend/** → rebuild frontend
  • Changes in render.yaml → rebuild both
3

Build and Deploy

Render builds and deploys updated services automatically
4

Health Check Verification

Render waits for health checks to pass before routing traffic to the new deployment

Manual Deployments

Deploy manually from the Render dashboard:
  1. Navigate to service
  2. Click “Manual Deploy” → “Deploy latest commit”
  3. Select branch to deploy

Monitoring and Logs

Viewing Logs

Render Dashboard:
  • Navigate to service → Logs
  • Real-time log streaming
  • Search and filter capabilities
CLI:
render logs <service-id>

Metrics and Monitoring

Render provides:
  • CPU usage graphs
  • Memory usage tracking
  • Request metrics
  • Health check history
For advanced monitoring, integrate with external services like Datadog, New Relic, or Sentry.

Scaling

Horizontal Scaling

Render supports horizontal scaling for paid plans:
services:
  - type: web
    name: product-distribution-backend
    numInstances: 3  # Run 3 instances

Vertical Scaling

Upgrade instance size for more resources:
plan: starter  # or standard, pro
Available Plans:
  • Free: 512 MB RAM, 0.1 CPU
  • Starter: 2 GB RAM, 1 CPU
  • Standard: 4 GB RAM, 2 CPU
  • Pro: 8 GB RAM, 4 CPU

Security Best Practices

1

Use Environment Variables for Secrets

Never commit sensitive data to render.yaml:
- key: DATABASE_PASSWORD
  sync: false  # Requires manual configuration
2

Enable SSL/TLS

  • Render provides automatic HTTPS
  • Azure PostgreSQL enforces SSL with sslmode=require
3

Restrict Database Access

Configure Azure firewall rules to allow only Render’s IP addresses
4

Use Non-Root Docker User

The backend Dockerfile runs as non-root user spring:spring
5

Limit Exposed Endpoints

Only expose necessary Actuator endpoints:
management.endpoints.web.exposure.include=health,info

Troubleshooting

Backend Won’t Start

Check Environment Variables:
render shell <service-id>
env | grep DATABASE
Verify Database Connection:
psql -h <DATABASE_HOST> -U <DATABASE_USERNAME> -d <DATABASE_NAME>

Database Connection Errors

Symptoms: Backend logs show connection timeouts Solutions:
  1. Verify Azure firewall rules allow Render IPs
  2. Check database credentials
  3. Ensure SSL is configured: sslmode=require
  4. Verify database is running:
    az postgres flexible-server show \
      --resource-group <group> \
      --name product-distribution-db
    

Build Failures

Frontend Build Fails:
  • Check Node.js version compatibility (requires Node 20)
  • Verify package-lock.json is committed
  • Review build logs for dependency errors
Backend Build Fails:
  • Check Java version (requires Java 17)
  • Verify Maven dependencies are accessible
  • Review Dockerfile for syntax errors

High Memory Usage

Solution: Adjust JVM memory settings: Add to Dockerfile:
ENTRYPOINT ["java", "-Xmx512m", "-Xms256m", "-jar", "app.jar"]

Slow Performance

Solutions:
  1. Increase instance size (upgrade plan)
  2. Optimize database connection pool:
    spring.datasource.hikari.maximum-pool-size=20
    
  3. Enable database query caching
  4. Add database indexes for frequently queried fields

Rollback

Render maintains deployment history:
1

View Deployment History

Navigate to service → Deploys tab
2

Rollback to Previous Version

Click “Rollback” next to any previous successful deployment
3

Verify Rollback

Check logs and health endpoints to confirm service is running correctly

Next Steps

Docker Setup

Run the application locally using Docker Compose

Local Development

Set up your local development environment

Build docs developers (and LLMs) love