Skip to content

Fooj Environments

Section: 17 — Fooj
Last Updated: 2026-05-30
Scope: Fooj staging and production environments, NAT GW IP, domain configuration


Overview

Fooj runs two environments: staging and production. Unlike the ERP platform which has five environments (dev, stage, preprod, uat, production), Fooj uses a simpler two-stage model appropriate for its release cadence.

EnvironmentPurposeBranch
stgIntegration testing, UAT, demostage / staging
prodLive production trafficmain / production

Environment Summary

Staging

PropertyValue
Subscriptionf2340b90-2a00-4551-aabc-6e1776e82077
Resource Group (containers)fooj-stg-containers-rg
ACA Environmentfooj-stg-cae
ACRfoojstgacr
Key Vaultfooj-stg-kv
Outbound NAT IP20.26.0.39
Primary Domainstg.fooj.sa
Azure regionUK South

Production

PropertyValue
Subscriptionf2340b90-2a00-4551-aabc-6e1776e82077
Resource Group (containers)fooj-prod-containers-rg
ACA Environmentfooj-prod-cae
ACRfoojprodacr
Key Vaultfooj-prod-kv
Outbound NAT IP20.26.0.39
Primary Domainfooj.sa
Azure regionUK South

NAT Gateway — Shared Egress IP

Both Fooj environments and ERP stage + production share the same outbound IP:

Shared NAT Gateway IP

20.26.0.39 — Used by Fooj staging, Fooj production, ERP staging, and ERP production for all outbound internet traffic.

This IP is the one to provide to any third-party service that requires IP allowlisting (payment processors, SMS gateways, external APIs).

Init Container Gotcha

When initialising Fooj containers for the first time, the NAT Gateway route takes a few seconds to propagate after the ACA environment's VNet integration is configured. During this window, outbound calls from init containers may fail.

Init container network timing

If an init container that performs an outbound call (e.g., database connectivity check, secret resolution) fails immediately after a fresh ACA environment provision, wait 60 seconds and retry. The NAT Gateway route is eventually consistent — it is not instantaneous.

Workaround used in pipelines:

bash
# In the init container entrypoint script
for i in $(seq 1 10); do
  curl -sf https://graph.microsoft.com/v1.0/$metadata && break
  echo "Attempt $i: waiting for network..."
  sleep 10
done

Domain Configuration

Staging — stg.fooj.sa

Record TypeNameValue
CNAMEstg.fooj.safooj-stg-cae.{hash}.uksouth.azurecontainerapps.io
TXTasuid.stg.fooj.saACA domain verification token

Production — fooj.sa

Record TypeNameValue
A@Azure Front Door anycast IP (if AFD is used)
CNAMEwww.fooj.safooj-prod-cae.{hash}.uksouth.azurecontainerapps.io
TXTasuid.fooj.saACA domain verification token

Custom Domain Setup on ACA

bash
# Add custom domain to the container app
az containerapp hostname add \
  --name fooj-ssr \
  --resource-group fooj-stg-containers-rg \
  --hostname stg.fooj.sa

# Bind a managed certificate
az containerapp hostname bind \
  --name fooj-ssr \
  --resource-group fooj-stg-containers-rg \
  --hostname stg.fooj.sa \
  --environment fooj-stg-cae \
  --validation-method CNAME

ACA provisions and renews the TLS certificate automatically once the CNAME record is in place.


Environment Variables

Environment-specific configuration is stored in Key Vault and referenced via keyvaultref: in the ACA container app's environment variable settings.

Staging Key Vault secrets (representative list)

KV Secret NameEnvironment VariableDescription
Fooj--DbConnectionDATABASE_URLPostgreSQL connection string
Fooj--RedisConnectionREDIS_URLRedis cache connection
Fooj--JwtSecretJWT_SECRETToken signing key
Fooj--BlobStorageAZURE_BLOB_CONNECTIONStorage account connection
Fooj--SendgridKeySENDGRID_API_KEYEmail delivery

Promoting secrets from staging to production

Secrets are not copied automatically. Each environment maintains its own Key Vault with independently managed secrets. A promotion checklist in the CI/CD runbook ensures production secrets are set before deploying to production.


Scaling Configuration

Staging

json
{
  "minReplicas": 0,
  "maxReplicas": 3,
  "scaleRules": [
    {
      "name": "http-scale",
      "type": "http",
      "metadata": { "concurrentRequests": "30" }
    }
  ]
}

Staging scales to zero when idle to save cost.

Production

json
{
  "minReplicas": 2,
  "maxReplicas": 20,
  "scaleRules": [
    {
      "name": "http-scale",
      "type": "http",
      "metadata": { "concurrentRequests": "50" }
    }
  ]
}

Production maintains a minimum of 2 replicas to avoid cold-start latency for real users.


Health Checks

Both environments expose a /health endpoint on the SSR container:

bash
# Staging
curl https://stg.fooj.sa/health

# Production
curl https://fooj.sa/health

Expected response:

json
{
  "status": "Healthy",
  "checks": {
    "database": "Healthy",
    "redis": "Healthy"
  }
}

ACA liveness and readiness probes are configured to call /health every 30 seconds with a 5-second timeout.


Differences Between Environments

FeatureStagingProduction
Min replicas0 (scale to zero)2
Zone redundancyNoYes
ACR geo-replicationNoYes
Debug loggingEnabledDisabled
SSR cache TTL60 seconds300 seconds
CDNNot configuredAzure CDN

Internal Documentation — Microtec Platform Team