Countly
Overview
Countly Community Edition is a self-hosted product, mobile and web analytics and engagement platform, released under AGPL-3.0. It is 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, and send sessions, custom events, view durations, crashes and user properties using Countly's SDKs or a simple HTTP call. Every metric is aggregated and explored through a web dashboard, and every session and event stays on infrastructure you control.
This SecureAnyCloud image installs Countly Community Edition 25.03.49, the free open-source Community build with no Enterprise license, to /opt/countly. It runs the ingest and read API and the dashboard as Node.js 20 processes managed by countly.service, backed by a local MongoDB 8.0 database and fronted by nginx on port 80. It creates a unique admin account on first boot.
What you get:
- Countly Community Edition 25.03.49 (AGPL-3.0 open-source build). The
countly-apiprocess serves the/iingest and/oread APIs on127.0.0.1:3001, and thecountly-dashboardprocess serves the web UI on127.0.0.1:6001. Both are 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 and never exposed to the network - An nginx reverse proxy on port
80that routes/iand/oto the API and/to the dashboard. It also serves 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 on first boot, so nothing is baked into the image
- A public dashboard that opens only after the per-VM admin has been created, so no unconfigured setup wizard is ever exposed
- 24/7 SecureAnyCloud support
The Countly API and dashboard bind to loopback only, and you reach them through the nginx reverse proxy on port 80. MongoDB listens on 127.0.0.1:27017 only.
Prerequisites
You need an active Azure subscription, an SSH key pair, and a VNet with a subnet in the target region.
Standard_B2s (2 vCPU / 4 GiB RAM) is the recommended size, and this image is tuned for it. Choose a larger size for heavy ingest volumes.
In the network security group (NSG), allow inbound 22/tcp from your management network. Allow 80/tcp from the clients and apps that 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 SecureAnyCloud, and select Create.
On the Basics tab, 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 select Review + create and Create.
Step 2 - Deploy from the Azure CLI
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:countly-ubuntu-24-04:default:latest
az vm create \
--resource-group <your-rg> \
--name countly \
--image sac1647283583153:countly-ubuntu-24-04:default:latest \
--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 900Step 3 - Connect to your VM
ssh azureuser@<vm-public-ip>Step 4 - Confirm MongoDB, Countly and nginx are running
First boot brings up MongoDB, starts the Countly API and dashboard, generates the per-VM admin, and then starts nginx. Confirm that 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/pingThe output shows 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
First boot generates a unique administrator account, password and API key for your VM and writes them to a root-only file. Display it with:
sudo cat /root/countly-credentials.txtThe file lists the dashboard URL, the admin username (admin), the generated admin password and the admin API key. Keep this file safe, because 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. First boot has already completed the initial setup.
Step 7 - Create an application
Each thing you want to measure, such as a website or a mobile app, is a Countly "application" with its own app key. Create one from the dashboard (Management -> Applications -> Add new app) or from the API with your admin API key. This command reads the admin API key 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). Your website or app uses the app key 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, use one of Countly's SDKs. The raw HTTP form below shows the mechanics. It 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, authenticated with your admin API key. This block resolves the app ID for "My Website" and lists the events it has registered, including the purchase event you just sent. It then 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 you expose it to production traffic, front it with TLS using your own domain. Point a DNS A record at the VM's public IP and 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.comcertbot obtains a certificate, rewrites the nginx site to serve HTTPS on 443, and sets up automatic renewal. After you enable TLS, restrict the NSG so that only 443/tcp (and 22/tcp from your management network) is reachable.
Verifying the stack
Confirm the runtime versions and that all listeners are bound to loopback, except nginx on 80:
node -v mongod --version | head -1 sudo ss -tlnp | grep -E ':80 |:3001 |:6001 |:27017 'Security notes
- No default credential. The image ships with no account. 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 stays private. It is bound to
127.0.0.1only and is never exposed to the network. - Loopback-only services. The Countly API (
3001) and dashboard (6001) bind to loopback only. All external access goes through nginx on port80, or443once you enable TLS. - Protect the credentials file.
/root/countly-credentials.txtis0600 root:rootand holds the only copy of the generated admin password. - Automatic patches. The base OS has unattended security upgrades enabled.
Support
Every SecureAnyCloud image comes with 24/7 support. If you have a problem deploying or running Countly Community Edition on Azure, contact SecureAnyCloud support.
Deploy on Azure
Launch Countly Community Edition on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com
Overview
Countly Lite is the free Community Edition of Countly, an open source, self-hosted product, mobile and web analytics platform. It captures sessions, custom events, funnels, retention and crash data from your apps and websites, and presents them through a web dashboard and a simple HTTP API. Everything runs on infrastructure you control, so your product data never leaves your own account.
This SecureAnyCloud image runs the full stack on one instance. What you get:
- nginx on port 80, which proxies the ingest and read APIs to the Countly api process and the dashboard to the Countly dashboard process
- A local MongoDB 8.0 database that stores all data, bound to the loopback interface only and never accepting a connection from the network
- No account and no default credential of any kind. On the first boot of every instance, a unique administrator password, session secret and password secret are generated, and a single administrator is created from them
- The credentials written to a root-only file
- A public dashboard that opens only after that per-instance administrator exists, so the first visitor never lands on an unconfigured setup wizard
- 24/7 SecureAnyCloud support
Architecture
Component Detail
Reverse proxy nginx on port 80, the only public interface, with an unauthenticated /health endpoint
Ingest and read API Countly api process on 127.0.0.1:3001, serving the /i write and /o read APIs
Dashboard Countly dashboard process on 127.0.0.1:6001, serving the web UI
Database MongoDB 8.0 standalone on 127.0.0.1:27017, on a dedicated EBS data volume
Runtime Node.js 20, managed by supervisor under systemd
The api process, the dashboard process and MongoDB all bind to the loopback interface, so only ports 22, 80 and 443 need an inbound rule in your security group.
Connecting to your instance
Connect over SSH as the default login user for your operating system variant. This listing currently ships one variant:
OS variant SSH login user
Ubuntu 24.04 ubuntu
ssh -i /path/to/your-key.pem ubuntu@<instance-public-ip>Replace <instance-public-ip> with the public IPv4 address of your instance. Replace your-key.pem with the private key you selected at launch.
Confirming the services are running
The three services start automatically on boot. Confirm that they are active:
sudo systemctl is-active mongod.service countly.service nginx.serviceactive active activeChecking the health endpoints
nginx serves an unauthenticated static /health endpoint for load balancer probes and proxies the api health check at /o/ping. After first boot has seeded the per-instance administrator, the dashboard also answers /login:
curl -s -o /dev/null -w 'health: HTTP %{http_code}\n' http://127.0.0.1/health
curl -s -o /dev/null -w 'o/ping: HTTP %{http_code}\n' http://127.0.0.1/o/ping
curl -s -o /dev/null -w 'login: HTTP %{http_code}\n' http://127.0.0.1/loginhealth: HTTP 200 o/ping: HTTP 200 login: HTTP 200Retrieving your administrator credentials
First boot writes the per-instance administrator, its password, its API key and the dashboard URL to /root/countly-credentials.txt. View the non-secret fields:
sudo grep -E '^COUNTLY_(URL|ADMIN_USERNAME|ADMIN_EMAIL)=' /root/countly-credentials.txtCOUNTLY_URL=http://<instance-public-ip>/ COUNTLY_ADMIN_USERNAME=admin COUNTLY_ADMIN_EMAIL=admin@localhostWhen you are ready to sign in, print the administrator password with sudo grep '^COUNTLY_ADMIN_PASSWORD=' /root/countly-credentials.txt | cut -d= -f2-. Run it only when you are about to use the value, and avoid leaving it in your shell history or terminal scrollback.
The file also holds COUNTLY_ADMIN_API_KEY, the admin API key that authenticates the read and write APIs.
Signing in to the dashboard
Browse to http://<instance-public-ip>/ and sign in with the username admin and the password from the credentials file. The installation is already complete, so there is no setup wizard.
The Home dashboard opens on the application overview, with audience metrics, a sessions time series and quick links into each analytics area.
You can also verify the administrator login from the command line, without a browser. A successful sign-in returns HTTP 302 and redirects to /dashboard:
PW=$(sudo grep '^COUNTLY_ADMIN_PASSWORD=' /root/countly-credentials.txt | cut -d= -f2-)
JAR=$(mktemp)
CSRF=$(curl -s -c "$JAR" http://127.0.0.1/login | grep -oE 'name="_csrf"[^>]*value="[^"]+"|value="[^"]+"[^>]*name="_csrf"' | grep -oE 'value="[^"]+"' | head -1 | sed -E 's/value="([^"]+)"/\1/')
curl -s -b "$JAR" -c "$JAR" -o /dev/null -w 'sign in: HTTP %{http_code} redirect %{redirect_url}\n' \
--data-urlencode "username=admin" --data-urlencode "password=$PW" --data-urlencode "_csrf=$CSRF" http://127.0.0.1/login rm -f "$JAR"sign in: HTTP 302 redirect http://127.0.0.1/dashboardCreating an application and sending data
Countly organises analytics per application. Create your first application from the dashboard, or open Management > Applications in the administration area. Each application shows its App ID and App Key there.
Your SDK or HTTP client uses the App Key to send data. With the App Key, you can begin a session and send a custom event through the /i write API, for example:
# begin a session for a device curl "http://<instance-public-ip>/i?app_key=<app_key>&device_id=device-123&begin_session=1"
# send a custom event curl "http://<instance-public-ip>/i?app_key=<app_key>&device_id=device-123" \
--data-urlencode 'events=[{"key":"purchase","count":1,"sum":42}]'For a real integration, use one of the official Countly SDKs for web, iOS, Android or your server language. They handle sessions, events, views, crashes and user properties for you.
Exploring analytics
The Analytics section breaks your traffic down by sessions, visitors, page views, acquisition, technology and geography. The Session Analytics view shows total, new and unique sessions over time.
The Technology view breaks sessions down by platform, device, resolution, app version and browser.
The MongoDB data volume
The MongoDB database lives on a dedicated EBS volume mounted at /var/lib/mongodb. The volume is separate from the operating system disk and can be resized independently. It is mounted by filesystem UUID, so the layout persists on every instance launched from the image:
df -h /var/lib/mongodb | tail -1 grep mongodb /etc/fstab/dev/nvme1n1 30G 214M 28G 1% /var/lib/mongodb UUID=76ba1d12-f75c-43f9-96c9-d88a1f3e1447 /var/lib/mongodb ext4 defaults,nofail 0 2MongoDB, the api process and the dashboard process all listen on the loopback interface only, so none of them is reachable from the network. Confirm this with:
sudo ss -tlnp | grep -E ':27017|:3001|:6001' | sed -E 's/users:.*//'LISTEN 0 511 127.0.0.1:6001 0.0.0.0:* LISTEN 0 511 127.0.0.1:3001 0.0.0.0:* LISTEN 0 4096 127.0.0.1:27017 0.0.0.0:*To grow the database volume, expand the EBS volume in the AWS console. Then extend the filesystem with sudo resize2fs /dev/nvme1n1.
Component versions
echo "Node.js $(node -v)"
echo "MongoDB $(mongod --version | awk '/db version/{print $3}')"
echo "nginx $(nginx -v 2>&1 | sed 's|nginx version: nginx/||')"Node.js v20.20.2 MongoDB v8.0.28 nginx 1.30.4This image ships Countly Community Edition 25.03.49.
Restarting and maintaining the services
Manage the services with systemctl. To restart Countly after a configuration change, run:
sudo systemctl restart countly.serviceThe services are ordered so that a reboot brings up MongoDB first. Next comes the first-boot initialisation, if it has not run, then Countly, then nginx.
Enabling HTTPS
Countly is served over plain HTTP on port 80. Before you expose it to production traffic, terminate TLS with your own domain and certificate. Point a DNS record at the instance and open port 443 in your security group. Then use certbot with the nginx plugin to obtain and install a certificate:
sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d analytics.your-domain.comcertbot edits the nginx configuration to serve HTTPS and sets up automatic renewal.
Support
Every deployment is backed by SecureAnyCloud engineers, available around the clock by email at sales@secureanycloud.com and by live chat. Support covers deployment guidance and instance sizing, MongoDB administration and backup, nginx reverse proxy and TLS termination, Countly upgrades and migration assistance, and performance tuning.
Critical issues receive a one-hour average response time.
Deploy on AWS
Launch Countly Lite on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com