Skip to main content

Install Fluent Manager in Azure App Services

This guide walks through deploying Fluent Manager using Azure App Services with an Azure Database for PostgreSQL flexible server. All steps are performed through the Azure Portal.

Prerequisites

  • An Azure subscription with Contributor permissions (or higher)

  • A resource group — you can use an existing one or create a new one. All resources in this guide must be in the same region.

  • An Azure Database for PostgreSQL flexible server with:

    • A database named fluent
    • A schema named manager created inside the fluent database

    For instructions on setting up the database and schema, see Setting Up Your Database. For creating an Azure Database for PostgreSQL flexible server, refer to Microsoft's documentation.

  • An Azure Virtual Network with the backend App Service and PostgreSQL server connected privately. We recommend placing the database behind a VNet with private access — refer to your organization's security team for VNet and Private DNS zone configuration. At minimum, you will need:

    • A subnet delegated to Microsoft.Web/serverFarms for the App Service (with a Microsoft.Storage service endpoint if using Azure Files for WebDav)
    • A subnet delegated to Microsoft.DBforPostgreSQL/flexibleServers for the database
  • The Fluent Manager container image versions you want to deploy — images are hosted at public.ecr.aws/apryse/

  • A generated .env file from the Kotlin deploy script (for the keyset handle and JWT key). See Generate Configuration Secrets for details

info

Before starting, decide on a consistent name prefix (e.g., fluent-manager) that you will use across all resources. This guide uses fluent-manager as the prefix throughout.

Deployment

Step 1 — Create a Storage Account and File Share

The Azure Files share provides shared storage for WebDav, allowing multiple backend instances to share files.

Create the Storage Account:

  1. Search for Storage accounts and click Create
  2. On the Basics tab:
    • Select your resource group
    • Enter a Storage account name (e.g., fluentmanagerfiles) — must be globally unique, lowercase, no hyphens
    • Select the same region
    • Under Redundancy, select Zone-redundant storage (ZRS) for HA
  3. On the Networking tab:
    • Select Enable public access from selected virtual networks and IP addresses
    • Click Add existing virtual network, select your VNet and the App Service subnet
  4. Click Review + create, then Create

Create the File Share:

  1. Open the storage account
  2. In the left menu under Data storage, click File shares
  3. Click + File share
  4. Enter a name (e.g., webdav)
  5. Select an Access tier (e.g., Transaction optimized)
  6. Click Review + create, then Create

Step 2 — Generate Configuration Secrets

The backend requires a FLUENT_MANAGER_KEYSET_HANDLE (AES-256 encryption key for connection strings) and a FLUENT_MANAGER_JWT_PRIVATE_KEY (for authentication tokens). These are generated by the Kotlin deploy script.

If you have an existing Fluent Manager deployment, copy these values from your existing .env file.

For a fresh deployment, generate them using the deploy script:

  1. Download the deploy script matching your Manager version from JFrog
  2. Run the following command to generate a .env file (on Windows, put the entire command on one line):
kotlin fluent-deploy-docker-compose-x.y.z.main.kts create-config-file --admin-email=admin@yourdomain.com --admin-password=YourSecurePassword123!
  1. Open the generated .env file and note down the values for:
    • FLUENT_MANAGER_KEYSET_HANDLE
    • FLUENT_MANAGER_JWT_PRIVATE_KEY
danger

Securely back up the keyset handle. If the keyset handle is lost, connection strings stored in the database cannot be decrypted.

Step 3 — Create the App Service Plan

The App Service Plan defines the compute resources shared by all App Services.

  1. Search for App Service plans and click Create
  2. On the Basics tab:
    • Select your resource group
    • Enter a name (e.g., fluent-manager-plan)
    • Under Operating System, select Linux
    • Select the same region
    • Under Pricing plan, select Premium V3 P1v3 or higher
    • Under Zone redundancy, select Enabled
info

Zone-redundant App Service plans require a Premium v3 SKU or higher. With zone redundancy enabled, the minimum instance count is 2, and Azure distributes instances across availability zones automatically.

caution

Quota: If you receive a quota error when creating the App Service plan, your subscription may not have sufficient vCPU quota for Premium SKUs in this region. Check Quotas in the portal and request an increase, or use a non-zone-redundant plan with a lower SKU (e.g., Basic B3) for initial testing.

  1. Click Review + create, then Create

Step 4 — Deploy the Backend App Service

Create the Web App:

  1. Search for App Services and click Create > Web App
  2. On the Basics tab:
    • Select your resource group
    • Enter a name (e.g., fluent-manager-backend) — this becomes part of the URL
    • If you see a "Try a secure unique default hostname" toggle, disable it (otherwise Azure generates a random hostname suffix)
    • Under Publish, select Container
    • Under Operating System, select Linux
    • Select your App Service plan (fluent-manager-plan)
  3. On the Container tab:
    • Image Source: Select Other container registries
    • Image type: Select Public
    • Registry server URL: https://public.ecr.aws
    • Image and tag: apryse/fluent-manager-backend:x.y.z (replace x.y.z with your version)
    • Port: 8080
  4. On the Networking tab:
    • Enable public access: On
    • Enable virtual network integration: On
    • Select your VNet and App Service subnet
  5. Click Review + create, then Create

Immediately after creation, stop the App Service to prevent it from starting before environment variables are configured:

  1. Open the backend App Service
  2. Click Stop in the top toolbar and confirm

Configure VNet integration routing:

  1. In the backend App Service left menu, go to Networking
  2. Click on the VNet integration link
  3. Under Application routing, ensure Outbound internet traffic is unchecked
  4. Under Configuration routing, ensure Container image pull is unchecked
danger

The Outbound internet traffic checkbox must be unchecked. When checked, all outbound traffic routes through the VNet. Since the VNet has no NAT gateway, the container cannot pull images from ECR and will fail silently — you will see "Failed to get container logs for main" with no other errors. This is the most common cause of containers not starting. This setting can sometimes reset when the App Service Plan is changed or the App Service is recreated.

Configure environment variables:

  1. In the left menu under Settings, click Environment variables
  2. Click Advanced edit and paste the following JSON
  3. Edit the values in the EDIT THESE VALUES section before saving
  4. Leave the PRESET VALUES section as-is unless you have a specific reason to change it
[
{"name": "FLUENT_MANAGER_JWT_PRIVATE_KEY", "value": "<from Step 2>", "slotSetting": false},
{"name": "FLUENT_MANAGER_KEYSET_HANDLE", "value": "<from Step 2>", "slotSetting": false},
{"name": "FLUENT_MANAGER_DATABASE_URL", "value": "<your-server>.postgres.database.azure.com:5432", "slotSetting": false},
{"name": "FLUENT_MANAGER_DATABASE_USERNAME", "value": "<your-pg-admin-user>", "slotSetting": false},
{"name": "FLUENT_MANAGER_DATABASE_PASSWORD", "value": "<your-pg-password>", "slotSetting": false},
{"name": "POSTGRES_USER", "value": "<your-pg-admin-user>", "slotSetting": false},
{"name": "POSTGRES_PASSWORD", "value": "<your-pg-password>", "slotSetting": false},
{"name": "FLUENT_MANAGER_DEFAULT_ADMIN_EMAIL", "value": "<your-admin-email>", "slotSetting": false},
{"name": "FLUENT_MANAGER_DEFAULT_ADMIN_PASSWORD", "value": "<your-admin-password-12-to-64-chars>", "slotSetting": false},

{"name": "WEBSITES_CONTAINER_START_TIME_LIMIT", "value": "600", "slotSetting": false},
{"name": "SPRING_PROFILES_ACTIVE", "value": "prod, fluent", "slotSetting": false},
{"name": "FLUENT_MANAGER_SDK_TYPE", "value": "fluent", "slotSetting": false},
{"name": "FLUENT_MANAGER_ACCESS_TOKEN_TIME_TO_LIVE", "value": "86400", "slotSetting": false},
{"name": "FLUENT_MANAGER_REFRESH_TOKEN_TIME_TO_LIVE", "value": "1209600", "slotSetting": false},
{"name": "FLUENT_MANAGER_COOKIES_TIME_TO_LIVE", "value": "2592000", "slotSetting": false},
{"name": "FLUENT_MANAGER_MAXIMUM_FAILED_LOGIN_ATTEMPTS", "value": "5", "slotSetting": false},
{"name": "FLUENT_MANAGER_CORS_ALLOWED_PATHS", "value": "/**", "slotSetting": false},
{"name": "FLUENT_MANAGER_CORS_ALLOWED_ORIGINS", "value": "*", "slotSetting": false},
{"name": "FLUENT_MANAGER_CORS_ALLOWED_METHODS", "value": "*", "slotSetting": false},
{"name": "FLUENT_MANAGER_DATABASE_NAME", "value": "fluent", "slotSetting": false},
{"name": "FLUENT_MANAGER_RESET_SYSTEM_ADMINISTRATOR_CREDENTIALS", "value": "false", "slotSetting": false},
{"name": "FLUENT_MANAGER_MAILING_ENABLE", "value": "false", "slotSetting": false},
{"name": "FLUENT_MANAGER_SMTP_HOST", "value": "smtp.gmail.com", "slotSetting": false},
{"name": "FLUENT_MANAGER_SMTP_PORT", "value": "587", "slotSetting": false},
{"name": "FLUENT_MANAGER_SMTP_USERNAME", "value": "admin@gmail.com", "slotSetting": false},
{"name": "FLUENT_MANAGER_SMTP_PASSWORD", "value": "placeholder", "slotSetting": false},
{"name": "FLUENT_MANAGER_SMTP_FROM", "value": "admin@email.com", "slotSetting": false},
{"name": "FLUENT_MANAGER_SMTP_AUTH", "value": "true", "slotSetting": false},
{"name": "FLUENT_MANAGER_SMTP_TLS_ENABLE", "value": "true", "slotSetting": false},
{"name": "FLUENT_MANAGER_LICENSE_SUBSCRIPTION_ENABLE", "value": "true", "slotSetting": false},
{"name": "FLUENT_MANAGER_VAULT_ENABLE", "value": "false", "slotSetting": false},
{"name": "FLUENT_MANAGER_VAULT_URI", "value": "http://localhost:8200", "slotSetting": false},
{"name": "FLUENT_MANAGER_VAULT_TOKEN", "value": "static-token-value", "slotSetting": false},
{"name": "FLUENT_MANAGER_VAULT_SECRET_ENGINE_PATH", "value": "fluent-manager", "slotSetting": false},
{"name": "FLUENT_MANAGER_SENTRY_DSN", "value": "", "slotSetting": false},
{"name": "FLUENT_MANAGER_SENTRY_ENVIRONMENT", "value": "", "slotSetting": false},
{"name": "PUBLIC_URL", "value": "", "slotSetting": false},
{"name": "POSTGRES_DB", "value": "fluent", "slotSetting": false},
{"name": "WEBSITES_PORT", "value": "8080", "slotSetting": false},
{"name": "WEBSITES_ENABLE_APP_SERVICE_STORAGE", "value": "false", "slotSetting": false}
]
info

In the JSON above, the entries at the top are the ones you must edit for your environment. The remaining entries are preset defaults from the standard deployment.

  1. Click Apply, then confirm
info

All environment variables are required — the backend Spring Boot application does not provide defaults for most of these and will fail to start if any are missing.

tip

To enable SMTP mailing, set FLUENT_MANAGER_MAILING_ENABLE to true and update the FLUENT_MANAGER_SMTP_* settings with your actual mail server details. See the SMTP Configuration Example for details.

Mount Azure Files for WebDav storage:

  1. In the backend App Service left menu under Settings, click Configuration
  2. Click the Path mappings tab
  3. Click New Azure Storage Mount
  4. Fill in:
    • Name: webdav-mount
    • Configuration options: Basic
    • Storage type: Azure Files
    • Protocol: SMB
    • Account name: Select your storage account (e.g., fluentmanagerfiles)
    • Share name / Storage container: Select webdav
    • Mount path: /mnt/webdav
  5. Click OK, then Save

Start the backend:

  1. Click Start in the top toolbar
  2. Wait 2–3 minutes, then check the Log stream (under Monitoring) for the Spring Boot banner and Started TemplateManagerApplication message
  3. Verify the backend is running by opening its Default domain URL — you should see an API response (or a 401 Unauthorized, which is normal)

Step 5 — Deploy the Frontend App Service

Create the Web App:

  1. Search for App Services and click Create > Web App
  2. On the Basics tab:
    • Select your resource group
    • Enter a name (e.g., fluent-manager-frontend)
    • If you see a "Try a secure unique default hostname" toggle, disable it
    • Under Publish, select Container
    • Under Operating System, select Linux
    • Select your App Service plan (fluent-manager-plan)
  3. On the Container tab:
    • Image Source: Select Other container registries
    • Image type: Select Public
    • Registry server URL: https://public.ecr.aws
    • Image and tag: apryse/fluent-manager-frontend:x.y.z (replace x.y.z with your version)
    • Port: 8080
  4. Click Review + create, then Create

Immediately after creation, stop the App Service:

  1. Open the frontend App Service
  2. Click Stop in the top toolbar and confirm

Configure environment variables:

  1. In the left menu under Settings, click Environment variables
  2. Click Advanced edit and paste the following JSON
  3. Edit the values in the EDIT THESE VALUES section before saving
  4. Leave the PRESET VALUES section as-is unless you have a specific reason to change it
[
{"name": "BACKEND_HOST", "value": "<your-backend-app-name>.azurewebsites.net", "slotSetting": false},

{"name": "BACKEND_SCHEME", "value": "https", "slotSetting": false},
{"name": "PUBLIC_URL", "value": "", "slotSetting": false},
{"name": "FLUENT_MANAGER_BE_RATE_REQ_ZONE_SIZE_MB", "value": "10", "slotSetting": false},
{"name": "FLUENT_MANAGER_BE_RATE_RPS", "value": "100", "slotSetting": false},
{"name": "FLUENT_MANAGER_BE_RATE_BURST", "value": "200", "slotSetting": false},
{"name": "FLUENT_MANAGER_FE_RATE_REQ_ZONE_SIZE_MB", "value": "10", "slotSetting": false},
{"name": "FLUENT_MANAGER_FE_RATE_RPS", "value": "100", "slotSetting": false},
{"name": "FLUENT_MANAGER_FE_RATE_BURST", "value": "200", "slotSetting": false},
{"name": "FLUENT_MANAGER_MAX_UPLOADED_FILE_SIZE_MB", "value": "100", "slotSetting": false},
{"name": "FLUENT_MANAGER_REQUEST_TIMEOUT", "value": "60000", "slotSetting": false},
{"name": "WEBSITES_PORT", "value": "8080", "slotSetting": false},
{"name": "WEBSITES_CONTAINER_START_TIME_LIMIT", "value": "900", "slotSetting": false},
{"name": "WEBSITES_ENABLE_APP_SERVICE_STORAGE", "value": "false", "slotSetting": false}
]
info

In the JSON above, the entry at the top is the value you must edit for your environment. The remaining entries are preset defaults.

  1. Replace <your-backend-app-name>.azurewebsites.net with the backend App Service's Default domain (found on the backend's Overview page). Use the full hostname without https://.
  2. Click Apply, then confirm
info

The frontend container runs nginx which proxies /api/ requests to the backend. BACKEND_HOST tells nginx where to find the backend, and BACKEND_SCHEME specifies whether to use http or https. Azure App Service forces HTTPS, so BACKEND_SCHEME must be set to https — otherwise nginx will proxy over HTTP, causing a 301 redirect loop. In Docker Compose, BACKEND_SCHEME is set to http because containers communicate over plain HTTP internally.

Start the frontend:

  1. Click Start in the top toolbar
  2. Wait 1–2 minutes for the container to start

Step 6 — Deploy the RESTful Engine App Service

For instructions on deploying the RESTful Engine as an Azure App Service, see Deploying the RESTful Engine.

When configuring the engine, note that it does not require VNet integration — the backend communicates with the engine over HTTPS using the engine's public .azurewebsites.net URL, which is configured during workspace setup in Step 7.

info

We recommend using the same major and minor versions for the Manager and RESTful Engine. For example, if your Manager is version 26.1.x, use a RESTful Engine version 26.1.x.x.

Step 7 — Access and Configure Fluent Manager

Open the frontend in your browser using the Default domain from the frontend App Service's Overview page:

https://<frontend-app-name>.azurewebsites.net

Log in with the admin credentials you configured in Step 4 (FLUENT_MANAGER_DEFAULT_ADMIN_EMAIL / FLUENT_MANAGER_DEFAULT_ADMIN_PASSWORD). On first login, Fluent Manager will walk you through workspace initialization:

  1. Workspace setup: Choose a name, alias, and timezone for your workspace. Optionally select "Fill the workspace with sample templates" to explore the application.
  2. Licensing: Enter your Fluent license key. If you do not have one, you can request a trial license.
  3. RESTful Engine URL: When prompted, enter the engine App Service's Default domain:
https://<engine-app-name>.azurewebsites.net
tip

Azure App Service provides HTTPS by default via *.azurewebsites.net certificates. You can add custom domains later through the App Service Custom domains settings.

Upgrading

To update the Manager to a new version:

danger

Always backup your database before upgrading. In the Azure Portal, open your PostgreSQL server and go to Overview > Restore to create a point-in-time backup.

  1. Open the backend App Service
  2. In the left menu, go to Deployment Center
  3. Update the Image and tag to the new version (e.g., apryse/fluent-manager-backend:x.y.z)
  4. Click Save
  5. Repeat for the frontend App Service

Database migrations are handled automatically by the Fluent Manager backend application using Liquibase. No manual migration steps are required.

To update the RESTful Engine, repeat the same process for the engine App Service with the new engine image tag.

Troubleshooting

Backend container fails to start — no logs visible

If the backend shows "Issues Detected" but no logs appear in the Log stream or Deployment Center:

  1. Check Networking > VNet integration and verify that Outbound internet traffic is unchecked. If checked, all outbound traffic (including container image pulls) is routed through the VNet, which has no internet access. This is the most common cause — the container image silently fails to pull with no error logged.
  2. Verify the container image tag is correct in Deployment Center — check that the registry server URL is public.ecr.aws and the image/tag are correct.
  3. Try a full Stop > wait 30 seconds > Start (not just Restart).
  4. If the site shows "blocked due to multiple, consecutive cold start failures", Stop the App Service, wait for it to show "Stopped", then Start it again.
  5. Note that the Outbound internet traffic setting can reset when the App Service Plan is changed or the App Service is recreated. Always re-verify after making infrastructure changes.
Backend crash: missing environment variable placeholder

If the backend logs show errors like Could not resolve placeholder 'FLUENT_MANAGER_MAILING_ENABLE' or similar, one or more required environment variables are missing. The backend requires all environment variables from the JSON in Step 4 — it does not have built-in defaults for most settings.

Verify all environment variables are present in Environment variables > Advanced edit.

Backend crash: ERROR schema "manager" does not exist

The backend requires a manager schema in the fluent database. If the schema was not created during the prerequisites, connect to the database and run:

CREATE SCHEMA IF NOT EXISTS manager;

See Setting Up Your Database for details.

Backend crash: UnknownHostException for the database hostname

If the backend logs show java.net.UnknownHostException: <your-server>.postgres.database.azure.com, the Private DNS zone does not have an A record for the database server. This can happen if you delete and recreate the PostgreSQL server — the new server may not automatically register with the existing Private DNS zone.

  1. Go to the Private DNS zone for your PostgreSQL server
  2. Check if there is an A record for your database server name
  3. If the record is missing, ensure the PostgreSQL server was created with the Private DNS zone selected on the Networking tab. You may need to delete and recreate the server, selecting the existing Private DNS zone during creation.
Backend cannot connect to database
  1. Open the backend App Service > Networking > VNet integration and confirm it is connected to the App Service subnet
  2. Open the PostgreSQL server > Networking and confirm it is using Private access (VNet Integration) with the database subnet
  3. Verify both subnets are in the same VNet and the VNet is in the same region
  4. Check the Private DNS zone has an A record for the database server
  5. Check that the FLUENT_MANAGER_DATABASE_URL value matches the PostgreSQL server's Endpoint from the Overview page, with :5432 appended
Frontend crash: host not found in upstream "backend"

The frontend nginx container requires the BACKEND_HOST environment variable to know where to proxy API requests. Verify BACKEND_HOST is set to the backend's Default domain (without https://) and all rate limit and timeout variables are present.

Frontend: 301 redirect loop or API requests fail

If the frontend loads but API calls fail with 301 redirects or mixed content errors, check that BACKEND_SCHEME is set to https. Azure App Service forces HTTPS on all incoming requests — if the frontend proxies to the backend over http://, the backend responds with a 301 redirect to HTTPS, which nginx cannot follow, creating an infinite loop.

Container image pull fails from ECR
  1. Verify the image exists by running locally: docker pull public.ecr.aws/apryse/fluent-manager-backend:x.y.z
  2. If the App Service has VNet integration with Outbound internet traffic checked, uncheck it.
  3. If your organization restricts outbound internet access, mirror the images to an Azure Container Registry (ACR).
App Service plan quota error

If you receive "Operation cannot be completed without additional quota", your subscription does not have enough vCPU quota for the selected SKU. Request a quota increase, try a different SKU, or use a Basic B3 plan for testing.

Resource group deletion blocked by a lock

If you cannot delete the resource group due to an AzureBackupProtectionLock, go to the resource group's Settings > Locks and delete the lock. This requires Owner permissions on the subscription. The lock is typically auto-created by Azure Backup on storage accounts.