| |

LFCE 127 ๐Ÿง Prometheus โ€” Scrape Configs

โฑ๏ธ Estimated read: 13 min | |

The scrape_configs section of prometheus.yml is where Prometheus learns what to monitor. Each entry defines a job: a named group of targets that share the same scrape behavior. A job can list targets statically, discover them from a file, or query a service discovery mechanism. It can override the global scrape interval, change the metrics path, attach labels, and apply relabeling rules before a sample is ever ingested.

This chapter covers the structure of a scrape job, the four target discovery mechanisms that matter in production (static_configs, file_sd_configs, dns_sd_configs, and kubernetes_sd_configs), the per-job overrides that change how a target is scraped, and the relabel_configs rules that filter and transform targets before scraping. By the end, you will be able to write a scrape configuration that discovers targets dynamically, attaches meaningful labels, and avoids scraping things it should not.

Key point: relabel_configs run before the scrape. They can drop a target entirely, rewrite its address, or add labels that appear on every metric from that target. metric_relabel_configs run after the scrape and can only drop or rewrite individual samples.


Why scrape configs have this structure

The target discovery problem. A static list of targets works for a handful of servers. It does not work for a Kubernetes cluster where pods are created and destroyed continuously, or a cloud environment where instances are ephemeral. The scrape config supports multiple discovery mechanisms because no single mechanism fits every environment. static_configs is the simplest, file_sd_configs delegates discovery to an external script, and kubernetes_sd_configs queries the Kubernetes API directly .

The label attachment problem. A metric’s identity is its name plus its labels. The scrape config controls two sets of labels: the labels attached to the target (labels in static_configs, or metadata labels from service discovery), and the labels attached to every sample from that target (the job label, which is the job_name, and any instance label derived from __address__). The relabel_configs section can add, modify, or remove these labels before the scrape .

The filtering problem. Service discovery returns everything it knows about. A Kubernetes discovery role returns every pod, including the ones that do not expose metrics. A file-based discovery might return targets that are temporarily down. The relabel_configs section is where filtering happens: a keep action drops targets that do not match a regex, and a drop action removes targets that do. Without relabeling, Prometheus scrapes every target the discovery mechanism returns, including the ones that should be ignored .


a. The basic job structure

A scrape job is a YAML mapping with job_name as the only required field. Everything else has a default. The job_name becomes the value of the job label on every metric scraped by that job.

scrape_configs:
  - job_name: "prometheus"
    static_configs:
      - targets: ["localhost:9090"]

This is the minimal job. It scrapes http://localhost:9090/metrics every 15 seconds (the global default), and every metric it collects carries job="prometheus" and instance="localhost:9090" .

The static_configs block accepts a list of target groups. Each group has a targets list and an optional labels mapping. The labels in a static config group are attached to every metric from every target in that group.

scrape_configs:
  - job_name: "node"
    static_configs:
      - targets: ["10.0.1.10:9100", "10.0.1.11:9100"]
        labels:
          env: "production"
          role: "web"

The two targets share the env and role labels, but each gets its own instance label derived from its address.


b. Per-job overrides

Every field in a scrape job can override the global setting. The most commonly overridden fields are scrape_interval, scrape_timeout, metrics_path, and scheme.

scrape_configs:
  - job_name: "slow-service"
    scrape_interval: 60s
    scrape_timeout: 30s
    metrics_path: "/custom/metrics"
    scheme: "https"
    static_configs:
      - targets: ["10.0.2.20:8443"]

A job that scrapes a slow endpoint can use a longer interval and timeout without affecting the other jobs. A job that exposes metrics on a non-standard path can set metrics_path. A job that requires HTTPS can set scheme and provide tls_config for certificate validation .

The params field passes query parameters to the scrape request. It is a mapping of parameter names to lists of values.

  - job_name: "api-metrics"
    params:
      module: ["http_2xx"]
    static_configs:
      - targets: ["10.0.3.30:9115"]

The params field is useful for exporters that require a module parameter, such as the blackbox exporter.


c. File-based service discovery

file_sd_configs reads target lists from JSON or YAML files and re-reads them at a configured interval. It is the bridge between Prometheus and any external system that can write a file: configuration management tools, custom scripts, or a controller that watches a cloud API .

scrape_configs:
  - job_name: "dynamic-targets"
    file_sd_configs:
      - files:
          - "/etc/prometheus/targets/*.json"
          - "/etc/prometheus/targets/*.yml"
        refresh_interval: "5m"

The JSON file format is a list of target groups, each with targets and labels:

[
  {
    "targets": ["10.0.4.40:9100", "10.0.4.41:9100"],
    "labels": {"env": "staging", "team": "platform"}
  }
]

The YAML format is equivalent. Prometheus watches the files and reloads them when they change, without a restart. The refresh_interval controls how often it checks for changes. File-based discovery is the recommended approach for targets managed by Ansible, Puppet, or a custom inventory script, because the script writes the file and Prometheus picks up the change .


d. Relabeling: filtering and transforming targets

relabel_configs is the most powerful and most misunderstood part of the scrape config. It processes every target that a discovery mechanism returns, before the target is scraped. Each rule has a source_labels list, a separator, a regex, and an action. The action determines what happens when the concatenated source labels match the regex.

The most common actions are keep, drop, replace, and labelmap.

scrape_configs:
  - job_name: "kubernetes-pods"
    kubernetes_sd_configs:
      - role: pod
    relabel_configs:
      # Only scrape pods with prometheus.io/scrape=true
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
        action: keep
        regex: "true"

      # Use the port from the prometheus.io/port annotation
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_port]
        action: replace
        target_label: __address__
        regex: "(.+):(\\d+)"
        replacement: "$1:$2"

      # Add the pod name as a label
      - source_labels: [__meta_kubernetes_pod_name]
        target_label: pod

The __meta_* labels are internal labels provided by the discovery mechanism. They are available during relabeling but are removed before the sample is ingested. The __address__ label is the target’s host:port, and rewriting it changes where Prometheus sends the scrape. The __metrics_path__ label can be rewritten to change the path per target .

The keep action drops every target that does not match the regex. The drop action drops every target that does match. These two actions are how a job filters the output of a discovery mechanism that returns more targets than it should scrape.


Complete Example Session

# ============================================
# PART 1: THE MINIMAL JOB
# ============================================
scrape_configs:
  - job_name: "prometheus"
    static_configs:
      - targets: ["localhost:9090"]

# Scrapes http://localhost:9090/metrics every 15s.
# Adds job="prometheus" and instance="localhost:9090".
# ============================================
# PART 2: STATIC TARGETS WITH LABELS
# ============================================
scrape_configs:
  - job_name: "node"
    static_configs:
      - targets: ["10.0.1.10:9100", "10.0.1.11:9100"]
        labels:
          env: "production"
          role: "web"

# Both targets carry env="production" and role="web".
# ============================================
# PART 3: PER-JOB OVERRIDES
# ============================================
  - job_name: "slow-api"
    scrape_interval: 60s
    scrape_timeout: 30s
    metrics_path: "/api/metrics"
    scheme: "https"
    static_configs:
      - targets: ["10.0.2.20:8443"]

# This job scrapes a different path, over HTTPS,
# with a longer interval than the global default.
# ============================================
# PART 4: FILE-BASED DISCOVERY
# ============================================
  - job_name: "dynamic"
    file_sd_configs:
      - files:
          - "/etc/prometheus/targets/*.json"
        refresh_interval: "5m"

# Prometheus re-reads the JSON files every 5 minutes.
# A script can write new targets without a reload.
# ============================================
# PART 5: THE JSON TARGET FILE
# ============================================
[
  {
    "targets": ["10.0.4.40:9100"],
    "labels": {"env": "staging"}
  }
]

# Placed in /etc/prometheus/targets/staging.json.
# ============================================
# PART 6: KUBERNETES POD DISCOVERY
# ============================================
  - job_name: "kubernetes-pods"
    kubernetes_sd_configs:
      - role: pod

# Discovers every pod in the cluster.
# Returns far more targets than should be scraped.
# ============================================
# PART 7: FILTER WITH KEEP
# ============================================
    relabel_configs:
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
        action: keep
        regex: "true"

# Only pods with the annotation prometheus.io/scrape=true.
# Everything else is dropped before scraping.
# ============================================
# PART 8: REWRITE THE ADDRESS
# ============================================
      - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
        action: replace
        regex: "([^:]+)(?::\\d+)?;(\\d+)"
        replacement: "$1:$2"
        target_label: __address__

# Replaces the pod IP with IP:annotation_port.
# The port comes from the prometheus.io/port annotation.
# ============================================
# PART 9: ADD A LABEL
# ============================================
      - source_labels: [__meta_kubernetes_pod_name]
        target_label: pod

# Every metric from this target gets pod="<name>".
# ============================================
# PART 10: VALIDATE AND RELOAD
# ============================================
promtool check config /etc/prometheus/prometheus.yml
sudo systemctl reload prometheus

# promtool catches syntax errors before reload.
# A successful reload applies the new scrape configs.

The ten parts covered the minimal job, static labels, per-job overrides, file-based discovery, the JSON target file, Kubernetes discovery, keep filtering, address rewriting, label addition, and validation.


Quick Reference

Job fields

FieldDefaultPurpose
job_nameRequiredJob identifier, becomes job label
scrape_intervalGlobalPer-job scrape frequency
scrape_timeoutGlobalPer-job timeout
metrics_path/metricsHTTP path to scrape
schemehttpProtocol scheme
paramsโ€”Query parameters
static_configsโ€”Static target list
file_sd_configsโ€”Targets from files
kubernetes_sd_configsโ€”Targets from Kubernetes API
relabel_configsโ€”Pre-scrape target transformation
metric_relabel_configsโ€”Post-scrape sample transformation

Discovery mechanisms

MechanismUse caseDynamic
static_configsFixed targetsNo
file_sd_configsExternal inventoryYes (file watch)
dns_sd_configsDNS SRV recordsYes
kubernetes_sd_configsKubernetes resourcesYes

Relabel actions

ActionEffect
keepKeep targets matching regex
dropDrop targets matching regex
replaceReplace target label with regex capture
labelmapMap discovered labels to new label names
labeldropDrop matching labels
labelkeepKeep matching labels

Common relabel patterns

PatternPurpose
keep on __meta_*_annotation_prometheus_io_scrapeOnly scrape annotated pods
replace on __address__Change scrape port
replace on __metrics_path__Change metrics path per target
replace on __meta_*_name โ†’ pod/nodeAdd human-readable label

Best Practices

โœ… Do This:

# Use file_sd_configs for targets managed by external tools
file_sd_configs:
  - files: ["/etc/prometheus/targets/*.json"]              # โœ…
# Filter Kubernetes discovery with keep rules
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
  action: keep
  regex: "true"                                            # โœ…
# Override scrape_interval per job when needed
scrape_interval: 60s                                       # โœ…
# Validate with promtool before reloading
promtool check config /etc/prometheus/prometheus.yml       # โœ…

โŒ Don’t Do This:

# Scrape every Kubernetes pod without filtering
kubernetes_sd_configs:
  - role: pod
# no relabel_configs                                      # โŒ
# Use static_configs for a dynamic environment
static_configs:
  - targets: ["10.0.1.10:9100"]  # pods change constantly # โŒ
# Rewrite __address__ without preserving the port
target_label: __address__
replacement: "$1"  # drops the port                       # โŒ
# Set scrape_timeout greater than scrape_interval
scrape_interval: 15s
scrape_timeout: 30s                                        # โŒ (timeout > interval)

Common Pitfalls

PitfallWhy It HappensFix
Target shows up=0Wrong metrics_path, scheme, or portCheck the target’s actual endpoint
Kubernetes job scrapes too many targetsNo keep relabel ruleAdd keep on the scrape annotation
File discovery not updatingrefresh_interval not set or file path wrongCheck the glob and the interval
relabel_configs has no effectSource labels use wrong __meta_* nameVerify the discovery mechanism’s label names
Scrape timeout errorsscrape_timeout too short for slow endpointIncrease per-job timeout
Port missing after address rewriteReplacement drops the portInclude $2 in the replacement

Real-World Examples

1. Minimal Prometheus self-scrape

- job_name: "prometheus"
  static_configs:
    - targets: ["localhost:9090"]

2. Node exporter static targets

- job_name: "node"
  static_configs:
    - targets: ["10.0.1.10:9100", "10.0.1.11:9100"]

3. Per-job interval override

- job_name: "slow"
  scrape_interval: 60s
  static_configs:
    - targets: ["10.0.2.20:9100"]

4. File-based discovery

- job_name: "dynamic"
  file_sd_configs:
    - files: ["/etc/prometheus/targets/*.json"]

5. JSON target file

[{"targets": ["10.0.4.40:9100"], "labels": {"env": "prod"}}]

6. Kubernetes pod discovery

- job_name: "pods"
  kubernetes_sd_configs:
    - role: pod

7. Keep annotated pods

- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
  action: keep
  regex: "true"

8. Rewrite address with annotation port

- source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
  action: replace
  regex: "([^:]+)(?::\\d+)?;(\\d+)"
  replacement: "$1:$2"
  target_label: __address__

9. Add pod name label

- source_labels: [__meta_kubernetes_pod_name]
  target_label: pod

10. Validate config

promtool check config /etc/prometheus/prometheus.yml

Visual

The scrape config lifecycle

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  SCRAPE CONFIG LIFECYCLE                                     โ”‚
โ”‚                                                              โ”‚
โ”‚  Discovery mechanism                                         โ”‚
โ”‚    (static, file, k8s, dns)                                  โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  Raw targets with __meta_* labels                            โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  relabel_configs                                             โ”‚
โ”‚    โ”œโ”€โ”€ keep/drop: filter targets                             โ”‚
โ”‚    โ”œโ”€โ”€ replace: rewrite __address__, labels                  โ”‚
โ”‚    โ””โ”€โ”€ labelmap: map discovered labels                       โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  Final targets                                               โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  HTTP GET /metrics                                           โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  metric_relabel_configs                                      โ”‚
โ”‚    โ”œโ”€โ”€ drop: remove samples                                  โ”‚
โ”‚    โ””โ”€โ”€ replace: rewrite sample labels                        โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  TSDB ingestion                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Static vs file discovery

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  STATIC_CONFIGS              FILE_SD_CONFIGS                 โ”‚
โ”‚                                                              โ”‚
โ”‚  Targets in prometheus.yml   Targets in external files       โ”‚
โ”‚  Reload required             Auto-refreshed                  โ”‚
โ”‚  Good for fixed hosts        Good for dynamic inventory      โ”‚
โ”‚  No external dependency      Requires file writer            โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Kubernetes relabel flow

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  KUBERNETES RELABELING                                       โ”‚
โ”‚                                                              โ”‚
โ”‚  kubernetes_sd_configs: role: pod                            โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  Every pod with __meta_kubernetes_pod_* labels              โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  keep: __meta_kubernetes_pod_annotation_prometheus_io_scrape โ”‚
โ”‚    = "true"                                                  โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  replace: __address__ = pod_ip:annotation_port               โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  replace: pod = __meta_kubernetes_pod_name                   โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  Scrape the pod                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Per-job override decision

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  WHEN TO OVERRIDE                                            โ”‚
โ”‚                                                              โ”‚
โ”‚  Slow endpoint?                                              โ”‚
โ”‚    โ””โ”€โ”€ scrape_interval: 60s, scrape_timeout: 30s             โ”‚
โ”‚                                                              โ”‚
โ”‚  Non-standard path?                                          โ”‚
โ”‚    โ””โ”€โ”€ metrics_path: /custom/metrics                         โ”‚
โ”‚                                                              โ”‚
โ”‚  HTTPS with client cert?                                     โ”‚
โ”‚    โ””โ”€โ”€ scheme: https, tls_config: {...}                      โ”‚
โ”‚                                                              โ”‚
โ”‚  Requires query parameter?                                   โ”‚
โ”‚    โ””โ”€โ”€ params: {module: ["http_2xx"]}                        โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

ItemValue
Required job fieldjob_name
Default metrics path/metrics
Default schemehttp
Static targetsstatic_configs
File-based discoveryfile_sd_configs with files glob
Kubernetes discoverykubernetes_sd_configs with role
Pre-scrape filteringrelabel_configs
Post-scrape filteringmetric_relabel_configs
Target address label__address__
Metrics path label__metrics_path__

Key takeaways:

  • Every scrape job needs a job_name, and that name becomes the job label on every metric it collects. The instance label is derived from __address__. Together, these two labels identify the source of every sample .
  • static_configs is for fixed targets; file_sd_configs is for everything else. A file-based discovery lets an external script or configuration management tool write the target list, and Prometheus picks up changes without a restart .
  • relabel_configs runs before the scrape. It can drop targets (keep, drop), rewrite the address (__address__), change the metrics path (__metrics_path__), and add labels. The __meta_* labels from service discovery are available only during this phase.
  • Kubernetes discovery returns every resource for the chosen role. Without a keep rule that filters on an annotation or label, Prometheus scrapes pods that do not expose metrics, producing up=0 noise and wasted scrape cycles .
  • Per-job overrides isolate slow or special endpoints. A job that scrapes a slow API can use a longer interval and timeout without changing the behavior of the jobs that scrape fast exporters .
  • Validate with promtool before reloading. A YAML syntax error or an invalid relabel rule prevents Prometheus from starting. promtool check config catches these before the service is affected.

Remember: A scrape config is a contract between Prometheus and a set of targets. The discovery mechanism determines which targets exist. The relabel rules determine which ones are scraped and what labels they carry. The per-job overrides determine how the scrape is performed. Get the discovery right first: static_configs for a handful of fixed hosts, file_sd_configs for anything managed by an external system, kubernetes_sd_configs for pods and services. Then add a keep rule to filter out the targets that should not be scraped. Without it, the job scrapes everything the discovery returns, and the up metric becomes noise instead of signal.

Was this helpful?


Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!