A lightweight, privacy-respecting analytics collector for Pelagica, a custom frontend for Jellyfin. It counts how many instances are installed and how many are actively running, without collecting any personally identifiable information.
Each Pelagica instance generates a random UUID on first run and sends a minimal ping to the collector once every 24 hours. The collector records the instance ID, version, and timestamp. No IP addresses are stored. No user data is collected.
The /stats endpoint is public so anyone can verify what the numbers look like.
| Field | Description |
|---|---|
instance_id |
A random UUID generated on first install |
version |
The Pelagica version string |
pinged_at |
Timestamp of the ping |
Nothing else is collected or stored.
Sent automatically by Pelagica once per 24 hours.
Request body:
{
"instance_id": "a4b1c2d3-e5f6-7890-abcd-ef1234567890",
"version": "1.2.0",
"token": "optional-token-if-needed"
}Responses:
204 No Contenton success400 Bad Requestif the payload is invalid401 Unauthorizedif a token is required and missing/invalid429 Too Many Requestsif the rate limit is exceeded
Returns the current install and activity counts.
Response:
{
"total_installs": 412,
"active_instances": 198
}Active instances are those that have sent a ping in the last 48 hours.
The recommended way to run the collector is via Docker Compose.
services:
postgres:
image: postgres:16
environment:
POSTGRES_USER: pelagica
POSTGRES_PASSWORD: yourpassword
POSTGRES_DB: pelagica
volumes:
- postgres_db_data:/var/lib/postgresql/data
expose:
- "5432"
collector:
image: kartoffelchipss/pelagica-collector:latest
environment:
DATABASE_URL: postgres://pelagica:yourpassword@postgres:5432/pelagica
BEHIND_PROXY: "true"
ports:
- "4000:4000"
volumes:
postgres_db_data:All configuration is done via environment variables.
| Variable | Required | Description |
|---|---|---|
DATABASE_URL |
Yes | Postgres connection string, e.g. postgres://user:pass@host:5432/db |
PORT |
No | Port to listen on. Default: 4000 |
PING_TOKEN |
No | If set, requires this token in the token field of the ping payload for authentication. Default: no token required |
BEHIND_PROXY |
No | Set to true if running behind a reverse proxy. Enables X-Forwarded-For trust. Default: false |
TRUSTED_PROXIES |
No | Comma-separated list of trusted proxy IPs or CIDR ranges if BEHIND_PROXY=true. Default: empty (trust all proxies) |
PROXY_HEADER |
No | Header to read client IP from when BEHIND_PROXY=true. Default: X-Forwarded-For |
If you run the collector behind Nginx or Caddy, set BEHIND_PROXY=true. This tells the collector to read the real client IP from the X-Forwarded-For header for rate limiting purposes.
For Nginx, make sure you forward the real IP:
proxy_set_header X-Forwarded-For $remote_addr;Requires Go 1.25 or later.
git clone https://github.com/PelagicaApp/pelagica-collector
cd pelagica-collector
go build -o collector .The collector manages its own schema and runs migrations automatically on startup. No manual setup is required beyond creating the database.
Ping history older than 90 days should be pruned periodically. You can set up a cron job or Postgres scheduled task with:
DELETE FROM pings WHERE pinged_at < now() - interval '90 days';MIT. See LICENSE.