Self-hosted iOS & Android analytics toolkit. A private, unified alternative to App Store Connect and Google Play Console β keep 100% of your data and credentials on your own server.
Stop context-switching between App Store Connect and Google Play Console. AppRankly unifies your iOS and Android app performance into a single, sleek, self-hosted dashboard. Monitor installs, uninstalls, active devices, retention cohorts, user survival curves, and release markers side-by-side.
Features an AI-Assisted ASO Studio for zero-cost store autocomplete keyword discovery, keyword ranking checks, competitor comparisons, and metadata listing audits using your choice of OpenAI, Anthropic, or Gemini β with 100% of your analytics and API keys remaining securely on your server.
βΆ Try the Live Interactive Demo β no installation required, pre-loaded with sample data.
- Screenshots
- Quick Start
- Key Features
- Authentication & Credentials Setup
- Configuration Reference
- Headless CLI Utility
- Architecture & Tech Stack
- Troubleshooting & FAQ
- Contributing & Support
**π· Click to view individual full-resolution screenshots**
Installs, uninstalls, active devices, and country breakdowns across Google Play & Apple App Store in one glassmorphic interface.

Per-app drill-down: version performance, daily trends, retention, and country-level distribution.

Cohort retention heatmaps, active retention proxies, survival curves, stickiness index, and churn risk intelligence.

Mine store autocomplete for zero-cost keyword discovery, check keyword ranks, audit listing health, and generate metadata variants with your choice of AI provider (OpenAI, Anthropic, or Gemini).

Export overview stats, daily trends, dimension breakdowns, or full raw data archive bundles for offline analysis.

Form editor, raw JSON manager, test connection tools, and built-in setup guide for credentials and data sources.

Authoritative mathematical formulas, interpretation guides, platform origins, and data lag disclosures used to compute metrics.

Pick your preferred deployment method. Note where config.json lives for each ecosystem:
| Deployment Method | Directory for config.json |
Default Dashboard URL |
|---|---|---|
| Docker Compose | ./config/ |
http://localhost:3000 |
Pre-built Image (docker run) |
./data/config/ |
http://localhost:3000 |
| Unraid | /mnt/user/appdata/AppRankly/config/ |
http://[SERVER-IP]:3020 |
| Docker Compose (Source Build) | ./config/ |
http://localhost:3000 |
| Local Node.js Development | ./data/config/ |
http://localhost:3000 |
Important
Prerequisite: AppRankly fetches data directly from official store APIs (Google Cloud Storage reports and Apple App Store Connect API) to ensure complete privacy. Before launching, configure your access keys following the Credentials Setup guide.
# 1. Clone the repository
git clone https://github.com/zmsp/AppRankly.git
cd AppRankly
# 2. Setup config files (mounts ./config into the container)
mkdir -p config/keys
cp example.config.json config/config.json
# Edit config/config.json and place API key files (.json for Google, .p8 for Apple) in config/keys/
# 3. Launch the container stack
docker compose up -dAccess the dashboard at http://localhost:3000.
# 1. Pull the container
docker pull ghcr.io/zmsp/apprankly:main
# 2. Prepare data directory mappings
mkdir -p data/config/keys
cp example.config.json data/config/config.json
# Edit data/config/config.json and place API keys in data/config/keys/
# 3. Spin up the container
docker run -d \
--name AppRankly \
-p 3000:3000 \
-v $(pwd)/data:/app/data \
-e JWT_SECRET="your-secure-random-secret" \
ghcr.io/zmsp/apprankly:mainAppRankly is available natively via Unraid Community Applications.
- Search for AppRankly in your Unraid Apps tab and click Install.
- Alternative Manual Setup: Download the XML template directly:
curl -o /boot/config/plugins/dockerMan/templates-user/apprankly.xml https://raw.githubusercontent.com/zmsp/AppRankly/main/unraid/apprankly.xml- Set appropriate permissions for the mapped directory:
chown -R 1000:1000 /mnt/user/appdata/AppRankly/ - Access via
http://[YOUR-SERVER-IP]:3020.
(See the detailed unraid/README.md for deeper customization options).
Requires Node.js 20+ (Node 22.5+ highly recommended β the SQLite caching layer relies on native node:sqlite features and safely degrades on older versions).
git clone https://github.com/zmsp/AppRankly.git
cd AppRankly
# Setup local workspace configuration
mkdir -p data/config/keys
cp example.config.json data/config/config.json
# Install workspace & UI dependencies, then launch development hot-reload
cd app && npm install
npm --prefix frontend install
npm run dev- π― Unified Cross-Platform Core: Access installs, uninstalls, active devices, software upgrades, and country/device breakdowns across Google and Apple under one cohesive UI.
- π€ AI-Powered ASO Studio: Autocomplete keyword mining, keyword rank checks, competitor parsing, metadata health checks, and review sentiment synthesis. Use OpenAI, Anthropic, or Gemini natively.
- β‘ Zero-Dependency SQLite Cache: Store historical metrics, aggregates, and expensive AI results safely inside
node:sqliteso you never pay double API bills or redundant fetch times. - π Contextual Notebooks: Save persistent notes per application to mark feature additions or marketing campaigns directly inline with data trends.
- π Background Scheduler & Push Notifications: Automated cron sync cycles that broadcast performance thresholds to your phone using ntfy.sh (free, privacy-preserving, and optional).
- π Release Tracking: Tag specific application releases or let the app auto-detect updates to trace impact visually across data timelines.
- π Headless CLI Companion: Pull data payloads, migrate states, or perform manual historical database backfills over custom chron ranges using a scriptable terminal tool.
Google Play exports structured performance metrics into a private Google Cloud Storage bucket daily. AppRankly authenticates against this bucket securely.
- Generate a GCP Service Account Key:
- Visit the Google Cloud IAM Console and create a service account (e.g.,
playstore-stats-reader). - Navigate to Keys β Add Key β Create New Key (JSON). Save this file to your mapped volume as
keys/google_key.json.
- Assign Play Console Access:
- Navigate to the Google Play Console β Users and Permissions and invite the newly created service account email.
- Assign the "View app information and download bulk reports (read-only)" permissions.
- Note: Permissions across GCS can sometimes take up to 24 hours to replicate completely.
- Capture Your Bucket Endpoint:
- In the Play Console, head to Download reports β Statistics and select Copy Cloud Storage URI (e.g.,
gs://pubsite_prod_12345678/stats/installs/). - Your target bucket name is the unique string value located between
gs://and the subsequent forward slash.
- Navigate to App Store Connect β Users and Access β Integrations (Keys).
- Generate an API Key with the Sales and Reports access role.
- Download the provided
.p8credential token into your designatedkeys/directory mapping. - Record the Issuer ID listed at the top of the interface and the specific 10-character Key ID.
AppRankly can sync your application notes and marketing records to a private GitHub repository, ensuring your annotations are versioned and backed up.
- Create a Repository: Create a private repository on GitHub (e.g.,
my-app-notes). - Generate a Personal Access Token:
- Visit GitHub Token Settings.
- Click Generate new token (classic).
- Scope: Select 'repo' (Full control of private repositories).
- Copy the generated token (this will be used as your
passwordin the config).
- Configure in AppRankly: Add the
gitNotesblock to yourconfig.jsonas shown in the Configuration Reference.
Duplicate example.config.json inside your designated configuration target directory and adjust the payload schema values:
{
"name": "Production Apps",
"projectID": "your-gcp-project-id",
"bucketName": "pubsite_prod_12345678",
"keyFilePath": "keys/google_key.json",
"appleIssuerId": "xxxx-xxxx-xxxx-xxxx",
"appleKeyId": "XXXXXXXXXX",
"appleVendorId": "85000000",
"keyFilePath_apple": "keys/apple_key.p8",
"PlaystoreConsoleUrl": "https://play.google.com/console/u/0/developers/123456",
"ntfyTopic": "",
"refreshIntervalHours": 1,
"statsCheckRangeDays": 30,
"activeStartHour": 9,
"activeEndHour": 20,
"appMetadata": {
"com.example.app": { "consoleAppId": "123456" }
},
"ignoredPackages": ["com.example.testapp"],
"ai": {
"defaultProvider": "openai",
"providers": {
"openai": { "apiKey": "sk-...", "model": "gpt-4.5-preview" },
"anthropic": { "apiKey": "sk-ant-...", "model": "claude-3-5-sonnet" },
"gemini": { "apiKey": "...", "model": "gemini-1.5-flash" }
}
},
"gitNotes": {
"remoteUrl": "https://github.com/youruser/your-notes-repo.git",
"username": "your-github-username",
"password": "your-personal-access-token",
"branch": "main"
}
}| Field | Context Ecosystem | Purpose and Definition | Mandate |
|---|---|---|---|
name |
Core Environment | Human-readable identity tag for the accounts dashboard. | Required |
projectID |
Google Play | The target identifier matching your Google Cloud Console project. | Optional |
bucketName |
Google Play | Extracted Cloud Storage ID value containing metric exports. | Optional |
keyFilePath |
Google Play | Relative directory reference parsing your Google IAM .json token file. |
Optional |
appleIssuerId |
App Store Connect | Universal API UUID extracted from your Connect keys management area. | Optional |
appleKeyId |
App Store Connect | Explicit 10-character reference code matching your downloaded key token. | Optional |
keyFilePath_apple |
App Store Connect | Relative directory reference parsing the generated .p8 access key. |
Optional |
ntfyTopic |
Alert Framework | Unique secret path defining your subscription string. Leave empty "" to turn off. |
Optional |
refreshIntervalHours |
Worker Lifecycle | Defines the lookup intervals checking for fresh metrics upstream. | Default: 1 |
statsCheckRangeDays |
Data Aggregation | Window depth processing past timeline metrics during cron sync updates. | Default: 30 |
gitNotes |
Version Control | Configuration for syncing local notes to a remote Git repository (GitHub/GitLab). | Optional |
ai |
Optimization Lab | Object mappings storing keys and customized models across AI vendors. | Optional |
Run maintenance operations, sync metrics, or handle migrations cleanly via the integrated command interface:
# Sync + verify configuration against index position zero
node app/cli.js -s 0
# Target historical sync tasks by custom environment name
node app/cli.js -s "Production Apps"
# Fully decoupled ingestion parameters bypassing file configurations
node app/cli.js \
--key="data/config/keys/google_key.json" \
--projectID="your-gcp-project-id" \
--bucketName="pubsite_prod_12345678" \
--packageName="com.example.app"Execute these utilities directly from inside the localized /app path workspace:
npm run db:migrate # Force target and process outstanding schema changes
npm run db:backfill # Pull file archives historically (e.g., cli.js backfill --since YYYY-MM)
npm run db:status # Output active table indices, row weight, and localized scope coverage
npm run cache:clear # Drop cached timeline states to force total downstream cache regeneration- Backend Application Core: Node.js 20+ alongside Express. Secured with JWT tokens. Employs zero-native dependency performance pipelines like
node:sqlite, raw UTF-16 streaming buffers using Fast-CSV, and native cryptographic integrations signing asymmetric ES256 tokens for Apple integrations. - User Interface Engineering: React 18 powered alongside Vite. Implements fully dynamic visual charting using
react-chartjs-2running over an adaptive Tailwind CSS engine optimized for fluid dark environments. - Scraping, Optimization & LLM Adapters: Integrates multi-channel store hooks parsing active listings data concurrently while standardizing multi-tenant inference models cleanly across the native SDK layers of OpenAI, Anthropic, and Google Gemini.
graph TD
User([Browser]) --> SPA["React SPA"]
SPA <-->|"JWT API"| API["Express Server"]
subgraph Backend["Node.js Backend Engine"]
direction TB
API --> Resolver["Data Resolver"]
API --> ASO["ASO Studio"]
API --> Notes["Notes Module"]
DB[("SQLite Cache")] <--> Resolver
DB <--> Notes
Scheduler["Cron Sync"] --> Resolver
CLI["CLI Utility"] --> Resolver
end
%% Grouping External Integrations Vertically
subgraph Integrations["Subsystems & Integrations"]
direction TB
subgraph Metrics["Metric Sources"]
GCS[("Google Play GCS")] <--> Resolver
ASC[("App Store API")] <--> Resolver
end
subgraph Intelligence["AI & Intelligence"]
Scrape["Store Scrapers"] <--> ASO
AI{{"AI: LLM Models"}} <--> ASO
end
subgraph Persistence["Notes & Sync"]
Git[("GitHub Remote")] <--> Notes
FS[("Markdown Files")] <--> Notes
end
end
SPA -.->|"static"| Pages[("GitHub Pages")]
We love contributions! Review our detailed CONTRIBUTING.md to learn how to prepare local tracking environments, map diagnostic test workflows, and open up performance patches inside our pipelines cleanly.
For security concerns, vulnerabilities, and disclosures, please see our Security Policy.
Discover additional utilities and self-hosted tools on my homepage at apps.shahadat.us or support ongoing open-source maintenance here:
AppRankly is distributed as open-source software protected under the terms of the GNU Affero General Public License v3.0 (AGPL-3.0).

