Countly Community Edition on Ubuntu 24.04 on Azure User Guide
Overview
Countly Community Edition is an AGPL-3.0, self-hosted product, mobile and web analytics and engagement platform - a privacy-conscious alternative to sending your product data to a third-party service. You create one or more applications, each with its own app key, then send sessions, custom events, view durations, crashes and user properties from your apps and websites using Countly's SDKs or a simple HTTP call. Every metric, from active users and retention to the exact in-product actions you choose to measure, is aggregated and explored through a rich web dashboard, and every session and event stays on infrastructure you control.
The cloudimg image installs Countly Community Edition 25.03.49 (the free open-source Community build - no Enterprise license) to /opt/countly, runs the ingest/read API and the dashboard as Node.js 20 processes managed by countly.service, backs them with a local MongoDB 8.0 database, fronts everything with nginx on port 80, and generates a unique admin account on first boot. Backed by 24/7 cloudimg support.
What is included:
- Countly Community Edition 25.03.49 (AGPL-3.0 open-source build) - the
countly-apiprocess serving the/iingest and/oread APIs on127.0.0.1:3001, and thecountly-dashboardprocess serving the web UI on127.0.0.1:6001, both managed bycountly.service - A local MongoDB 8.0 database (standalone, wiredTiger, cache capped for a 4 GiB VM) bound to loopback
127.0.0.1:27017only - never exposed to the network - nginx reverse proxy on port
80, routing/iand/oto the API and/to the dashboard, and serving an unauthenticatedGET /healthfor load-balancer and uptime checks - A unique per-VM administrator account (
admin) created on first boot, with its password and API key written to a root-only credentials file - the image ships with no account and no default credential - Per-VM session and password secrets generated fresh on first boot - nothing is baked into the image
- The public dashboard is opened only after the per-VM admin has been created, so no unconfigured setup wizard is ever exposed
Countly's API and dashboard bind loopback only; customers reach them through the nginx reverse proxy on port 80. MongoDB is never exposed - it listens on 127.0.0.1:27017 only.
Prerequisites
An active Azure subscription, an SSH key pair, and a VNet + subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is the recommended size and is what this image is tuned for; step up to a larger size for heavy ingest volumes. NSG inbound: allow 22/tcp from your management network and 80/tcp from the clients and apps that will send analytics and reach the dashboard. For production, front the VM with TLS (see the final step) and allow 443/tcp instead of plain 80.
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Countly Community Edition by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size (Standard_B2s); under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22) and HTTP (80). Then Review + create -> Create.
Step 2 - Deploy from the Azure CLI
az vm create \
--resource-group <your-rg> \
--name countly \
--image <marketplace-image-urn> \
--size Standard_B2s \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_ed25519.pub \
--vnet-name <your-vnet> --subnet <your-subnet> \
--public-ip-sku Standard
az vm open-port --resource-group <your-rg> --name countly --port 80 --priority 900
Step 3 - Connect to your VM
ssh azureuser@<vm-public-ip>
Step 4 - Confirm MongoDB, Countly and nginx are running
The first boot brings up MongoDB, starts the Countly API and dashboard, generates the per-VM admin, and then starts nginx. Confirm all three services are active and that the API health endpoint answers:
systemctl is-active mongod countly nginx
curl -s http://127.0.0.1/health
curl -s http://127.0.0.1/o/ping
You should see active three times, ok from the nginx health endpoint, and {"result":"Success"} from the Countly API ping.

Step 5 - Retrieve the per-VM admin credentials
The administrator account, its password and its API key are generated uniquely on first boot and written to a root-only file:
sudo cat /root/countly-credentials.txt
This prints the dashboard URL, the admin username (admin), the generated admin password and the admin API key. Keep this file safe - the password is not stored anywhere else.

Step 6 - Sign in to the Countly dashboard
Open http://<vm-public-ip>/ in a browser. Sign in with username admin and the password from /root/countly-credentials.txt (shown below as <COUNTLY_ADMIN_PASSWORD>). You land directly in the Countly dashboard - the initial setup has already been completed for you on first boot.


Step 7 - Create an application
Each thing you want to measure (a website, a mobile app) is a Countly "application" with its own app key. You can create one from the dashboard (Management -> Applications -> Add new app), or from the API using your admin API key. This command reads the admin API key straight from the credentials file and creates an application called "My Website":
API_KEY=$(sudo grep '^COUNTLY_ADMIN_API_KEY=' /root/countly-credentials.txt | cut -d= -f2-)
curl -s "http://127.0.0.1/i/apps/create?api_key=${API_KEY}" \
--data-urlencode 'args={"name":"My Website","type":"web","category":"6","timezone":"UTC"}' \
| jq '{app_id: ._id, name, app_key: .key}'
The response shows the new application's app_id (_id) and app_key (key). The app key is what your website or app uses to send data.

Step 8 - Send analytics to the ingest API
Your apps and sites send data to the /i write endpoint with the app key. In production you would use one of Countly's SDKs; here is the raw HTTP form. This block looks up the app key for "My Website", starts a session and records a custom purchase event:
API_KEY=$(sudo grep '^COUNTLY_ADMIN_API_KEY=' /root/countly-credentials.txt | cut -d= -f2-)
APP_KEY=$(curl -s "http://127.0.0.1/o/apps/mine?api_key=${API_KEY}" \
| jq -r '.admin_of | to_entries[] | select(.value.name=="My Website") | .value.key')
curl -s "http://127.0.0.1/i" \
--data-urlencode "app_key=${APP_KEY}" \
--data-urlencode "device_id=demo-device-1" \
--data-urlencode "begin_session=1" \
--data-urlencode 'metrics={"_os":"Linux","_app_version":"1.0"}'
curl -s "http://127.0.0.1/i" \
--data-urlencode "app_key=${APP_KEY}" \
--data-urlencode "device_id=demo-device-1" \
--data-urlencode 'events=[{"key":"purchase","count":1,"sum":42}]'
Each call returns {"result":"Success"}.
Step 9 - Read the analytics back
The /o read API returns aggregated analytics for an app using your admin API key. This block resolves the app ID for "My Website", lists the events it has registered (the purchase event you just sent appears in the list), and reads the session totals:
API_KEY=$(sudo grep '^COUNTLY_ADMIN_API_KEY=' /root/countly-credentials.txt | cut -d= -f2-)
APP_ID=$(curl -s "http://127.0.0.1/o/apps/mine?api_key=${API_KEY}" \
| jq -r '.admin_of | to_entries[] | select(.value.name=="My Website") | .key')
echo "Registered events:"
curl -s "http://127.0.0.1/o?api_key=${API_KEY}&app_id=${APP_ID}&method=get_events" | jq '.list'
echo "Session totals (this month):"
curl -s "http://127.0.0.1/o?api_key=${API_KEY}&app_id=${APP_ID}&method=sessions&period=month" | jq -c '.[keys[0]]'
The registered events include purchase, and the session totals reflect the sessions you started. In the dashboard, the same data appears under the app's Analytics views.


Step 10 - Put TLS in front for production
Countly serves plain HTTP on port 80. Before exposing it to production traffic, front it with TLS using your own domain. Point a DNS A record at the VM's public IP, allow 443/tcp in the NSG, then install a certificate with certbot:
sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d countly.example.com
certbot obtains a certificate, rewrites the nginx site to serve HTTPS on 443, and sets up automatic renewal. After enabling TLS, restrict the NSG so only 443/tcp (and 22/tcp from your management network) is reachable.
Verifying the stack
You can confirm the runtime versions and that all listeners are loopback-bound (except nginx on 80):
node -v
mongod --version | head -1
sudo ss -tlnp | grep -E ':80 |:3001 |:6001 |:27017 '

Security notes
- The image ships with no account and no default credential. On first boot a single administrator is created with a freshly generated password, and the session and password secrets are generated per-VM - nothing is baked into the image.
- MongoDB is bound to
127.0.0.1only and is never exposed to the network. - The Countly API (
3001) and dashboard (6001) bind loopback only; all external access goes through nginx on port80(or443once you enable TLS). - Keep
/root/countly-credentials.txtprotected - it is0600 root:rootand holds the only copy of the generated admin password. - The base OS has unattended security upgrades enabled so it keeps receiving patches.
Support
Every cloudimg image is backed by 24/7 support. If you hit any issue deploying or running Countly Community Edition on Azure, contact cloudimg support.