{"id":47598409,"url":"https://github.com/teamfighter/servercore-billing-exporter","last_synced_at":"2026-04-01T18:32:12.042Z","repository":{"id":345293445,"uuid":"1185206665","full_name":"teamfighter/servercore-billing-exporter","owner":"teamfighter","description":"Prometheus exporter for Servercore cloud billing data","archived":false,"fork":false,"pushed_at":"2026-03-27T15:17:08.000Z","size":2263,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-03-28T00:44:34.181Z","etag":null,"topics":["billing","exporter","golang","monitoring","prometheus","servercore"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/teamfighter.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-18T10:49:19.000Z","updated_at":"2026-03-27T15:11:57.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/teamfighter/servercore-billing-exporter","commit_stats":null,"previous_names":["teamfighter/servercore-billing-exporter"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/teamfighter/servercore-billing-exporter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/teamfighter%2Fservercore-billing-exporter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/teamfighter%2Fservercore-billing-exporter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/teamfighter%2Fservercore-billing-exporter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/teamfighter%2Fservercore-billing-exporter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/teamfighter","download_url":"https://codeload.github.com/teamfighter/servercore-billing-exporter/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/teamfighter%2Fservercore-billing-exporter/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31290879,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T13:12:26.723Z","status":"ssl_error","status_checked_at":"2026-04-01T13:12:25.102Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["billing","exporter","golang","monitoring","prometheus","servercore"],"created_at":"2026-04-01T18:32:11.361Z","updated_at":"2026-04-01T18:32:12.035Z","avatar_url":"https://github.com/teamfighter.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Servercore Billing Exporter\n\n[![Go](https://img.shields.io/badge/Go-1.25+-00ADD8?logo=go)](https://go.dev)\n[![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n\nPrometheus exporter for [Servercore](https://servercore.com) cloud billing data.\n\nCollects account balance, debt, consumption statistics, and billing predictions from the Servercore API and exposes them as Prometheus metrics. Optionally enriches VM-level metrics with OpenStack tags for team/owner attribution.\n\n## Features\n\n- **Account-level metrics** — balance, debt, and balance depletion prediction\n- **Consumption breakdown** — monthly costs by project × service\n- **Historical consumption** — past month billing costs properly cached (1 day TTL) for longitudinal tracking\n- **Per-resource detail** — cost and quantity per metric (vCPU, RAM, disk, traffic, etc.)\n- **Per-VM cost attribution** — individual VM costs with custom tag labels from OpenStack\n- **Per-disk/volume costs** — disk costs linked to their parent VM\n- **Tag enrichment** — automatic label injection from OpenStack VM tags\n- **Tag overrides** — prefix-based fallback tags for untagged VMs (via JSON config)\n- **Multi-project support** — single exporter instance can query multiple OpenStack projects\n- **Config generator script** — converts Servercore OpenStack RC files to INI config\n\n## Metrics\n\n### Account \u0026 Billing\n\n| Metric | Type | Labels | Description |\n|--------|------|--------|-------------|\n| `sc_balance_by_type` | Gauge | `type` | Balance by type (main, bonus) |\n| `sc_balance_total` | Gauge | — | Total account balance |\n| `sc_debt_total` | Gauge | — | Total account debt |\n| `sc_debt_by_service` | Gauge | `service` | Debt per service |\n| `sc_prediction_days` | Gauge | `billing_type` | Days until balance exhaustion per billing type |\n\n### Consumption (project-level)\n\n| Metric | Type | Labels | Description |\n|--------|------|--------|-------------|\n| `sc_consumption_cost` | Gauge | `project`, `service` | Monthly cost for the current metric month by project × service |\n| `sc_consumption_cost_monthly` | Gauge | `project`, `service`, `period` | Historical monthly cost by project × service (cached 24h, excludes current active period) |\n| `sc_resource_cost` | Gauge | `project`, `service`, `metric` | Cost per resource metric |\n| `sc_resource_quantity` | Gauge | `project`, `service`, `metric`, `unit` | Resource quantity (vCPU-hours, GB-hours, etc.) |\n\n### Per-VM / Per-Disk (object-level, requires `OPENSTACK_CONFIG`)\n\n| Metric | Type | Labels | Description |\n|--------|------|--------|-------------|\n| `sc_vm_cost` | Gauge | `project`, `service`, `metric`, `vm_name`, `\u003ctags\u003e...` | Per-VM resource cost with custom tag labels |\n| `sc_disk_cost` | Gauge | `project`, `service`, `metric`, `disk_name`, `parent_vm`, `\u003ctags\u003e...` | Per-disk/volume cost linked to parent VM |\n\n\u003e The `\u003ctags\u003e` labels are dynamically defined by the `EXPORTED_TAGS` environment variable (e.g., `env`, `owner`, `cost_center`).\n\n### Scrape Metadata\n\n| Metric | Type | Labels | Description |\n|--------|------|--------|-------------|\n| `sc_scrape_success` | Gauge | — | 1 if last scrape succeeded, 0 otherwise |\n| `sc_scrape_duration_seconds` | Gauge | — | Duration of last API scrape |\n\n## Configuration\n\n| Environment Variable | Required | Default | Description |\n|---------------------|----------|---------|-------------|\n| `TOKEN` | ✅ | — | Servercore API static token |\n| `LISTEN_ADDR` | ❌ | `:9876` | HTTP listen address |\n| `BILLING_HISTORY_MONTHS` | ❌ | `12` | Depth in months for fetching historical data (`sc_consumption_cost_monthly`). Set to `0` to disable history scraping |\n| `OPENSTACK_CONFIG` | ❌ | — | Path to INI file with OpenStack credentials (enables tag enrichment and per-VM metrics) |\n| `EXPORTED_TAGS` | ❌ | — | Comma-separated list of OpenStack tag keys to export as Prometheus labels |\n| `TAG_OVERRIDES_FILE` | ❌ | — | Path to JSON file mapping VM name prefixes to tag values (fallback for untagged VMs) |\n\n### Getting a token\n\n1. Go to [Servercore Control Panel](https://my.servercore.com/profile/apikeys)\n2. Create a new API key\n3. Copy the static token\n\n### OpenStack tag enrichment\n\nWhen `OPENSTACK_CONFIG` is set, the exporter authenticates against one or more OpenStack projects to read VM tags. Tags in the format `key=value` (e.g., `env=production`, `owner=user@example.com`) are extracted and injected as Prometheus labels into `sc_vm_cost` and `sc_disk_cost` metrics.\n\n#### Step 1: Generate the config file\n\nDownload the OpenStack RC files from the Servercore panel for each project you want to enrich. Then use the provided script:\n\n```sh\n# Generate config.ini from OpenStack RC files (one password per batch):\nOS_PASSWORD='your_openstack_password' ./scripts/generate-config.sh rc-project1.sh rc-project2.sh\n\n# If you have projects with a different password, append with --append:\nOS_PASSWORD='another_password' ./scripts/generate-config.sh --append rc-project3.sh\n```\n\nThis produces a `config.ini` file:\n\n```ini\n[project1]\nauth_url = https://cloud.servercore.com:5000/v3\nproject_id = abc123def456\ndomain_name = users\nregion_name = ru-1\nusername = user@example.com\npassword = your_openstack_password\n\n[project2]\nauth_url = https://cloud.servercore.com:5000/v3\nproject_id = 789xyz012abc\ndomain_name = users\nregion_name = ru-1\nusername = user@example.com\npassword = your_openstack_password\n```\n\n\u003e ⚠️ Keep `config.ini` secure — it contains credentials. Do not commit it to version control.\n\n#### Step 2: Configure tag export\n\nSet `EXPORTED_TAGS` to the OpenStack tag keys you want as Prometheus labels:\n\n```sh\nEXPORTED_TAGS=env,owner,cost_center\n```\n\nThis will add `env`, `owner`, and `cost_center` labels to `sc_vm_cost` and `sc_disk_cost` metrics. VMs without these tags will have the label value `Untagged`.\n\n#### Step 3 (optional): Tag overrides for untagged VMs\n\nIf some VMs don't have OpenStack tags but follow a naming convention, you can assign tags based on VM name prefixes. Create a JSON file:\n\n```json\n{\n  \"k8s-prod-node\":   {\"env\": \"production\", \"cost_center\": \"infrastructure\"},\n  \"k8s-dev-node\":    {\"env\": \"development\"},\n  \"dwh-greenplum\":   {\"owner\": \"analytics@example.com\"},\n  \"monitoring-node\": {\"cost_center\": \"operations\"}\n}\n```\n\nThe exporter matches VM names using longest prefix first. Overrides are only applied to tags that are still `Untagged` after OpenStack lookup — explicit OpenStack tags always take precedence.\n\nSet `TAG_OVERRIDES_FILE` to the path of this JSON file.\n\n## Running\n\n### Docker Compose (basic)\n\n```yaml\nservices:\n  servercore-billing-exporter:\n    image: ghcr.io/teamfighter/servercore-billing-exporter:latest\n    ports:\n      - \"9876:9876\"\n    restart: unless-stopped\n    environment:\n      TOKEN: your_servercore_api_token\n```\n\n```sh\ndocker compose up -d\ncurl http://localhost:9876/metrics\n```\n\n### Docker Compose (with tag enrichment)\n\n```yaml\nservices:\n  servercore-billing-exporter:\n    image: ghcr.io/teamfighter/servercore-billing-exporter:latest\n    ports:\n      - \"9876:9876\"\n    restart: unless-stopped\n    environment:\n      TOKEN: your_servercore_api_token\n      OPENSTACK_CONFIG: /etc/exporter/config.ini\n      EXPORTED_TAGS: env,owner,cost_center\n      TAG_OVERRIDES_FILE: /etc/exporter/overrides.json\n    volumes:\n      - ./config.ini:/etc/exporter/config.ini:ro\n      - ./overrides.json:/etc/exporter/overrides.json:ro\n```\n\n### Docker\n\n```sh\ndocker run -d \\\n  -p 9876:9876 \\\n  -e TOKEN=your_token \\\n  ghcr.io/teamfighter/servercore-billing-exporter:latest\n```\n\nWith tag enrichment:\n\n```sh\ndocker run -d \\\n  -p 9876:9876 \\\n  -e TOKEN=your_token \\\n  -e OPENSTACK_CONFIG=/etc/exporter/config.ini \\\n  -e EXPORTED_TAGS=env,owner,cost_center \\\n  -v $(pwd)/config.ini:/etc/exporter/config.ini:ro \\\n  ghcr.io/teamfighter/servercore-billing-exporter:latest\n```\n\n### Binary\n\n```sh\nexport TOKEN=your_token\n./servercore-billing-exporter\n\n# With tag enrichment:\nexport OPENSTACK_CONFIG=./config.ini\nexport EXPORTED_TAGS=env,owner,cost_center\n./servercore-billing-exporter\n```\n\n### Kubernetes\n\n```yaml\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: servercore-billing\n  namespace: monitoring\nspec:\n  selector:\n    matchLabels:\n      app: servercore-billing\n  template:\n    metadata:\n      labels:\n        app: servercore-billing\n    spec:\n      containers:\n        - name: exporter\n          image: ghcr.io/teamfighter/servercore-billing-exporter:latest\n          ports:\n            - containerPort: 9876\n          env:\n            - name: TOKEN\n              valueFrom:\n                secretKeyRef:\n                  name: servercore-billing\n                  key: token\n            - name: OPENSTACK_CONFIG\n              value: /etc/exporter/config.ini\n            - name: EXPORTED_TAGS\n              value: env,owner,cost_center\n          volumeMounts:\n            - name: openstack-config\n              mountPath: /etc/exporter\n              readOnly: true\n      volumes:\n        - name: openstack-config\n          secret:\n            secretName: servercore-billing-openstack\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: servercore-billing\n  namespace: monitoring\nspec:\n  ports:\n    - name: metrics\n      port: 9876\n      targetPort: 9876\n  selector:\n    app: servercore-billing\n```\n\n## Prometheus Configuration\n\n```yaml\nscrape_configs:\n  - job_name: servercore_billing\n    scrape_interval: 60m\n    scrape_timeout: 2m\n    static_configs:\n      - targets: [\"servercore-billing:9876\"]\n```\n\n\u003e **Note:** Scrape timeout should be generous (1–2 min) because the Servercore billing API can be slow, especially for accounts with many resources. The default `scrape_interval: 60m` is recommended since billing data is hourly.\n\n## Alert Examples\n\n### Low balance alert\n\n```yaml\ngroups:\n  - name: servercore_billing\n    rules:\n      - alert: ServercoreLowBalance\n        expr: sc_prediction_days \u003c 30\n        for: 1h\n        labels:\n          severity: warning\n        annotations:\n          summary: \"Servercore balance will run out in {{ $value }} days\"\n          description: \"Account balance is predicted to be exhausted within 30 days.\"\n\n      - alert: ServercoreDebt\n        expr: sc_debt_total \u003e 0\n        for: 5m\n        labels:\n          severity: critical\n        annotations:\n          summary: \"Servercore account has debt: {{ $value }}\"\n```\n\n### Top VM cost alert\n\n```yaml\n      - alert: ServercoreHighVMCost\n        expr: sum by (vm_name) (sc_vm_cost) \u003e 10000000\n        for: 1h\n        labels:\n          severity: info\n        annotations:\n          summary: \"VM {{ $labels.vm_name }} monthly cost exceeds 10M\"\n```\n\n## Scripts\n\n### `scripts/generate-config.sh`\n\nConverts Servercore OpenStack RC files into a `config.ini` understood by the exporter.\n\n**Usage:**\n\n```sh\n# Convert one or more RC files (prompted for password if OS_PASSWORD is not set):\nOS_PASSWORD=secret ./scripts/generate-config.sh rc-file1.sh rc-file2.sh\n\n# Append to an existing config.ini (e.g., for projects with a different password):\nOS_PASSWORD=other ./scripts/generate-config.sh --append rc-file3.sh\n```\n\n**How it works:**\n\n1. Parses `OS_AUTH_URL`, `OS_PROJECT_ID`, `OS_PROJECT_DOMAIN_NAME`, `OS_REGION_NAME`, `OS_USERNAME` from each RC file\n2. Uses the section name derived from the filename (strips `rc-`/`openrc-` prefix and `.sh` extension)\n3. Combines them with the provided `OS_PASSWORD` into INI sections\n4. Writes to `config.ini` in the current directory\n\n**Example RC file** (`rc-production.sh`):\n\n```sh\nexport OS_AUTH_URL=\"https://cloud.servercore.com:5000/v3\"\nexport OS_PROJECT_ID=\"abc123\"\nexport OS_PROJECT_DOMAIN_NAME=\"users\"\nexport OS_REGION_NAME=\"ru-1\"\nexport OS_USERNAME=\"admin@example.com\"\n```\n\n**Generated output** (`config.ini`):\n\n```ini\n[production]\nauth_url = https://cloud.servercore.com:5000/v3\nproject_id = abc123\ndomain_name = users\nregion_name = ru-1\nusername = admin@example.com\npassword = secret\n```\n\n## Development\n\n```sh\n# Run tests\nmake test\n\n# Build binary\nmake build\n\n# Run linter\nmake vet\n\n# Build Docker image\nmake docker-build\n```\n\n## API Endpoints Used\n\n| Endpoint | Description |\n|----------|-------------|\n| `GET /v3/balances` | Account balance and debt |\n| `GET /v2/billing/prediction` | Days until balance exhaustion |\n| `GET /v1/cloud_billing/statistic/consumption` | Monthly consumption data (project, project_metric, object_metric aggregations) |\n\nAuthentication is done via `X-Token` header with a static API token.\n\n## Acknowledgments\n\n- [selectel-billing-exporter](https://github.com/mxssl/selectel-billing-exporter) by [@mxssl](https://github.com/mxssl) — billing API client and exporter structure, adapted for the Servercore cloud platform.\n- [openstack-resource-exporter](https://github.com/avorr/openstack-resource-exporter) by [@avorr](https://github.com/avorr) (Alexander Vorobyev) — OpenStack integration patterns and tag extraction approach.\n\n## License\n\nMIT — see [LICENSE](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fteamfighter%2Fservercore-billing-exporter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fteamfighter%2Fservercore-billing-exporter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fteamfighter%2Fservercore-billing-exporter/lists"}