Installation
Using the provided Docker images, components can be easily deployed in a local environment or on any cloud platform, such as Microsoft Azure or Amazon AWS.
Database Setup
Mobile In-App Protection backend services use a shared PostgreSQL database instance as the primary data store and a Redis instance for data caching and streaming.
PostgreSQL Setup
The Docker images automatically keep the database schema up-to-date using Liquibase. Whenever you install the application or update it to a new version, the container updates the database schema.
Install PostgreSQL
Download and install the current version of PostgreSQL:
Start the database and connect to it using your preferred database tool.
Create User
Start by creating a user in the database and by setting the user a strong password.
Tip: You can generate a strong password locally on your computer using openssl rand -base64 12.
CREATE USER username;
ALTER USER username WITH PASSWORD '$PASSWORD$';
Create Database
Now, let’s create the iap database to which the data will be stored.
CREATE DATABASE iap;
GRANT ALL PRIVILEGES ON DATABASE iap TO username;
You can assign more granular privileges instead of using ALL PRIVILEGES, if required for security reasons.
Redis Setup
Download and install the current version of Redis:
and start the Redis Server.
By default the components connect to a single (standalone) Redis instance via IAP_<COMPONENT>_REDIS_HOST /
IAP_<COMPONENT>_REDIS_PORT, which is also the correct topology for managed Redis using the Enterprise cluster policy.
To run against a Redis OSS cluster instead, activate the redis-cluster Spring profile
(SPRING_PROFILES_ACTIVE=redis-cluster) and set IAP_<COMPONENT>_REDIS_CLUSTER_NODES. See
Configuration Properties for the full list of Redis variables,
authentication/TLS options, and cluster tuning.
Pull the Docker Images
To deploy the Docker image, you need to log in to our Artifactory repository and pull the Docker images:
docker login wultra.jfrog.io
docker pull wultra.jfrog.io/wultra-docker/iap-init:${VERSION}
docker pull wultra.jfrog.io/wultra-docker/iap-device-api:${VERSION}
docker pull wultra.jfrog.io/wultra-docker/iap-integration-api:${VERSION}
docker pull wultra.jfrog.io/wultra-docker/iap-console-api:${VERSION}
docker pull wultra.jfrog.io/wultra-docker/iap-console-web:${VERSION}
Database Initialization
The iap-init image is responsible for initializing the database schema using Liquibase. Before running this
container, set the following environment variables:
DATASOURCE_URL=jdbc:postgresql://host.docker.internal:5432/iap
DATASOURCE_USERNAME=$USERNAME$
DATASOURCE_PASSWORD=$PASSWORD$
Note that the iap-init container is intended for one-time execution only. It performs database initialization and then exits. It does not need to run continuously.
Device Model Mapping Data
When device information is received, IAP translates its technical model identifier into a human-readable model name;
for example, SM-S938Q becomes Galaxy S25 Ultra. Both the received identifier and the translated name are stored
with the device and then included in callbacks, event queue records, and Integration API device responses.
To perform this translation, IAP uses device mapping data included in every release and loaded by iap-init. No
additional configuration is required when the release-provided data is sufficient. Because new device models are
introduced frequently, particularly on Android, the mapping data can also be updated independently of an IAP release.
Updating Device Model Mapping Data
The latest device lists can be obtained from:
- Android: official list from Google Play, encoded as UTF-16LE with a byte order mark (BOM);
- Apple: maintained Apple device types list, encoded as UTF-8.
To update the mapping data, download both device lists and use the
device model mapping generator (requires Python3), to prepare the file
for iap-init:
python3 generate-device-info-mapping.py \
--android google-supported-devices.csv \
--apple apple-device-types.txt \
--output device_info_mapping.csv
Configure the iap-init container to mount the generated file read-only at the path /usr/local/liquibase/db/changelog/data/device_info_mapping.csv,
replacing the mapping data bundled with the image. Then run iap-init using the initialization mechanism of your deployment platform. For example,
when running the container directly with Docker:
docker run --rm \
-e DATASOURCE_URL="jdbc:postgresql://<db-host>:5432/iap" \
-e DATASOURCE_USERNAME="<username>" \
-e DATASOURCE_PASSWORD="<password>" \
-v ./device_info_mapping.csv:/usr/local/liquibase/db/changelog/data/device_info_mapping.csv:ro \
wultra.jfrog.io/wultra-docker/iap-init:${VERSION}
Updated mappings take effect immediately for new devices. Existing device information is not updated retroactively.
Keep the custom file mounted during every subsequent iap-init execution. Otherwise, the mapping data may be
replaced by the data bundled in the selected IAP release.
Running Backend Services
Once the database has been successfully initialized, start the backend services. Each component is configured
via environment variables passed to the container at startup. The backend components iap-device-api, iap-console-api
and iap-integration-api share a common base configuration. Each component uses its own environment variable prefix (e.g. IAP_DEVICEAPI_).
Use the Correct Database URL
Backend components use Reactive Relational Database Connectivity (R2DBC). Provide a connection string with the
r2dbc: prefix. When running locally, note that localhost inside a Docker container refers to the container
itself, not the host machine. Use host.docker.internal to reach services running on the host.
Device API
The Device API requires the following environment variables:
IAP_DEVICEAPI_R2DBC_URL=r2dbc:postgresql://host.docker.internal:5432/iap
IAP_DEVICEAPI_R2DBC_USERNAME=$USERNAME$
IAP_DEVICEAPI_R2DBC_PASSWORD=$PASSWORD$
IAP_DEVICEAPI_REDIS_HOST=host.docker.internal
IAP_DEVICEAPI_REDIS_PORT=6379
See Configuration Properties for the full list of available configuration properties for the component.
Integration API
The Integration API requires the following environment variables:
IAP_INTEGRATIONAPI_R2DBC_URL=r2dbc:postgresql://host.docker.internal:5432/iap
IAP_INTEGRATIONAPI_R2DBC_USERNAME=$USERNAME$
IAP_INTEGRATIONAPI_R2DBC_PASSWORD=$PASSWORD$
IAP_INTEGRATIONAPI_REDIS_HOST=host.docker.internal
IAP_INTEGRATIONAPI_REDIS_PORT=6379
IAP_INTEGRATIONAPI_CONSOLEURL=https://<public-url-of-console-web>
IAP_INTEGRATIONAPI_CONSOLEURL must point to the publicly accessible URL of the Console Web frontend.
It is used to build the password reset link included in emails sent to users.
The Integration API also handles email delivery for the password reset flow. Email delivery is enabled
by default and requires Gmail API credentials to be configured. Set IAP_INTEGRATIONAPI_EMAIL_ENABLED=false to disable it. See
Configuration Properties for the full list of email properties.
See Configuration Properties for the full list of available configuration properties for the component.
Console API
The Console API requires the following environment variables:
IAP_CONSOLEAPI_R2DBC_URL=r2dbc:postgresql://host.docker.internal:5432/iap
IAP_CONSOLEAPI_R2DBC_USERNAME=$USERNAME$
IAP_CONSOLEAPI_R2DBC_PASSWORD=$PASSWORD$
IAP_CONSOLEAPI_REDIS_HOST=host.docker.internal
IAP_CONSOLEAPI_REDIS_PORT=6379
IAP_CONSOLEAPI_CONSOLEURL=https://<public-url-of-console-web>
IAP_CONSOLEAPI_CSRFCOOKIEDOMAIN=<your-domain>
IAP_CONSOLEAPI_CONSOLEURL must be set to the publicly accessible URL of the Console Web frontend.
IAP_CONSOLEAPI_CSRFCOOKIEDOMAIN sets the Domain attribute of the CSRF cookie. The cookie is always created with the
Secure=true flag. Ensure that the CORS configuration is set accordingly to allow requests from the Console Web.
IAP_CONSOLEAPI_ARTIFACTORYURL is optional and defaults to https://wultra.jfrog.io. Override it if SDK artifacts are hosted on a
private Artifactory instance.
See Configuration Properties for the full list of available configuration properties for the component.
Finally, start the iap-device-api:${VERSION}, iap-console-api:${VERSION}, iap-integration-api:${VERSION} images.
Running Web Console
The iap-console-web container requires the following environment variable:
API_URL=https://<console-api-host>:<port>
API_URL must point to the running iap-console-api instance.
Start the iap-console-web container. Once all services are running, continue to the
Configuration guide.
RASP Reporting Setup
RASP reporting collects security events and flag changes from the database and ships them to Elasticsearch via Logstash.
The pipeline runs three parallel polling jobs: rasp-events, rasp-flags-set, and rasp-flags-cleared.
Download the JDBC Driver
The Logstash JDBC input plugin requires the PostgreSQL JDBC driver. It is not bundled in the Docker image and must be downloaded separately.
curl -O https://jdbc.postgresql.org/download/postgresql-42.7.5.jar
Place the downloaded JAR in the logstash/drivers/ directory of the repository.
Create a Read-Only Database User
Create a dedicated PostgreSQL user for Logstash with read-only access to the reporting tables.
Tip: You can generate a strong password locally on your computer using openssl rand -base64 12.
CREATE USER logstash WITH PASSWORD '$PASSWORD$';
GRANT CONNECT ON DATABASE iap TO logstash;
GRANT USAGE ON SCHEMA public TO logstash;
GRANT SELECT ON
iap_app_event,
iap_app_event_type,
iap_app_flag,
iap_app_flag_history,
iap_app_flag_type,
iap_app,
iap_app_type,
iap_app_device_detail
TO logstash;
Apply Elasticsearch Index Templates
Run the setup script once against your Elasticsearch instance to create the required ILM policy and index templates. The script is idempotent and safe to re-run after any template changes.
ES_URL=https://<elasticsearch-host> \
ES_USERNAME=<user> \
ES_PASSWORD=<password> \
./elasticsearch/setup.sh
The script creates:
| Resource | Type | Description |
|---|---|---|
iap-rasp-policy |
ILM policy | Data lifecycle: hot → warm (read-only) after 90 days → delete after 365 days |
iap-rasp-events |
Index template | Mappings for security events, pattern iap-rasp-events-* |
iap-rasp-flags |
Index template | Mappings for flag changes, pattern iap-rasp-flags-* |
For Azure (SaaS) deployments, use the Azure Elasticsearch endpoint URL and credentials. No other changes to the script are required.
Run Logstash
Run Logstash as a Docker container alongside the other services. Mount the pipeline configuration and JDBC driver from the repository, and provide connection details as environment variables.
Persistent Volume Required
The /usr/share/logstash/data directory must be backed by a named Docker volume. It stores the last processed row ID for
each pipeline. If this volume is lost, Logstash will reprocess all historical data from the beginning.
docker run -d \
--name iap-logstash \
--restart unless-stopped \
-v /path/to/logstash/logstash.yml:/usr/share/logstash/config/logstash.yml:ro \
-v /path/to/logstash/pipelines.yml:/usr/share/logstash/config/pipelines.yml:ro \
-v /path/to/logstash/pipeline:/usr/share/logstash/pipeline:ro \
-v /path/to/logstash/drivers:/usr/share/logstash/drivers:ro \
-v logstash-data:/usr/share/logstash/data \
-e JDBC_URL="jdbc:postgresql://<db-host>:5432/iap" \
-e JDBC_USERNAME="logstash" \
-e JDBC_PASSWORD="<password>" \
-e JDBC_DRIVER_PATH="/usr/share/logstash/drivers/postgresql.jar" \
-e ES_URL="https://<elasticsearch-host>" \
-e ES_USERNAME="<user>" \
-e ES_PASSWORD="<password>" \
docker.elastic.co/logstash/logstash:9.5.6
For Azure (SaaS) deployments, replace the Elasticsearch connection variables with the Azure Elasticsearch endpoint and credentials. No other changes are required.
See Configuration Properties for the full list of available configuration properties.
Verify the Setup
# Check that all three pipelines started without errors
docker logs iap-logstash | grep -E "(ERROR|Pipeline started)"
# Verify that reporting indices exist in Elasticsearch
curl -u <user>:<password> "https://<elasticsearch-host>/_cat/indices/iap-rasp-*?v"
After the first polling interval, you should see iap-rasp-events-YYYY.MM and iap-rasp-flags-YYYY.MM indices with
a growing document count.
Resilience and Data Safety
The pipeline is designed to be resilient to transient failures:
Persistent queue — Logstash is configured with queue.type: persisted. Events are written to disk before the
pipeline processes them. If Logstash restarts while Elasticsearch is temporarily unavailable, no data is lost —
events are replayed from the persistent queue on the next startup.
Idempotent writes — each event is written to Elasticsearch using a stable document_id derived from the
PostgreSQL row ID. Reprocessing the same rows (e.g. after a metadata reset) produces no duplicates in Elasticsearch.
Source data is never consumed — unlike a message queue, the source data in PostgreSQL is not deleted by Logstash. If the metadata file is lost or reset, Logstash replays from the beginning. The idempotent writes ensure Elasticsearch remains consistent.
Recovering from a gap — if the metadata file is corrupted or points too far ahead, reset it manually:
docker exec iap-logstash sh -c 'echo "--- 0" > /usr/share/logstash/data/rasp-events.metadata'
docker exec iap-logstash sh -c 'echo "--- 0" > /usr/share/logstash/data/rasp-flags-set.metadata'
docker exec iap-logstash sh -c 'echo "--- 0" > /usr/share/logstash/data/rasp-flags-cleared.metadata'
Restart Logstash after the reset. The full history will be reprocessed and written to Elasticsearch idempotently.
For guidance on operating Logstash during extended outages, multi-instance HA deployment options, and step-by-step operational runbooks, see Logstash HA & Runbooks.